From 5d2a6f371aa5bbd3c90c61c0792f2c9ba85d6f00 Mon Sep 17 00:00:00 2001 From: Tobi Adeyemi Date: Tue, 12 May 2026 15:47:09 +0100 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20BTC=20=E2=86=92=20stablecoin=20swap?= =?UTF-8?q?=20via=20LendaSwap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Lightning → USDC/USDT-on-Ethereum swap flow so users can off-ramp to stablecoins without leaving avark. --- .env.sample | 5 + .github/workflows/release.yml | 6 + package.json | 9 +- pnpm-lock.yaml | 1431 +++++++++++++++++++++++- pnpm-workspace.yaml | 5 + src-tauri/Cargo.lock | 141 ++- src-tauri/Cargo.toml | 9 + src-tauri/capabilities/default.json | 23 +- src-tauri/src/commands/lendaswap.rs | 320 ++++++ src-tauri/src/commands/lightning.rs | 282 +++++ src-tauri/src/commands/mod.rs | 1 + src-tauri/src/commands/send.rs | 83 +- src-tauri/src/commands/wallet.rs | 9 +- src-tauri/src/lendaswap.rs | 244 ++++ src-tauri/src/lib.rs | 46 +- src-tauri/src/wallet.rs | 1 - src/App.css | 129 +++ src/App.tsx | 5 +- src/SendSheet.tsx | 24 +- src/components/BottomNav.tsx | 9 + src/components/WalletConnectButton.tsx | 277 +++++ src/components/swap/SwapIcons.tsx | 66 ++ src/context/WalletConnectContext.tsx | 208 ++++ src/lib/lendaswap/client.ts | 568 ++++++++++ src/lib/lendaswap/reconcile.ts | 76 ++ src/lib/tauriFetch.ts | 48 + src/main.tsx | 2 + src/router.tsx | 39 +- src/routes/recover-ln-route.tsx | 345 ++++++ src/routes/settings-route.tsx | 30 +- src/routes/swap-checkout-route.tsx | 1088 ++++++++++++++++++ src/routes/swap-history-route.tsx | 295 +++++ src/routes/swap-route.tsx | 648 +++++++++++ src/utils/lendaswapErrors.ts | 65 ++ 34 files changed, 6473 insertions(+), 64 deletions(-) create mode 100644 pnpm-workspace.yaml create mode 100644 src-tauri/src/commands/lendaswap.rs create mode 100644 src-tauri/src/lendaswap.rs create mode 100644 src/components/WalletConnectButton.tsx create mode 100644 src/components/swap/SwapIcons.tsx create mode 100644 src/context/WalletConnectContext.tsx create mode 100644 src/lib/lendaswap/client.ts create mode 100644 src/lib/lendaswap/reconcile.ts create mode 100644 src/lib/tauriFetch.ts create mode 100644 src/routes/recover-ln-route.tsx create mode 100644 src/routes/swap-checkout-route.tsx create mode 100644 src/routes/swap-history-route.tsx create mode 100644 src/routes/swap-route.tsx create mode 100644 src/utils/lendaswapErrors.ts diff --git a/.env.sample b/.env.sample index f5742f9..0e5baed 100644 --- a/.env.sample +++ b/.env.sample @@ -1 +1,6 @@ VITE_DEFAULT_ASP_URL=https://arkade.computer/ + +# WalletConnect v2 projectId — required for BTC→stablecoin swaps. +# Get one at https://cloud.reown.com (formerly cloud.walletconnect.com). +# Missing/empty → the Swap screen shows "WalletConnect not configured". +VITE_WALLETCONNECT_PROJECT_ID= diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b394b9..44a91d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,6 +158,9 @@ jobs: mv avark-release.jks app/avark-release.jks - name: Build signed arm64-v8a APK + env: + VITE_WALLETCONNECT_PROJECT_ID: ${{ vars.VITE_WALLETCONNECT_PROJECT_ID }} + VITE_DEFAULT_ASP_URL: ${{ vars.VITE_DEFAULT_ASP_URL }} run: | pnpm tauri android build --apk --target aarch64 -- --features vendored-openssl V='${{ needs.resolve.outputs.version }}' @@ -168,6 +171,9 @@ jobs: mv "$APK" "artifacts/avark-$V-android-arm64-v8a.apk" - name: Build signed armeabi-v7a APK + env: + VITE_WALLETCONNECT_PROJECT_ID: ${{ vars.VITE_WALLETCONNECT_PROJECT_ID }} + VITE_DEFAULT_ASP_URL: ${{ vars.VITE_DEFAULT_ASP_URL }} run: | pnpm tauri android build --apk --target armv7 -- --features vendored-openssl V='${{ needs.resolve.outputs.version }}' diff --git a/package.json b/package.json index 65edb2e..53d1196 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,12 @@ "prepare": "git config --local core.hooksPath githooks 2>/dev/null || true" }, "dependencies": { + "@lendasat/lendaswap-sdk-pure": "^0.2.24", "@tanstack/react-router": "^1.166.3", "@tauri-apps/api": "^2", + "@tauri-apps/plugin-http": "^2", "@tauri-apps/plugin-opener": "^2", + "@walletconnect/sign-client": "^2.23.9", "qr-scanner": "^1.4.2", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -35,6 +38,7 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", + "@walletconnect/types": "^2.23.9", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", @@ -44,5 +48,6 @@ "typescript-eslint": "^8.57.0", "vite": "^7.0.4", "vitest": "^3.0.0" - } -} + }, + "packageManager": "pnpm@11.1.1" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff91cb4..bf4c688 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,15 +8,24 @@ importers: .: dependencies: + '@lendasat/lendaswap-sdk-pure': + specifier: ^0.2.24 + version: 0.2.24 '@tanstack/react-router': specifier: ^1.166.3 version: 1.166.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tauri-apps/api': specifier: ^2 version: 2.10.1 + '@tauri-apps/plugin-http': + specifier: ^2 + version: 2.5.8 '@tauri-apps/plugin-opener': specifier: ^2 version: 2.5.3 + '@walletconnect/sign-client': + specifier: ^2.23.9 + version: 2.23.9(typescript@5.8.3)(zod@4.3.6) qr-scanner: specifier: ^1.4.2 version: 1.4.2 @@ -41,7 +50,7 @@ importers: version: 9.39.4 '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.2.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)) + version: 4.2.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)) '@tauri-apps/cli': specifier: ^2 version: 2.10.1 @@ -53,7 +62,10 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.6.0 - version: 4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)) + version: 4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)) + '@walletconnect/types': + specifier: ^2.23.9 + version: 2.23.9 eslint: specifier: ^9.39.4 version: 9.39.4(jiti@2.6.1) @@ -77,13 +89,37 @@ importers: version: 8.57.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) vite: specifier: ^7.0.4 - version: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1) + version: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) vitest: specifier: ^3.0.0 - version: 3.2.4(jiti@2.6.1)(lightningcss@1.31.1) + version: 3.2.4(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) packages: + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@arkade-os/sdk@0.4.19': + resolution: {integrity: sha512-ZKcnWipcYiK0xwyaolytT48TPOq1koNyQsW1PbE2M/0X5PaHRxs9av0JHaXZEdUkYDRcXtrirccDv/CJ0dWPqQ==} + engines: {node: '>=22.12.0 <23', pnpm: '>=10.25.0 <11'} + peerDependencies: + '@react-native-async-storage/async-storage': '>=1.0.0' + expo: '>=54.0.0' + expo-background-task: ~1.0.10 + expo-sqlite: ~16.0.10 + expo-task-manager: ~14.0.9 + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + expo: + optional: true + expo-background-task: + optional: true + expo-sqlite: + optional: true + expo-task-manager: + optional: true + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -167,6 +203,51 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@bitcoinerlab/descriptors-core@3.1.7': + resolution: {integrity: sha512-7VccUDvKcHK7RF07Vo19Obax9jO3wlPWIXtvXy61GBqXptKv156O9Z4+sm2py1CuxPRpTXHlvH70G4KVVDoKlw==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@ledgerhq/ledger-bitcoin': ^0.3.1 + '@noble/curves': ^2.0.1 + '@noble/hashes': ^2.0.1 + '@scure/base': ^2.0.0 + '@scure/bip32': ^2.0.1 + '@scure/btc-signer': ^2.0.1 + bip32: ^5.0.1 + bitcoinjs-lib: ^7.0.1 + ecpair: ^3.0.1 + peerDependenciesMeta: + '@ledgerhq/ledger-bitcoin': + optional: true + '@noble/curves': + optional: true + '@noble/hashes': + optional: true + '@scure/base': + optional: true + '@scure/bip32': + optional: true + '@scure/btc-signer': + optional: true + bip32: + optional: true + bitcoinjs-lib: + optional: true + ecpair: + optional: true + + '@bitcoinerlab/descriptors-scure@3.1.7': + resolution: {integrity: sha512-jeyi8L3hzOquJn3t5w+NY3G93B/amZw83xeF8hrpwe7w4FMt2SH2o9rithEydQ2tP3Tlqfog+LnJOOChmfFPWw==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@ledgerhq/ledger-bitcoin': ^0.3.1 + peerDependenciesMeta: + '@ledgerhq/ledger-bitcoin': + optional: true + + '@bitcoinerlab/miniscript@2.0.0': + resolution: {integrity: sha512-P8yyubPf6lphmIZfyD/ZbhT/umJX7zH1mKjGql7z0Qt+xuffnz2AueQqq2/01VE2rTIq80VM0oRFdJClGBYx/g==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} @@ -533,6 +614,61 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@lendasat/lendaswap-sdk-pure@0.2.24': + resolution: {integrity: sha512-yZpXICPc0agU1/YTbQ77qrANnleU7hIM29SmP/dLtRhjXU+04axznXf3W3F4rjq24/tCD9Fbanbgt460blIprg==} + + '@marcbachmann/cel-js@7.3.1': + resolution: {integrity: sha512-P6o26TvjStT8V8+8EF+yq9Pp7ZFV00bpiUMbssr76XbIZGxaB+NNWeBp6WNxOrR9gp0JPzvJueCKHpOs5LE9PQ==} + engines: {node: '>=20.19.0'} + hasBin: true + + '@msgpack/msgpack@3.1.3': + resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==} + engines: {node: '>= 18'} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.0': + resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@2.0.1': + resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} + engines: {node: '>= 20.19.0'} + + '@noble/curves@2.2.0': + resolution: {integrity: sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==} + engines: {node: '>= 20.19.0'} + + '@noble/hashes@1.7.0': + resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + + '@noble/hashes@2.2.0': + resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} + engines: {node: '>= 20.19.0'} + + '@noble/secp256k1@3.0.0': + resolution: {integrity: sha512-NJBaR352KyIvj3t6sgT/+7xrNyF9Xk9QlLSIqUGVUYlsnDTAUqY8LOmwpcgEx4AMJXRITQ5XEVHD+mMaPfr3mg==} + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} @@ -832,6 +968,33 @@ packages: cpu: [x64] os: [win32] + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/base@2.0.0': + resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==} + + '@scure/base@2.2.0': + resolution: {integrity: sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip32@2.2.0': + resolution: {integrity: sha512-zFr7t2F+a9+5tB7QbarF2HQNYrgjCNaoLAupZdKkrFMYMozJf5zqH2WJCQibMzm1qQ0QogrxVGO3qXfQDYMaQg==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@scure/bip39@2.0.1': + resolution: {integrity: sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==} + + '@scure/bip39@2.2.0': + resolution: {integrity: sha512-T/Bj/YvYMNkIPq6EENO6/rcs2e7qTNuyoUXf0KBFDmp0ZDu0H2X4Lq6yC3i0c8PcWkov5EbW+yQZZbdMmk154A==} + + '@scure/btc-signer@2.0.1': + resolution: {integrity: sha512-vk5a/16BbSFZkhh1JIJ0+4H9nceZVo5WzKvJGGWiPp3sQOExeW+L53z3dI6u0adTPoE8ZbL+XEb6hEGzVZSvvQ==} + '@tailwindcss/node@4.2.1': resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} @@ -1020,6 +1183,9 @@ packages: engines: {node: '>= 10'} hasBin: true + '@tauri-apps/plugin-http@2.5.8': + resolution: {integrity: sha512-oxd7oypzQeu8kAfFCrw534Kq7Cw+NzozcnCY21O4rz3A+veJiIiuSCMIprgGcZOcLAXFP9GmDhKUbhuKWcunRw==} + '@tauri-apps/plugin-opener@2.5.3': resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==} @@ -1152,6 +1318,80 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@walletconnect/core@2.23.9': + resolution: {integrity: sha512-ws4WG8LeagUo2ERRo02HryXRcpwIRmCQ3pHLW5gWbVReLXXIpgk6ZAfID3fEGHevIwwnHSGww+nNhNpdXyiq0g==} + engines: {node: '>=18.20.8'} + + '@walletconnect/environment@1.0.1': + resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + + '@walletconnect/events@1.0.1': + resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + + '@walletconnect/heartbeat@1.2.2': + resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} + + '@walletconnect/jsonrpc-provider@1.0.14': + resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + + '@walletconnect/jsonrpc-types@1.0.4': + resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} + + '@walletconnect/jsonrpc-utils@1.0.8': + resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} + + '@walletconnect/keyvaluestorage@1.1.1': + resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + peerDependencies: + '@react-native-async-storage/async-storage': 1.x + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@walletconnect/logger@3.0.2': + resolution: {integrity: sha512-7wR3wAwJTOmX4gbcUZcFMov8fjftY05+5cO/d4cpDD8wDzJ+cIlKdYOXaXfxHLSYeDazMXIsxMYjHYVDfkx+nA==} + + '@walletconnect/relay-api@1.0.11': + resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} + + '@walletconnect/relay-auth@1.1.0': + resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} + + '@walletconnect/safe-json@1.0.2': + resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + + '@walletconnect/sign-client@2.23.9': + resolution: {integrity: sha512-Xj+hw4E6mGRyhCdVOT/RMgnG+up/Y3v0ho5PlkVozvXWeVSqHNh9DmjLuU97a7OACoGd/oHBF6g3NVqD7MgCMQ==} + + '@walletconnect/time@1.0.2': + resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + + '@walletconnect/types@2.23.9': + resolution: {integrity: sha512-IUl1PpD/Dig8IE2OZ9XtjbPohEyOZJ73xs92EDUzoIyzRtfm36g2D340pY3iu3AAdLv1yFiaZafB8Hf8RFze8A==} + + '@walletconnect/utils@2.23.9': + resolution: {integrity: sha512-C5TltCs8UPypNiteYnKSv8+ZDK2EjVDyXCxN6kA9bkA+j6KGsNIV7l9MUA8WBAvE5Gi5EcBdhD3R9Hpo/1HHqQ==} + + '@walletconnect/window-getters@1.0.1': + resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} + + '@walletconnect/window-metadata@1.0.1': + resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + + abitype@1.2.4: + resolution: {integrity: sha512-dpKH+N27vRjarMVTFFkeY445VTKftzGWpL0FiT7xmVmzQRKazZexzC5uHG0f6XKsVLAuUlndnbGau6lRejClxg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1169,6 +1409,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1180,6 +1424,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1187,11 +1435,31 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.10.0: resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} engines: {node: '>=6.0.0'} hasBin: true + better-sqlite3@12.9.0: + resolution: {integrity: sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==} + engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bip68@1.0.4: + resolution: {integrity: sha512-O1htyufFTYy3EO0JkHg2CLykdXEtV2ssqw47Gq9A0WByp662xpJnMEB9m43LZjsSDjIAOozWRExlFQk2hlV1XQ==} + engines: {node: '>=4.5.0'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -1204,6 +1472,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1227,6 +1498,13 @@ packages: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1240,6 +1518,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + cookie-es@2.0.0: resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} @@ -1247,6 +1528,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -1259,13 +1543,30 @@ packages: supports-color: optional: true + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1273,9 +1574,15 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dexie@4.4.2: + resolution: {integrity: sha512-zMtV8q79EFE5U8FKZvt0Y/77PCU/Hr/RDxv1EDeo228L+m/HTbeN2AjoQm674rhQCX8n3ljK87lajt7UQuZfvw==} + electron-to-chromium@1.5.307: resolution: {integrity: sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.20.0: resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} @@ -1283,6 +1590,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-toolkit@1.44.0: + resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==} + esbuild@0.27.3: resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} @@ -1356,6 +1666,17 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -1382,6 +1703,9 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1393,6 +1717,9 @@ packages: flatted@3.4.1: resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1406,6 +1733,12 @@ packages: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1417,6 +1750,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1427,6 +1763,12 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1443,6 +1785,15 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1494,6 +1845,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyvaluestorage-interface@1.0.0: + resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1572,6 +1926,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -1582,12 +1939,24 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + micro-packed@0.8.0: + resolution: {integrity: sha512-AKb8znIvg9sooythbXzyFeChEY0SkW0C6iXECpy/ls0e5BtwXO45J9wD9SLzBztnS4XmF/5kwZknsq+jyynd/A==} + engines: {node: '>= 20.19.0'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + minimatch@10.2.4: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} @@ -1595,28 +1964,78 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-abi@3.89.0: + resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} + engines: {node: '>=10'} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + node-releases@2.0.36: resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openapi-fetch@0.17.0: + resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==} + + openapi-typescript-helpers@0.1.0: + resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ox@0.9.3: + resolution: {integrity: sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1647,21 +2066,47 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.0.0: + resolution: {integrity: sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==} + hasBin: true + postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1672,6 +2117,16 @@ packages: qr.js@0.0.0: resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -1723,15 +2178,37 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -1769,6 +2246,18 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + slow-redact@0.3.2: + resolution: {integrity: sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==} + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: @@ -1779,12 +2268,23 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1803,6 +2303,16 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -1837,9 +2347,20 @@ packages: peerDependencies: typescript: '>=4.8.4' + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1856,6 +2377,85 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + + uint8array-tools@0.0.8: + resolution: {integrity: sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==} + engines: {node: '>=14.0.0'} + + uint8array-tools@0.0.9: + resolution: {integrity: sha512-9vqDWmoSXOoi+K14zNaf6LBV51Q8MayF0/IiQs3GlygIKUYtog603e6virExkjjFosfJUBI4LhbQK1iq8IG11A==} + engines: {node: '>=14.0.0'} + + uint8arrays@3.1.1: + resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -1890,6 +2490,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + varuint-bitcoin@2.0.0: + resolution: {integrity: sha512-6QZbU/rHO2ZQYpWFDALCDSRsXbAs1VOEmXAxtbtjLtKuMJ/FQ8YbhfxlaiKv5nklci0M6lZtlZyxo9Q+qNnyog==} + vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: @@ -1983,6 +2589,21 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -2001,6 +2622,24 @@ packages: snapshots: + '@adraffy/ens-normalize@1.11.1': {} + + '@arkade-os/sdk@0.4.19': + dependencies: + '@bitcoinerlab/descriptors-scure': 3.1.7 + '@marcbachmann/cel-js': 7.3.1 + '@noble/curves': 2.0.1 + '@noble/secp256k1': 3.0.0 + '@scure/base': 2.0.0 + '@scure/bip39': 2.0.1 + '@scure/btc-signer': 2.0.1 + bip68: 1.0.4 + transitivePeerDependencies: + - '@ledgerhq/ledger-bitcoin' + - bip32 + - bitcoinjs-lib + - ecpair + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -2113,6 +2752,36 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bitcoinerlab/descriptors-core@3.1.7(@noble/curves@2.2.0)(@noble/hashes@2.2.0)(@scure/base@2.0.0)(@scure/bip32@2.2.0)(@scure/btc-signer@2.0.1)': + dependencies: + '@bitcoinerlab/miniscript': 2.0.0 + lodash.memoize: 4.1.2 + uint8array-tools: 0.0.9 + varuint-bitcoin: 2.0.0 + optionalDependencies: + '@noble/curves': 2.2.0 + '@noble/hashes': 2.2.0 + '@scure/base': 2.0.0 + '@scure/bip32': 2.2.0 + '@scure/btc-signer': 2.0.1 + + '@bitcoinerlab/descriptors-scure@3.1.7': + dependencies: + '@bitcoinerlab/descriptors-core': 3.1.7(@noble/curves@2.2.0)(@noble/hashes@2.2.0)(@scure/base@2.0.0)(@scure/bip32@2.2.0)(@scure/btc-signer@2.0.1) + '@noble/curves': 2.2.0 + '@noble/hashes': 2.2.0 + '@scure/base': 2.0.0 + '@scure/bip32': 2.2.0 + '@scure/btc-signer': 2.0.1 + transitivePeerDependencies: + - bip32 + - bitcoinjs-lib + - ecpair + + '@bitcoinerlab/miniscript@2.0.0': + dependencies: + bip68: 1.0.4 + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 @@ -2368,6 +3037,65 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@lendasat/lendaswap-sdk-pure@0.2.24': + dependencies: + '@arkade-os/sdk': 0.4.19 + '@noble/curves': 2.2.0 + '@noble/hashes': 2.2.0 + '@scure/bip32': 2.2.0 + '@scure/bip39': 2.2.0 + '@scure/btc-signer': 2.0.1 + dexie: 4.4.2 + openapi-fetch: 0.17.0 + optionalDependencies: + better-sqlite3: 12.9.0 + transitivePeerDependencies: + - '@ledgerhq/ledger-bitcoin' + - '@react-native-async-storage/async-storage' + - bip32 + - bitcoinjs-lib + - ecpair + - expo + - expo-background-task + - expo-sqlite + - expo-task-manager + + '@marcbachmann/cel-js@7.3.1': {} + + '@msgpack/msgpack@3.1.3': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.8.0': + dependencies: + '@noble/hashes': 1.7.0 + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@2.0.1': + dependencies: + '@noble/hashes': 2.0.1 + + '@noble/curves@2.2.0': + dependencies: + '@noble/hashes': 2.2.0 + + '@noble/hashes@1.7.0': {} + + '@noble/hashes@1.8.0': {} + + '@noble/hashes@2.0.1': {} + + '@noble/hashes@2.2.0': {} + + '@noble/secp256k1@3.0.0': {} + '@radix-ui/primitive@1.1.3': {} '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)': @@ -2588,6 +3316,46 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true + '@scure/base@1.2.6': {} + + '@scure/base@2.0.0': {} + + '@scure/base@2.2.0': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip32@2.2.0': + dependencies: + '@noble/curves': 2.2.0 + '@noble/hashes': 2.2.0 + '@scure/base': 2.2.0 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@2.0.1': + dependencies: + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 + + '@scure/bip39@2.2.0': + dependencies: + '@noble/hashes': 2.2.0 + '@scure/base': 2.2.0 + + '@scure/btc-signer@2.0.1': + dependencies: + '@noble/curves': 2.0.1 + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 + micro-packed: 0.8.0 + '@tailwindcss/node@4.2.1': dependencies: '@jridgewell/remapping': 2.3.5 @@ -2649,12 +3417,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/vite@4.2.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))': + '@tailwindcss/vite@4.2.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0))': dependencies: '@tailwindcss/node': 4.2.1 '@tailwindcss/oxide': 4.2.1 tailwindcss: 4.2.1 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) '@tanstack/history@1.161.4': {} @@ -2737,6 +3505,10 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.10.1 '@tauri-apps/cli-win32-x64-msvc': 2.10.1 + '@tauri-apps/plugin-http@2.5.8': + dependencies: + '@tauri-apps/api': 2.10.1 + '@tauri-apps/plugin-opener@2.5.3': dependencies: '@tauri-apps/api': 2.10.1 @@ -2874,7 +3646,7 @@ snapshots: '@typescript-eslint/types': 8.57.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))': + '@vitejs/plugin-react@4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -2882,7 +3654,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -2894,13 +3666,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))': + '@vitest/mocker@3.2.4(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -2928,6 +3700,265 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 + '@walletconnect/core@2.23.9(typescript@5.8.3)(zod@4.3.6)': + 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 + '@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.9 + '@walletconnect/utils': 2.23.9(typescript@5.8.3)(zod@4.3.6) + '@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/environment@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/events@1.0.1': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + + '@walletconnect/heartbeat@1.2.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-provider@1.0.14': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-types@1.0.4': + dependencies: + events: 3.3.0 + keyvaluestorage-interface: 1.0.0 + + '@walletconnect/jsonrpc-utils@1.0.8': + dependencies: + '@walletconnect/environment': 1.0.1 + '@walletconnect/jsonrpc-types': 1.0.4 + tslib: 1.14.1 + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@walletconnect/keyvaluestorage@1.1.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + idb-keyval: 6.2.2 + unstorage: 1.17.5(idb-keyval@6.2.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' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/logger@3.0.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 10.0.0 + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.1 + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/sign-client@2.23.9(typescript@5.8.3)(zod@4.3.6)': + dependencies: + '@walletconnect/core': 2.23.9(typescript@5.8.3)(zod@4.3.6) + '@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.9 + '@walletconnect/utils': 2.23.9(typescript@5.8.3)(zod@4.3.6) + 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/time@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/types@2.23.9': + 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/utils@2.23.9(typescript@5.8.3)(zod@4.3.6)': + 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.9 + '@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.8.3)(zod@4.3.6) + 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/window-getters@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/window-metadata@1.0.1': + dependencies: + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + + abitype@1.2.4(typescript@5.8.3)(zod@4.3.6): + optionalDependencies: + typescript: 5.8.3 + zod: 4.3.6 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -2945,6 +3976,11 @@ snapshots: dependencies: color-convert: 2.0.1 + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + argparse@2.0.1: {} aria-hidden@1.2.6: @@ -2953,12 +3989,39 @@ snapshots: assertion-error@2.0.1: {} + atomic-sleep@1.0.0: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} + base64-js@1.5.1: + optional: true + baseline-browser-mapping@2.10.0: {} + better-sqlite3@12.9.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + optional: true + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + optional: true + + bip68@1.0.4: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + optional: true + + blakejs@1.2.1: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -2976,6 +4039,12 @@ snapshots: node-releases: 2.0.36 update-browserslist-db: 1.2.3(browserslist@4.28.1) + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + optional: true + cac@6.7.14: {} callsites@3.1.0: {} @@ -2997,6 +4066,13 @@ snapshots: check-error@2.1.3: {} + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + chownr@1.1.4: + optional: true + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3007,6 +4083,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie-es@1.2.3: {} + cookie-es@2.0.0: {} cross-spawn@7.0.6: @@ -3015,22 +4093,47 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + csstype@3.2.3: {} debug@4.4.3: dependencies: ms: 2.1.3 + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + optional: true + deep-eql@5.0.2: {} + deep-extend@0.6.0: + optional: true + deep-is@0.1.4: {} + defu@6.1.7: {} + + destr@2.0.5: {} + + detect-browser@5.3.0: {} + detect-libc@2.1.2: {} detect-node-es@1.1.0: {} + dexie@4.4.2: {} + electron-to-chromium@1.5.307: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + optional: true + enhanced-resolve@5.20.0: dependencies: graceful-fs: 4.2.11 @@ -3038,6 +4141,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-toolkit@1.44.0: {} + esbuild@0.27.3: optionalDependencies: '@esbuild/aix-ppc64': 0.27.3 @@ -3160,6 +4265,13 @@ snapshots: esutils@2.0.3: {} + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + expand-template@2.0.3: + optional: true + expect-type@1.3.0: {} fast-deep-equal@3.1.3: {} @@ -3176,6 +4288,9 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-uri-to-path@1.0.0: + optional: true + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3188,6 +4303,9 @@ snapshots: flatted@3.4.1: {} + fs-constants@1.0.0: + optional: true + fsevents@2.3.3: optional: true @@ -3195,6 +4313,14 @@ snapshots: get-nonce@1.0.1: {} + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + optional: true + + github-from-package@0.0.0: + optional: true + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -3203,6 +4329,18 @@ snapshots: graceful-fs@4.2.11: {} + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.3 + uncrypto: 0.1.3 + has-flag@4.0.0: {} hermes-estree@0.25.1: {} @@ -3211,6 +4349,11 @@ snapshots: dependencies: hermes-estree: 0.25.1 + idb-keyval@6.2.2: {} + + ieee754@1.2.1: + optional: true + ignore@5.3.2: {} ignore@7.0.5: {} @@ -3222,6 +4365,14 @@ snapshots: imurmurhash@0.1.4: {} + inherits@2.0.4: + optional: true + + ini@1.3.8: + optional: true + + iron-webcrypto@1.2.1: {} + is-extglob@2.1.1: {} is-glob@4.0.3: @@ -3256,6 +4407,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + keyvaluestorage-interface@1.0.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -3314,6 +4467,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.memoize@4.1.2: {} + lodash.merge@4.6.2: {} loose-envify@1.4.0: @@ -3322,6 +4477,8 @@ snapshots: loupe@3.2.1: {} + lru-cache@11.3.5: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -3330,6 +4487,13 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + micro-packed@0.8.0: + dependencies: + '@scure/base': 2.0.0 + + mimic-response@3.1.0: + optional: true + minimatch@10.2.4: dependencies: brace-expansion: 5.0.4 @@ -3338,16 +4502,57 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimist@1.2.8: + optional: true + + mkdirp-classic@0.5.3: + optional: true + ms@2.1.3: {} + multiformats@9.9.0: {} + nanoid@3.3.11: {} + napi-build-utils@2.0.0: + optional: true + natural-compare@1.4.0: {} + node-abi@3.89.0: + dependencies: + semver: 7.7.4 + optional: true + + node-fetch-native@1.6.7: {} + + node-mock-http@1.0.4: {} + node-releases@2.0.36: {} + normalize-path@3.0.0: {} + object-assign@4.1.1: {} + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.3 + + on-exit-leak-free@2.1.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + optional: true + + openapi-fetch@0.17.0: + dependencies: + openapi-typescript-helpers: 0.1.0 + + openapi-typescript-helpers@0.1.0: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3357,6 +4562,21 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ox@0.9.3(typescript@5.8.3)(zod@4.3.6): + 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.4(typescript@5.8.3)(zod@4.3.6) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - zod + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -3379,22 +4599,68 @@ snapshots: picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.3: {} + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@10.0.0: + dependencies: + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + slow-redact: 0.3.2 + sonic-boom: 4.2.1 + thread-stream: 3.1.0 + postcss@8.5.8: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.89.0 + pump: 3.0.4 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + optional: true + prelude-ls@1.2.1: {} + process-warning@5.0.0: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + optional: true + punycode@2.3.1: {} qr-scanner@1.4.2: @@ -3403,6 +4669,18 @@ snapshots: qr.js@0.0.0: {} + quick-format-unescaped@4.0.4: {} + + radix3@1.1.2: {} + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + optional: true + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 @@ -3447,8 +4725,22 @@ snapshots: react@19.2.4: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + optional: true + + readdirp@5.0.0: {} + + real-require@0.2.0: {} + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: + optional: true + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -3480,6 +4772,11 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 + safe-buffer@5.2.1: + optional: true + + safe-stable-stringify@2.5.0: {} + scheduler@0.27.0: {} semver@6.3.1: {} @@ -3531,6 +4828,22 @@ snapshots: siginfo@2.0.0: {} + simple-concat@1.0.1: + optional: true + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + + slow-redact@0.3.2: {} + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + sonner@2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: react: 19.2.4 @@ -3538,10 +4851,20 @@ snapshots: source-map-js@1.2.1: {} + split2@4.2.0: {} + stackback@0.0.2: {} std-env@3.10.0: {} + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + optional: true + + strip-json-comments@2.0.1: + optional: true + strip-json-comments@3.1.1: {} strip-literal@3.1.0: @@ -3556,6 +4879,27 @@ snapshots: tapable@2.3.0: {} + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.4 + tar-stream: 2.2.0 + optional: true + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + optional: true + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + tiny-invariant@1.3.3: {} tiny-warning@1.0.3: {} @@ -3579,8 +4923,23 @@ snapshots: dependencies: typescript: 5.8.3 + tslib@1.14.1: {} + tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.3 + get-tsconfig: 4.14.0 + optionalDependencies: + fsevents: 2.3.3 + optional: true + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + optional: true + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -3598,6 +4957,31 @@ snapshots: typescript@5.8.3: {} + ufo@1.6.3: {} + + uint8array-tools@0.0.8: {} + + uint8array-tools@0.0.9: {} + + uint8arrays@3.1.1: + dependencies: + multiformats: 9.9.0 + + uncrypto@0.1.3: {} + + unstorage@1.17.5(idb-keyval@6.2.2): + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.3.5 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.3 + optionalDependencies: + idb-keyval: 6.2.2 + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -3627,6 +5011,13 @@ snapshots: dependencies: react: 19.2.4 + util-deprecate@1.0.2: + optional: true + + varuint-bitcoin@2.0.0: + dependencies: + uint8array-tools: 0.0.8 + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -3636,13 +5027,13 @@ snapshots: - '@types/react' - '@types/react-dom' - vite-node@3.2.4(jiti@2.6.1)(lightningcss@1.31.1): + vite-node@3.2.4(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) transitivePeerDependencies: - '@types/node' - jiti @@ -3657,7 +5048,7 @@ snapshots: - tsx - yaml - vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1): + vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -3669,12 +5060,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 + tsx: 4.21.0 - vitest@3.2.4(jiti@2.6.1)(lightningcss@1.31.1): + vitest@3.2.4(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)) + '@vitest/mocker': 3.2.4(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -3692,8 +5084,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1) - vite-node: 3.2.4(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) + vite-node: 3.2.4(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) why-is-node-running: 2.3.0 transitivePeerDependencies: - jiti @@ -3720,6 +5112,11 @@ snapshots: word-wrap@1.2.5: {} + wrappy@1.0.2: + optional: true + + ws@7.5.10: {} + yallist@3.1.1: {} yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..9d4238f --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +allowBuilds: + better-sqlite3: true + esbuild: true + sharp: true +minimumReleaseAge: 4320 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0641553..538b053 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -412,15 +412,18 @@ dependencies = [ "rustls", "serde", "serde_json", + "sqlx", "subtle", "tauri", "tauri-build", + "tauri-plugin-http", "tauri-plugin-opener", "thiserror 2.0.18", "tokio", "tracing", "tracing-subscriber", "url", + "uuid", "zeroize", ] @@ -936,10 +939,29 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding", "time", "version_check", ] +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1129,6 +1151,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + [[package]] name = "dbus" version = "0.9.10" @@ -1263,6 +1291,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -2383,7 +2420,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -2883,6 +2920,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -3283,6 +3326,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", "block2", + "libc", "objc2", "objc2-core-foundation", ] @@ -3974,6 +4018,22 @@ dependencies = [ "prost 0.14.3", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + [[package]] name = "pulldown-cmark" version = "0.13.1" @@ -4322,6 +4382,8 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", + "cookie", + "cookie_store", "encoding_rs", "futures-core", "h2 0.4.13", @@ -5433,7 +5495,7 @@ dependencies = [ "unicode-segmentation", "url", "windows", - "windows-core 0.61.2", + "windows-core", "windows-version", "x11-dl", ] @@ -5586,6 +5648,54 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-fs" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e1ec28b79f3d0683f4507e1615c36292c0ea6716668770d4396b9b39871ed8" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", + "url", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfba7d4ec72763f9d1fdf73c217747f01e2c84b08b87a8cacd2f94f35853f84d" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http 1.4.0", + "regex", + "reqwest 0.12.28", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "tokio", + "url", + "urlpattern", +] + [[package]] name = "tauri-plugin-opener" version = "2.5.3" @@ -6756,7 +6866,7 @@ dependencies = [ "webview2-com-macros", "webview2-com-sys", "windows", - "windows-core 0.61.2", + "windows-core", "windows-implement", "windows-interface", ] @@ -6780,7 +6890,7 @@ checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ "thiserror 2.0.18", "windows", - "windows-core 0.61.2", + "windows-core", ] [[package]] @@ -6846,7 +6956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core", "windows-future", "windows-link 0.1.3", "windows-numerics", @@ -6858,7 +6968,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.2", + "windows-core", ] [[package]] @@ -6874,26 +6984,13 @@ dependencies = [ "windows-strings 0.4.2", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-future" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core 0.61.2", + "windows-core", "windows-link 0.1.3", "windows-threading", ] @@ -6938,7 +7035,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.2", + "windows-core", "windows-link 0.1.3", ] @@ -7476,7 +7573,7 @@ dependencies = [ "webkit2gtk-sys", "webview2-com", "windows", - "windows-core 0.61.2", + "windows-core", "windows-version", "x11-dl", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d5f3510..21ccdd2 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -21,6 +21,10 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } tauri-plugin-opener = "2" +# Proxy SDK fetches through Rust so mobile WebView origins aren't blocked by +# api.lendaswap.com's CORS whitelist (desktop localhost:1420 is allowed; the +# Android `http://tauri.localhost` and iOS `tauri://localhost` origins are not). +tauri-plugin-http = "2" serde = { version = "1", features = ["derive"] } serde_json = { version = "1", default-features = false, features = ["std"] } thiserror = { version = "2", default-features = false } @@ -47,6 +51,11 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } subtle = "2" hex = "0.4" +uuid = { version = "1", features = ["v4", "serde"] } + +# User-facing LendaSwap swap records live in a dedicated `lendaswap.db`. + +sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite", "macros"] } # Vendor OpenSSL for Android cross-compilation (ark-bdk-wallet pulls in native-tls via esplora-client) openssl = { version = "0.10", features = ["vendored"], optional = true } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4cdbf49..65f6ae9 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -5,6 +5,27 @@ "windows": ["main"], "permissions": [ "core:default", - "opener:default" + "opener:default", + { + "identifier": "opener:allow-open-url", + "allow": [ + { "url": "wc:*" } + ] + }, + { + "identifier": "http:default", + "allow": [ + { "url": "https://api.lendaswap.com/*" }, + { "url": "https://api.ark.boltz.exchange/*" }, + { "url": "https://api.yadio.io/*" }, + { "url": "https://arkade.computer/*" }, + { "url": "https://cloudflare.com/*" }, + { "url": "https://cloudflare-dns.com/*" }, + { "url": "https://asp.109-176-197-122.nip.io/*" }, + { "url": "http://afronut.xyz/*" }, + { "url": "https://*.walletconnect.org/*" }, + { "url": "https://*.walletconnect.com/*" } + ] + } ] } diff --git a/src-tauri/src/commands/lendaswap.rs b/src-tauri/src/commands/lendaswap.rs new file mode 100644 index 0000000..60d2912 --- /dev/null +++ b/src-tauri/src/commands/lendaswap.rs @@ -0,0 +1,320 @@ +//! DB-only Tauri commands for mirroring TS-SDK swap state into avark's +//! `lendaswap.db`. The TS SDK (`@lendasat/lendaswap-sdk-pure`) is the source +//! of truth for all LendaSwap network operations — these commands exist only +//! to persist the user-facing swap records for history + resume-after-restart. +//! +//! +//! Surface: +//! * `insert_lendaswap_swap` — frontend calls after TS SDK creates a swap +//! * `update_lendaswap_swap_status` — frontend calls after polling / WS update +//! * `get_lendaswap_swap` — plain DB read (no remote refresh) +//! * `list_lendaswap_swaps` — paginated history query +//! * `get_lendaswap_xprv` — derives a LendaSwap-purpose xprv from the +//! wallet's mnemonic and hands only that to the +//! WebView. + +use bitcoin::bip32::DerivationPath; +use bitcoin::Network; +use serde::Deserialize; +use zeroize::Zeroize; + +use crate::lendaswap::{unix_now, LendaSwapDb, LendaSwapError, SwapRecord}; +use crate::secure_storage::SecureStorage; + +/// BIP-32 derivation path used as the master for the TS SDK's `Signer`. +/// +/// Rationale: +/// * The SDK's `Client.builder().withXprv(...)` is documented as **never** +/// persisted to storage. Handing it a purpose-derived xprv keeps the root +/// mnemonic Rust-only and limits a WebView compromise to keys under this +/// subtree (LendaSwap HTLC preimages + per-swap signing keys + a Permit2 +/// EVM key bound to this purpose path — not the user's main BTC funds). +/// +/// `887'` is unused by the standard BIP registry (BIPs 32, 43, 44, 49, 84, +/// 86, 87…) so it doesn't collide with any wallet derivation a recovery tool +/// would attempt. The trailing `0'` is a keyspace generation: bump to `1'`, +/// `2'`, … to invalidate the LendaSwap subtree without rotating the user's +/// mnemonic, should that ever be needed. +const LENDASWAP_PURPOSE_PATH: &str = "m/887'/0'"; + +/// Payload from the TS SDK after it successfully creates a swap via +/// `client.createSwap(...)`. The frontend serialises the relevant fields from +/// the SDK response (plus its own user-facing UUID) and hands them here for +/// persistence. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct InsertSwapParams { + /// Our local user-facing id (frontend generates via `crypto.randomUUID()` + /// so the persisted row's id matches whatever the router/URLs use). + pub id: String, + pub lendaswap_id: String, + pub source_amount_sats: i64, + pub target_token: String, + pub target_amount: String, + pub destination_address: String, + pub ln_invoice: String, + pub network_fee: i64, + pub protocol_fee: i64, + pub service_fee: i64, + /// Canonical status from the SDK's initial response. We don't constrain + /// it here — the CHECK constraint on the column rejects garbage and + /// bubbles up as a Database error. + pub status: String, +} + +#[tauri::command] +pub(crate) async fn insert_lendaswap_swap( + db: tauri::State<'_, LendaSwapDb>, + params: InsertSwapParams, +) -> Result { + let now = unix_now() as i64; + sqlx::query( + "INSERT INTO lendaswap_swaps ( + id, lendaswap_id, direction, source_amount_sats, target_token, + target_amount, destination_address, ln_invoice, + network_fee, protocol_fee, service_fee, + status, created_at, updated_at + ) VALUES ( + ?1, ?2, 'btc_to_evm', ?3, ?4, + ?5, ?6, ?7, + ?8, ?9, ?10, + ?11, ?12, ?12 + )", + ) + .bind(¶ms.id) + .bind(¶ms.lendaswap_id) + .bind(params.source_amount_sats) + .bind(¶ms.target_token) + .bind(¶ms.target_amount) + .bind(¶ms.destination_address) + .bind(¶ms.ln_invoice) + .bind(params.network_fee) + .bind(params.protocol_fee) + .bind(params.service_fee) + .bind(¶ms.status) + .bind(now) + .execute(&db.0) + .await?; + + fetch_swap(&db.0, ¶ms.id).await +} + +/// Frontend passes the new status (and optional claim tx hash) after it +/// polls or receives a WebSocket event from the SDK. If the status is +/// terminal, `completed_at` is set to now automatically. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UpdateSwapStatusParams { + pub id: String, + pub status: String, + pub claim_tx_hash: Option, +} + +#[tauri::command] +pub(crate) async fn update_lendaswap_swap_status( + db: tauri::State<'_, LendaSwapDb>, + params: UpdateSwapStatusParams, +) -> Result { + let now = unix_now() as i64; + let completed_at = if crate::lendaswap::is_terminal(¶ms.status) { + Some(now) + } else { + None + }; + + let result = sqlx::query( + "UPDATE lendaswap_swaps + SET status = ?1, + claim_tx_hash = COALESCE(?2, claim_tx_hash), + updated_at = ?3, + completed_at = COALESCE(?4, completed_at) + WHERE id = ?5", + ) + .bind(¶ms.status) + .bind(params.claim_tx_hash.as_deref()) + .bind(now) + .bind(completed_at) + .bind(¶ms.id) + .execute(&db.0) + .await?; + + if result.rows_affected() == 0 { + return Err(LendaSwapError::NotFound { + id: params.id.clone(), + }); + } + + fetch_swap(&db.0, ¶ms.id).await +} + +/// Minimum age before `dismiss_lendaswap_swap` will act. The LN invoice → +/// Boltz submarine swap → LendaSwap LN node flow typically completes within +/// a minute; 10 minutes is a conservative floor that ensures an in-flight +/// payment isn't cut off prematurely by a user hitting the dismiss button too +/// eagerly. Enforced on the Rust side so the UI's age gate isn't the only one. +const DISMISS_MIN_AGE_SECS: i64 = 10 * 60; + +/// Locally mark a swap as `expired` when LendaSwap's server-side expiry is +/// lagging. This is a one-way "stop tracking" for swaps that avark has +/// refunded Boltz-side but LendaSwap still thinks are awaiting payment. +/// No LendaSwap API call — purely local record keeping so the reconciler +/// and history views move on. +#[tauri::command] +pub(crate) async fn dismiss_lendaswap_swap( + db: tauri::State<'_, LendaSwapDb>, + id: String, +) -> Result { + let row = fetch_swap(&db.0, &id).await?; + + // Only `awaiting_payment` swaps are candidates. `pending` is an initial + // pre-invoice state we don't expose to the user as dismissable; anything + // past awaiting_payment is either mid-flight or already terminal and + // must not be force-terminated here. + if row.status != "awaiting_payment" { + return Err(LendaSwapError::Other { + message: format!( + "Swap is in state `{}` — only `awaiting_payment` swaps can be dismissed. \ + If it has progressed, let it run its course.", + row.status + ), + }); + } + + let now = unix_now() as i64; + let age = now - row.created_at; + if age < DISMISS_MIN_AGE_SECS { + let mins_to_wait = ((DISMISS_MIN_AGE_SECS - age) + 59) / 60; + return Err(LendaSwapError::Other { + message: format!( + "Swap is too young to dismiss — wait {mins_to_wait} more minute(s) \ + in case the Lightning payment is still routing." + ), + }); + } + + let result = sqlx::query( + "UPDATE lendaswap_swaps + SET status = 'expired', + updated_at = ?1, + completed_at = COALESCE(completed_at, ?1) + WHERE id = ?2", + ) + .bind(now) + .bind(&id) + .execute(&db.0) + .await?; + + if result.rows_affected() == 0 { + return Err(LendaSwapError::NotFound { id }); + } + + fetch_swap(&db.0, &id).await +} + +/// Plain DB read — no remote refresh. The TS SDK handles freshness; this just +/// returns the last known state Rust has on file. +#[tauri::command] +pub(crate) async fn get_lendaswap_swap( + db: tauri::State<'_, LendaSwapDb>, + id: String, +) -> Result { + let row = fetch_swap(&db.0, &id).await; + if matches!(row, Err(LendaSwapError::NotFound { .. })) { + // Replace the placeholder id in the From impl with the + // real one the caller supplied. + return Err(LendaSwapError::NotFound { id }); + } + row +} + +/// Paginated list of swap records, newest first. `limit` is clamped to a hard +/// cap so a buggy caller can't load the whole table at once. Optional status +/// filter narrows to a single canonical state. +#[tauri::command] +pub(crate) async fn list_lendaswap_swaps( + db: tauri::State<'_, LendaSwapDb>, + limit: i64, + offset: i64, + status: Option, +) -> Result, LendaSwapError> { + const MAX_LIMIT: i64 = 100; + if !(1..=MAX_LIMIT).contains(&limit) { + return Err(LendaSwapError::Other { + message: format!("limit must be between 1 and {MAX_LIMIT} (got {limit})"), + }); + } + if offset < 0 { + return Err(LendaSwapError::Other { + message: format!("offset must be >= 0 (got {offset})"), + }); + } + + let rows = match status { + Some(s) => { + sqlx::query_as::<_, SwapRecord>( + "SELECT * FROM lendaswap_swaps \ + WHERE status = ?1 \ + ORDER BY created_at DESC \ + LIMIT ?2 OFFSET ?3", + ) + .bind(s) + .bind(limit) + .bind(offset) + .fetch_all(&db.0) + .await? + } + None => { + sqlx::query_as::<_, SwapRecord>( + "SELECT * FROM lendaswap_swaps \ + ORDER BY created_at DESC \ + LIMIT ?1 OFFSET ?2", + ) + .bind(limit) + .bind(offset) + .fetch_all(&db.0) + .await? + } + }; + Ok(rows) +} + +/// Derive a LendaSwap-purpose extended private key from the wallet's mnemonic +/// and return it serialized (base58check). The frontend hands this to the +/// SDK's `Client.builder().withXprv(...)`, which per the SDK docs is **never** +/// persisted to IndexedDB — so the secret only lives in WebView memory for +/// the session. +/// +/// Keeps the BIP39 mnemonic Rust-only: a WebView compromise (npm supply +/// chain, IDB exfil, dev tools) can drain LendaSwap-purpose keys but not the +/// user's main BTC funds. See `LENDASWAP_PURPOSE_PATH` for the derivation +/// rationale. +#[tauri::command] +pub(crate) async fn get_lendaswap_xprv(app: tauri::AppHandle) -> Result { + let store = SecureStorage::get_instance(&app); + let mut words = + crate::load_mnemonic(store).map_err(|_| LendaSwapError::WalletNotInitialized)?; + let secret = crate::wallet::parse_mnemonic(&words).map_err(|e| LendaSwapError::Other { + message: format!("invalid stored mnemonic: {e}"), + })?; + // `load_mnemonic` returns a plain String — zero the backing buffer once + // we've parsed it into a zeroizing SecretMnemonic. + words.zeroize(); + + let path: DerivationPath = LENDASWAP_PURPOSE_PATH + .parse() + .expect("LENDASWAP_PURPOSE_PATH is a valid BIP-32 path"); + let xpriv = crate::wallet::derive_xpriv(&secret, Network::Bitcoin, &path).map_err(|e| { + LendaSwapError::Other { + message: format!("xpriv derivation failed: {e}"), + } + })?; + Ok(xpriv.to_string()) +} + +async fn fetch_swap(pool: &sqlx::SqlitePool, id: &str) -> Result { + sqlx::query_as::<_, SwapRecord>("SELECT * FROM lendaswap_swaps WHERE id = ?1") + .bind(id) + .fetch_one(pool) + .await + .map_err(Into::into) +} diff --git a/src-tauri/src/commands/lightning.rs b/src-tauri/src/commands/lightning.rs index e43dd93..91c1f3a 100644 --- a/src-tauri/src/commands/lightning.rs +++ b/src-tauri/src/commands/lightning.rs @@ -1,4 +1,5 @@ use ark_client::SwapStorage as _; +use bitcoin::hashes::Hash as _; use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::time::Duration; @@ -540,6 +541,248 @@ pub async fn retry_claim_swap(app: tauri::AppHandle, swap_id: String) -> Result< Ok(msg) } +// ── Submarine-swap recovery ───────────────────────────────────────────────── +// +// Submarine swaps (send path) lock sats into a VHTLC while Boltz routes the +// Lightning payment. If `pay_ln_invoice` returns but `wait_for_invoice_paid` +// is interrupted (app kill, network blip, tauri_spawn cancellation), the +// swap is orphaned in avark's UI even though the VHTLC is perfectly fine. +// +// These commands expose the stored submarine swaps so users can see what's +// stuck and refund if Boltz is never going to route the payment. + +/// Boltz statuses where the VHTLC still holds funds AND a refund is allowed. +/// Cooperative refund (`refund_vhtlc`) works for the non-expired variants; +/// `swap.expired` requires the unilateral `refund_expired_vhtlc` path. +fn is_refundable_boltz_status(status: &str) -> bool { + matches!( + status, + "transaction.failed" | "invoice.failedToPay" | "invoice.expired" | "swap.expired" + ) +} + +fn needs_unilateral_refund(status: &str) -> bool { + status == "swap.expired" +} + +#[derive(Serialize)] +pub struct SubmarineSwapRecord { + pub id: String, + pub amount_sat: u64, + /// BOLT11 payment hash in hex. Truncated display is a UI concern. + pub payment_hash: String, + pub created_at: u64, + /// avark's locally-cached Boltz status (may be stale). + pub local_status: String, + /// Freshly-fetched Boltz status, or `None` if the API was unreachable. + pub boltz_status: Option, + pub is_terminal: bool, + pub is_successful_terminal: bool, + /// True when the funds are still locked AND a refund path is available. + pub is_refundable: bool, + /// True when the timelock has expired — refund goes via the unilateral + /// path (`refund_submarine_swap` dispatches automatically). + pub is_expired_timelock: bool, +} + +#[tauri::command] +pub async fn list_pending_submarine_swaps( + app: tauri::AppHandle, +) -> Result, AppError> { + let swap_storage = { + let state = app.state::(); + let guard = state.0.read().await; + Arc::clone( + &guard + .as_ref() + .ok_or_else(|| AppError::Wallet("Wallet not connected".into()))? + .swap_storage, + ) + }; + + let swaps = swap_storage + .list_all_submarine() + .await + .map_err(|e| AppError::Wallet(format!("Failed to list swaps: {e}")))?; + + // Submarine swaps have a ~24h timelock on Boltz. Fetch fresh status for + // anything within a 48h window — older swaps are certainly terminal and + // not worth an API round-trip. Sort newest-first so the UI can render + // the most relevant (usually just-attempted) swap at the top. + const RECENT_WINDOW_SECS: u64 = 48 * 60 * 60; + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + let mut records = Vec::with_capacity(swaps.len()); + let mut pending: Vec<(usize, String)> = Vec::new(); + + for s in &swaps { + let local_status = serde_json::to_value(&s.status) + .ok() + .and_then(|v| v.as_str().map(String::from)) + .unwrap_or_else(|| format!("{:?}", s.status)); + + let idx = records.len(); + records.push(SubmarineSwapRecord { + id: s.id.clone(), + amount_sat: s.amount.to_sat(), + payment_hash: hex::encode(s.preimage_hash.to_byte_array()), + created_at: s.created_at, + local_status: local_status.clone(), + boltz_status: None, + is_terminal: is_terminal_status(&local_status), + is_successful_terminal: is_successful_terminal(&local_status), + is_refundable: is_refundable_boltz_status(&local_status), + is_expired_timelock: needs_unilateral_refund(&local_status), + }); + + if now.saturating_sub(s.created_at) < RECENT_WINDOW_SECS { + pending.push((idx, s.id.clone())); + } + } + + // Refresh Boltz status for in-window swaps in parallel. + if !pending.is_empty() { + let mut set = tokio::task::JoinSet::new(); + for (idx, swap_id) in &pending { + let id = swap_id.clone(); + let i = *idx; + set.spawn(async move { + let result = check_boltz_status(&id).await; + (i, result) + }); + } + + while let Some(Ok((idx, result))) = set.join_next().await { + if let Ok(boltz_status) = result { + records[idx].boltz_status = Some(boltz_status.clone()); + records[idx].is_terminal = is_terminal_status(&boltz_status); + records[idx].is_successful_terminal = is_successful_terminal(&boltz_status); + records[idx].is_refundable = is_refundable_boltz_status(&boltz_status); + records[idx].is_expired_timelock = needs_unilateral_refund(&boltz_status); + } + } + } + + // Newest first. The user's "just now stuck" swap should be at the top. + records.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + + Ok(records) +} + +#[tauri::command] +pub async fn refund_submarine_swap( + app: tauri::AppHandle, + swap_id: String, +) -> Result { + let (client, swap_storage) = { + let state = app.state::(); + let guard = state.0.read().await; + let ws = guard + .as_ref() + .ok_or_else(|| AppError::Wallet("Wallet not connected".into()))?; + (Arc::clone(&ws.client), Arc::clone(&ws.swap_storage)) + }; + + // Fetch the freshest Boltz status so we pick the right refund path. + // If Boltz is unreachable we can't safely call cooperative refund, but + // we can still offer the unilateral path if the user knows the timelock + // has passed — kept simple for now, require a live status. + let boltz_status = check_boltz_status(&swap_id).await.map_err(|e| { + AppError::Wallet(format!( + "Couldn't reach Boltz to confirm the swap's current state: {e}" + )) + })?; + + if !is_refundable_boltz_status(&boltz_status) { + return Err(AppError::Wallet(format!( + "Swap is not refundable yet (Boltz status: {boltz_status}). \ + If it's still routing, wait — the checkout screen will update \ + automatically once it settles." + ))); + } + + info!(swap_id = %swap_id, status = %boltz_status, "refunding submarine swap"); + let _ = app.emit( + "ln-swap-progress", + format!("[{swap_id}] Refunding via Boltz (status: {boltz_status})..."), + ); + + let txid = if needs_unilateral_refund(&boltz_status) { + // Timelock expired — no Boltz signature needed. + client + .refund_expired_vhtlc(&swap_id) + .await + .map_err(|e| AppError::Wallet(format!("Unilateral refund failed: {e}")))? + } else { + // Cooperative refund — Boltz co-signs. Much faster than waiting for + // the timelock since the swap failed and Boltz knows it. + client + .refund_vhtlc(&swap_id) + .await + .map_err(|e| AppError::Wallet(format!("Cooperative refund failed: {e}")))? + }; + + info!(swap_id = %swap_id, refund_txid = %txid, "submarine swap refunded"); + + // Our refund tx is on-chain — mark the swap as refunded locally so the + // recovery UI doesn't keep offering the refund button while Boltz's + // status lags behind. `sync_swap_status_submarine` short-circuits on a + // parse failure; the log is purely diagnostic. + sync_swap_status_submarine(&swap_storage, &swap_id, "transaction.refunded").await; + + Ok(txid.to_string()) +} + +/// Mirror of `sync_swap_status` for submarine swaps. The SDK's `SwapStatus` +/// enum is not re-exported from `ark_client`'s root, but we can lean on +/// type inference via `SubmarineSwapData::status` to deserialize into it +/// without ever having to name the enum directly. +async fn sync_swap_status_submarine( + storage: &ark_client::SqliteSwapStorage, + swap_id: &str, + status_str: &str, +) { + let mut data = match storage.get_submarine(swap_id).await { + Ok(Some(d)) => d, + Ok(None) => { + warn!(swap_id = %swap_id, "submarine swap not found in storage"); + return; + } + Err(e) => { + warn!( + swap_id = %swap_id, + error = %e, + "failed to read submarine swap before status sync" + ); + return; + } + }; + // `data.status: SwapStatus` drives the deserializer's type inference. + match serde_json::from_value(serde_json::Value::String(status_str.to_string())) { + Ok(new_status) => { + data.status = new_status; + if let Err(e) = storage.update_status_submarine(swap_id, data.status).await { + warn!( + swap_id = %swap_id, + error = %e, + "failed to update submarine swap status locally" + ); + } + } + Err(e) => { + warn!( + swap_id = %swap_id, + status = %status_str, + error = %e, + "could not deserialize Boltz status into SwapStatus enum" + ); + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -621,4 +864,43 @@ mod tests { ); } } + + // ── is_refundable_boltz_status ────────────────────────────────────── + + #[test] + fn refundable_statuses() { + for status in [ + "transaction.failed", + "invoice.failedToPay", + "invoice.expired", + "swap.expired", + ] { + assert!( + is_refundable_boltz_status(status), + "{status} should be refundable" + ); + } + } + + #[test] + fn non_refundable_statuses() { + // Successful terminals — funds went to Boltz as intended. + assert!(!is_refundable_boltz_status("transaction.claimed")); + assert!(!is_refundable_boltz_status("invoice.settled")); + // Already refunded — nothing left to refund. + assert!(!is_refundable_boltz_status("transaction.refunded")); + // In-flight — still routing, may still succeed. + assert!(!is_refundable_boltz_status("transaction.mempool")); + assert!(!is_refundable_boltz_status("invoice.pending")); + assert!(!is_refundable_boltz_status("swap.created")); + } + + #[test] + fn unilateral_refund_only_on_timelock_expiry() { + assert!(needs_unilateral_refund("swap.expired")); + // Cooperative-refundable states should not need unilateral. + assert!(!needs_unilateral_refund("transaction.failed")); + assert!(!needs_unilateral_refund("invoice.failedToPay")); + assert!(!needs_unilateral_refund("invoice.expired")); + } } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index aa4479b..0a93065 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -1,4 +1,5 @@ pub mod coins; +pub mod lendaswap; pub mod lightning; pub mod pin; pub mod receive; diff --git a/src-tauri/src/commands/send.rs b/src-tauri/src/commands/send.rs index ff0b2e6..453753a 100644 --- a/src-tauri/src/commands/send.rs +++ b/src-tauri/src/commands/send.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::time::Duration; use ark_client::lightning_invoice::Bolt11Invoice; use serde::Serialize; @@ -107,9 +108,24 @@ fn msat_to_sat(msat: u64) -> Option { #[derive(Serialize)] pub struct SendResult { - txid: String, + /// For Lightning: the VHTLC funding txid (Boltz claims this VTXO once + /// the LN payment routes). For Ark/onchain: the actual send txid. + pub txid: String, + /// Lightning-only. Set to the Boltz submarine swap id if settlement + /// didn't complete within the timeout. The VHTLC is already funded; + /// the LN route is still resolving in the background. Callers should + /// render this as in-flight / pending, not as success. + #[serde(skip_serializing_if = "Option::is_none", rename = "pendingLnSwapId")] + pub pending_ln_swap_id: Option, } +/// How long we wait for the Lightning invoice to settle before returning +/// with a pending marker. The VHTLC funding already happened synchronously +/// in `pay_ln_invoice`; this is purely the "is Boltz done routing?" window. +/// Tuned for UI responsiveness — the polling/reconciliation layer catches +/// the actual settlement eventually. +const LN_SETTLEMENT_WAIT: Duration = Duration::from_secs(30); + #[tauri::command] pub async fn send_lightning( app: tauri::AppHandle, @@ -158,22 +174,50 @@ pub async fn send_lightning( ), ); + // Wait for LN settlement up to `LN_SETTLEMENT_WAIT`. The SDK's + // `wait_for_invoice_paid` itself has no deadline, so we wrap it in + // `tokio::time::timeout` — on elapse, return Ok with `pending_ln_swap_id` + // so the UI can render a stable "routing" state instead of hanging. let wait_result = tokio::select! { biased; _ = cancel_rx.changed() => { - return Err(AppError::Wallet("Wallet disconnected while paying Lightning invoice".into())); + return Err(AppError::Wallet( + "Wallet disconnected while paying Lightning invoice".into(), + )); } - wait = client.wait_for_invoice_paid(&result.swap_id) => wait, + wait = tokio::time::timeout(LN_SETTLEMENT_WAIT, client.wait_for_invoice_paid(&result.swap_id)) => wait, }; - wait_result - .map_err(|e| AppError::Wallet(format!("Lightning payment did not complete: {e}")))?; - - info!(swap_id = %result.swap_id, txid = %result.txid, "Lightning invoice paid"); - - Ok(SendResult { - txid: result.txid.to_string(), - }) + match wait_result { + Ok(Ok(())) => { + info!(swap_id = %result.swap_id, txid = %result.txid, "Lightning invoice paid"); + Ok(SendResult { + txid: result.txid.to_string(), + pending_ln_swap_id: None, + }) + } + Ok(Err(e)) => { + // SDK observed an error (Boltz rejected / timed out unilaterally). + // The VHTLC is still funded and refundable via the recovery screen. + Err(AppError::Wallet(format!( + "Lightning payment did not complete: {e}" + ))) + } + Err(_elapsed) => { + // Our window elapsed but the SDK is still waiting. Return a + // pending marker; polling / recovery handle the eventual resolution. + info!( + swap_id = %result.swap_id, + txid = %result.txid, + "Lightning settlement still in flight after {}s — returning pending", + LN_SETTLEMENT_WAIT.as_secs() + ); + Ok(SendResult { + txid: result.txid.to_string(), + pending_ln_swap_id: Some(result.swap_id.clone()), + }) + } + } } #[tauri::command] @@ -211,6 +255,7 @@ pub async fn send_ark( Ok(SendResult { txid: txid.to_string(), + pending_ln_swap_id: None, }) } @@ -254,6 +299,7 @@ pub async fn send_onchain( Ok(SendResult { txid: txid.to_string(), + pending_ln_swap_id: None, }) } @@ -339,9 +385,24 @@ mod tests { fn send_result_serializes() { let result = SendResult { txid: "abc123".to_string(), + pending_ln_swap_id: None, + }; + let json = serde_json::to_value(&result).unwrap(); + assert_eq!(json["txid"], "abc123"); + // `skip_serializing_if = "Option::is_none"` keeps the field out of the + // JSON entirely when absent, so Ark/onchain payloads look unchanged. + assert!(json.get("pendingLnSwapId").is_none()); + } + + #[test] + fn send_result_pending_serializes() { + let result = SendResult { + txid: "abc123".to_string(), + pending_ln_swap_id: Some("boltz-swap-xyz".to_string()), }; let json = serde_json::to_value(&result).unwrap(); assert_eq!(json["txid"], "abc123"); + assert_eq!(json["pendingLnSwapId"], "boltz-swap-xyz"); } #[test] diff --git a/src-tauri/src/commands/wallet.rs b/src-tauri/src/commands/wallet.rs index 4a4ab2a..3a545e0 100644 --- a/src-tauri/src/commands/wallet.rs +++ b/src-tauri/src/commands/wallet.rs @@ -7,9 +7,9 @@ use tracing::{debug, info, warn}; use super::lightning::{spawn_pending_swap_recovery, BOLTZ_URL}; use crate::{ - ark, boarding_db_path, load_mnemonic, read_settings, secure_storage, store_mnemonic, - swap_db_path, wallet, wallet_path, write_settings, AppError, AppWalletState, GlobalWalletState, - SettingsLock, WalletCreationLock, MNEMONIC_KEY, ONCHAIN_SYNC_INTERVAL, + ark, boarding_db_path, lendaswap_db_path, load_mnemonic, read_settings, secure_storage, + store_mnemonic, swap_db_path, wallet, wallet_path, write_settings, AppError, AppWalletState, + GlobalWalletState, SettingsLock, WalletCreationLock, MNEMONIC_KEY, ONCHAIN_SYNC_INTERVAL, }; #[derive(Debug, Serialize, serde::Deserialize)] @@ -451,8 +451,11 @@ pub async fn delete_wallet(app: tauri::AppHandle) -> Result<(), AppError> { let _ = store.delete(MNEMONIC_KEY); // 4. Best-effort cleanup of remaining files — don't bail on individual failures. + // Includes lendaswap.db so a subsequent wallet restore doesn't hit + // `MnemonicMismatch` against the SDK's stored mnemonic. let _ = remove_if_exists(&boarding_db_path(&app)?).await; let _ = remove_if_exists(&swap_db_path(&app)?).await; + let _ = remove_if_exists(&lendaswap_db_path(&app)?).await; // 5. Reset settings. let state = app.state::(); diff --git a/src-tauri/src/lendaswap.rs b/src-tauri/src/lendaswap.rs new file mode 100644 index 0000000..7b233fe --- /dev/null +++ b/src-tauri/src/lendaswap.rs @@ -0,0 +1,244 @@ +//! LendaSwap persistence layer. +//! +//! Pre-2026-04-23 this module wrapped `lendaswap-core` to orchestrate quote + +//! create + claim against the LendaSwap backend from Rust. That architecture +//! is gone — the canonical SDK is now the TypeScript package +//! `@lendasat/lendaswap-sdk-pure`, called directly from the frontend. See +//! `tasks/prd-btc-stablecoin-swap.md` for the architectural note. +//! +//! What remains here is persistence-only: open `lendaswap.db`, run migrations, +//! expose a typed `SwapRecord` + small error enum so the frontend can mirror +//! SDK state into SQLite via `commands::lendaswap::{insert,update,get,list}`. +//! The Rust side no longer knows how to talk to LendaSwap; it just stores what +//! the TS SDK tells it about. + +use serde::Serialize; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; +use std::path::Path; +use std::time::{SystemTime, UNIX_EPOCH}; +use tracing::info; + +pub(crate) struct LendaSwapDb(pub(crate) SqlitePool); + +/// The set of `status` values considered terminal — no further server-side +/// transition is expected and polling/reconcile should stop. +/// +/// **Mirrored in TS:** `TERMINAL_SWAP_STATUSES` in `src/lib/lendaswap/client.ts` +/// and the SQL `CHECK(status IN (…))` in the `lendaswap_swaps` table's +/// migration both list the same set. Three sources of truth — keep them in +/// sync until codegen exists. +pub(crate) const TERMINAL_STATUSES: &[&str] = &["completed", "failed", "expired", "refunded"]; + +pub(crate) fn is_terminal(status: &str) -> bool { + TERMINAL_STATUSES.contains(&status) +} + +pub(crate) fn unix_now() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0) +} + +/// Open `lendaswap.db` (creating if missing), run migrations, return the pool. +pub(crate) async fn init(path: &Path) -> Result { + if let Some(parent) = path.parent() { + tokio::fs::create_dir_all(parent).await.ok(); + } + let opts = SqliteConnectOptions::new() + .filename(path) + .create_if_missing(true) + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal); + let pool = SqlitePoolOptions::new() + .max_connections(5) + .connect_with(opts) + .await?; + migrate(&pool).await?; + info!(path = %path.display(), "lendaswap db ready"); + Ok(pool) +} + +async fn migrate(pool: &SqlitePool) -> Result<(), sqlx::Error> { + // Storage rule for this table: + // * BTC amounts (sats) → INTEGER. Includes source_amount_sats, network_fee, + // protocol_fee, service_fee — all sat-denominated u64 from LendaSwap. + // * Stablecoin amounts → TEXT (Decimal). target_amount only, because + // USDC/USDT carry fractional units that don't fit in an INTEGER. + sqlx::query( + r#" + CREATE TABLE IF NOT EXISTS lendaswap_swaps ( + id TEXT PRIMARY KEY NOT NULL, + lendaswap_id TEXT NOT NULL UNIQUE, + direction TEXT NOT NULL CHECK(direction IN ('btc_to_evm')), + source_amount_sats INTEGER NOT NULL, + target_token TEXT NOT NULL CHECK(target_token IN ('usdc_eth','usdt_eth')), + target_amount TEXT NOT NULL, + destination_address TEXT NOT NULL, + ln_invoice TEXT NOT NULL, + network_fee INTEGER NOT NULL, + protocol_fee INTEGER NOT NULL, + service_fee INTEGER NOT NULL, + status TEXT NOT NULL CHECK(status IN ( + 'pending','awaiting_payment','processing', + 'completed','failed','expired','refunded' + )), + claim_tx_hash TEXT, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + completed_at INTEGER + ) + "#, + ) + .execute(pool) + .await?; + + rebuild_fee_columns_if_legacy(pool).await?; + + sqlx::query("CREATE INDEX IF NOT EXISTS idx_lendaswap_swaps_status ON lendaswap_swaps(status)") + .execute(pool) + .await?; + + sqlx::query( + "CREATE INDEX IF NOT EXISTS idx_lendaswap_swaps_created_at \ + ON lendaswap_swaps(created_at DESC)", + ) + .execute(pool) + .await?; + + sqlx::query( + "CREATE INDEX IF NOT EXISTS idx_lendaswap_swaps_status_created_at \ + ON lendaswap_swaps(status, created_at DESC)", + ) + .execute(pool) + .await?; + + Ok(()) +} + +/// Migrate dev DBs created with the original schema where the three fee +/// columns were `TEXT`. Rebuilds the table in place with the INTEGER shape +/// and CASTs existing values across. Idempotent — the PRAGMA guard skips once +/// the new schema is in effect. Safe because the feature is pre-release and +/// the only rows present are dev test data. +async fn rebuild_fee_columns_if_legacy(pool: &SqlitePool) -> Result<(), sqlx::Error> { + let fee_type: Option = sqlx::query_scalar( + "SELECT type FROM pragma_table_info('lendaswap_swaps') WHERE name = 'network_fee'", + ) + .fetch_optional(pool) + .await?; + + if !matches!(fee_type.as_deref(), Some("TEXT")) { + return Ok(()); + } + + let mut tx = pool.begin().await?; + sqlx::query( + r#" + CREATE TABLE lendaswap_swaps_v2 ( + id TEXT PRIMARY KEY NOT NULL, + lendaswap_id TEXT NOT NULL UNIQUE, + direction TEXT NOT NULL CHECK(direction IN ('btc_to_evm')), + source_amount_sats INTEGER NOT NULL, + target_token TEXT NOT NULL CHECK(target_token IN ('usdc_eth','usdt_eth')), + target_amount TEXT NOT NULL, + destination_address TEXT NOT NULL, + ln_invoice TEXT NOT NULL, + network_fee INTEGER NOT NULL, + protocol_fee INTEGER NOT NULL, + service_fee INTEGER NOT NULL, + status TEXT NOT NULL CHECK(status IN ( + 'pending','awaiting_payment','processing', + 'completed','failed','expired','refunded' + )), + claim_tx_hash TEXT, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + completed_at INTEGER + ) + "#, + ) + .execute(&mut *tx) + .await?; + + sqlx::query( + "INSERT INTO lendaswap_swaps_v2 (\ + id, lendaswap_id, direction, source_amount_sats, target_token, \ + target_amount, destination_address, ln_invoice, \ + network_fee, protocol_fee, service_fee, \ + status, claim_tx_hash, created_at, updated_at, completed_at\ + ) \ + SELECT \ + id, lendaswap_id, direction, source_amount_sats, target_token, \ + target_amount, destination_address, ln_invoice, \ + CAST(network_fee AS INTEGER), CAST(protocol_fee AS INTEGER), \ + CAST(service_fee AS INTEGER), \ + status, claim_tx_hash, created_at, updated_at, completed_at \ + FROM lendaswap_swaps", + ) + .execute(&mut *tx) + .await?; + + sqlx::query("DROP TABLE lendaswap_swaps") + .execute(&mut *tx) + .await?; + sqlx::query("ALTER TABLE lendaswap_swaps_v2 RENAME TO lendaswap_swaps") + .execute(&mut *tx) + .await?; + tx.commit().await?; + + info!("lendaswap_swaps fee columns rebuilt as INTEGER"); + Ok(()) +} + +/// The full `lendaswap_swaps` row as returned to the frontend. Field names +/// match the table columns 1:1. +#[derive(Debug, Clone, Serialize, sqlx::FromRow)] +pub(crate) struct SwapRecord { + pub id: String, + pub lendaswap_id: String, + pub direction: String, + pub source_amount_sats: i64, + pub target_token: String, + pub target_amount: String, + pub destination_address: String, + pub ln_invoice: String, + pub network_fee: i64, + pub protocol_fee: i64, + pub service_fee: i64, + pub status: String, + pub claim_tx_hash: Option, + pub created_at: i64, + pub updated_at: i64, + pub completed_at: Option, +} + +/// Errors surfaced to the frontend from the DB-only Rust commands. +/// Tagged so the TS `formatLendaSwapError` can match on `kind`. +#[derive(Debug, thiserror::Error, Serialize)] +#[serde(tag = "kind", rename_all = "kebab-case")] +pub(crate) enum LendaSwapError { + #[error("database error: {message}")] + Database { message: String }, + #[error("swap not found: {id}")] + NotFound { id: String }, + #[error("wallet not initialized")] + WalletNotInitialized, + #[error("{message}")] + Other { message: String }, +} + +impl From for LendaSwapError { + fn from(e: sqlx::Error) -> Self { + match e { + // `fetch_one` on a missing id bubbles up RowNotFound; callers that + // know the id supply it themselves rather than relying on this + // unknown placeholder. + sqlx::Error::RowNotFound => Self::NotFound { + id: "(unknown)".into(), + }, + other => Self::Database { + message: other.to_string(), + }, + } + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 078c5d4..f0d29e9 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,6 @@ mod ark; mod commands; +mod lendaswap; mod secure_storage; mod wallet; @@ -111,6 +112,10 @@ pub(crate) fn swap_db_path(app: &tauri::AppHandle) -> Result app_data_file(app, "swaps.db") } +pub(crate) fn lendaswap_db_path(app: &tauri::AppHandle) -> Result { + app_data_file(app, "lendaswap.db") +} + pub(crate) const MNEMONIC_KEY: &str = "wallet-mnemonic"; pub(crate) fn store_mnemonic( @@ -145,8 +150,7 @@ pub(crate) async fn write_settings( tokio::fs::create_dir_all(dir).await?; } let data = serde_json::to_string_pretty(settings)?; - // Atomic write: write to a temp file then rename, so a crash mid-write - // never leaves a corrupted settings.json. + // Atomic write: write to a temp file then rename let tmp = path.with_extension("json.tmp"); tokio::fs::write(&tmp, data).await?; tokio::fs::rename(&tmp, &path).await?; @@ -235,6 +239,7 @@ pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_http::init()) .plugin(secure_storage::init()) .manage(SettingsLock(RwLock::new(()))) .manage(GlobalWalletState(RwLock::new(None))) @@ -242,6 +247,34 @@ pub fn run() { .manage(commands::receive::ReceiveSubscriptionState( tokio::sync::Mutex::new(None), )) + .setup(|app| { + // Open the swap records DB. Failure here leaves avark's core + // wallet features fully functional; the Swap tab will fail loudly + // on first IPC call. The frontend reconciliation logic surfaces + // that as a user-visible error (`formatLendaSwapError`). + let handle = app.handle().clone(); + match lendaswap_db_path(&handle) { + Ok(db_path) => match tauri::async_runtime::block_on(lendaswap::init(&db_path)) { + Ok(pool) => { + app.manage(lendaswap::LendaSwapDb(pool)); + } + Err(e) => { + tracing::error!( + error = %e, + path = %db_path.display(), + "lendaswap db init failed — swap commands will error" + ); + } + }, + Err(e) => { + tracing::error!( + error = %e, + "failed to resolve lendaswap db path — swap commands will error" + ); + } + }; + Ok(()) + }) .invoke_handler(tauri::generate_handler![ // Onboarding has_seen_onboarding, @@ -275,6 +308,15 @@ pub fn run() { commands::lightning::get_ln_invoice, commands::lightning::debug_list_swaps, commands::lightning::retry_claim_swap, + commands::lightning::list_pending_submarine_swaps, + commands::lightning::refund_submarine_swap, + // LendaSwap — DB-only, TS SDK handles network calls + commands::lendaswap::insert_lendaswap_swap, + commands::lendaswap::update_lendaswap_swap_status, + commands::lendaswap::dismiss_lendaswap_swap, + commands::lendaswap::get_lendaswap_swap, + commands::lendaswap::list_lendaswap_swaps, + commands::lendaswap::get_lendaswap_xprv, // Receive subscription commands::receive::start_receive_subscription, commands::receive::stop_receive_subscription, diff --git a/src-tauri/src/wallet.rs b/src-tauri/src/wallet.rs index 6a133d6..2ed5d9e 100644 --- a/src-tauri/src/wallet.rs +++ b/src-tauri/src/wallet.rs @@ -112,7 +112,6 @@ pub fn derive_master_xpriv_from_secret( Ok(xpriv) } -#[allow(dead_code)] /// Derive an extended private key at a specific BIP-32 derivation path. /// /// Uses an empty BIP39 passphrase (the spec-default). This is intentional: diff --git a/src/App.css b/src/App.css index 9bd85eb..1433790 100644 --- a/src/App.css +++ b/src/App.css @@ -33,6 +33,9 @@ --color-warning-bg: rgba(253, 224, 71, 0.2); --color-drawer-bg: #1f2937; --color-drawer-handle: rgba(255, 255, 255, 0.2); + --color-bitcoin: #f7931a; + --color-bitcoin-bg: rgba(247, 147, 26, 0.14); + --color-bitcoin-border: rgba(247, 147, 26, 0.4); } [data-theme="light"] { @@ -57,6 +60,9 @@ --color-warning-bg: rgba(202, 138, 4, 0.12); --color-drawer-bg: #ffffff; --color-drawer-handle: rgba(0, 0, 0, 0.15); + --color-bitcoin: #d97706; /* amber-600 — darker for contrast on light bg */ + --color-bitcoin-bg: rgba(217, 119, 6, 0.12); + --color-bitcoin-border: rgba(217, 119, 6, 0.35); } /* ── Theme utility classes ────────────────────────────────────────────── */ @@ -174,6 +180,129 @@ color: var(--color-text-faint); } +/* Primary solid button — lime accent with near-black text for readable + contrast. Mirrors the Receive CTA style on the Dashboard. */ +.theme-button-primary { + background: var(--color-accent); + color: #0a0f0a; + transition: filter 120ms ease, transform 120ms ease; +} +.theme-button-primary:hover:not(:disabled) { + filter: brightness(1.06); +} +.theme-button-primary:active:not(:disabled) { + transform: scale(0.98); +} +.theme-button-primary:disabled { + cursor: not-allowed; +} + +/* Bitcoin semantic utilities — paired with --color-bitcoin for the + "paying BTC" side of swap UIs. */ +.theme-btc { + color: var(--color-bitcoin); +} +.theme-btc-bg { + background: var(--color-bitcoin-bg); + color: var(--color-bitcoin); +} +.theme-btc-border { + border-color: var(--color-bitcoin-border); +} + +/* Display font — used sparingly on hero numerics + titles to give the + interface a monetary-instrument feel (Righteous is already loaded for + the splash screen, so no extra font request). */ +.font-display { + font-family: 'Righteous', cursive; + letter-spacing: 0.005em; +} + +/* Live signal pulse — attach to the dot shown next to a fresh countdown. */ +@keyframes live-pulse { + 0%, 100% { + opacity: 1; + box-shadow: 0 0 0 0 rgba(190, 242, 100, 0.45); + } + 60% { + opacity: 0.75; + box-shadow: 0 0 0 6px rgba(190, 242, 100, 0); + } + 100% { + opacity: 1; + box-shadow: 0 0 0 0 rgba(190, 242, 100, 0); + } +} +.live-dot { + animation: live-pulse 1.8s cubic-bezier(0.4, 0, 0.2, 1) infinite; +} + +/* Dot-grid background — subtle texture behind the unified swap panel + so it reads as a "machine surface" rather than a flat card. */ +.swap-surface { + position: relative; + background: + radial-gradient( + ellipse 120% 55% at 50% -10%, + var(--color-bitcoin-bg) 0%, + transparent 55% + ), + radial-gradient( + ellipse 120% 55% at 50% 110%, + var(--color-accent-bg) 0%, + transparent 55% + ), + var(--color-bg-card); +} +.swap-surface::before { + content: ''; + position: absolute; + inset: 0; + background-image: radial-gradient( + circle at 1px 1px, + var(--color-text-faint) 0.8px, + transparent 0 + ); + background-size: 14px 14px; + opacity: 0.35; + pointer-events: none; +} + +/* Checkout "claim ticket" surface — tighter dot-grid than .swap-surface, + no radial glow, so it reads as printed paper rather than machine glass. */ +.invoice-surface { + position: relative; + background: var(--color-bg-card); + overflow: hidden; +} +.invoice-surface::before { + content: ''; + position: absolute; + inset: 0; + background-image: radial-gradient( + circle at 1px 1px, + var(--color-text-faint) 0.7px, + transparent 0 + ); + background-size: 14px 14px; + opacity: 0.3; + pointer-events: none; +} + +/* Perforated divider — dashed horizontal line for ticket stubs. */ +.perforation { + height: 1px; + background-image: linear-gradient( + to right, + var(--color-border) 0, + var(--color-border) 4px, + transparent 4px, + transparent 8px + ); + background-size: 8px 1px; + background-repeat: repeat-x; +} + /* Hide scrollbar for horizontal scroll containers */ .scrollbar-none { -ms-overflow-style: none; diff --git a/src/App.tsx b/src/App.tsx index 7f63b3b..ed7cea1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,7 @@ import { router } from "./router"; import { ThemeProvider, useTheme } from "./context/ThemeContext"; import { PinLockProvider } from "./context/PinLockContext"; import { FiatProvider } from "./context/FiatContext"; +import { WalletConnectProvider } from "./context/WalletConnectContext"; import "./App.css"; function AppInner() { @@ -28,7 +29,9 @@ function AppInner() { - + + + diff --git a/src/SendSheet.tsx b/src/SendSheet.tsx index ad180ac..873264b 100644 --- a/src/SendSheet.tsx +++ b/src/SendSheet.tsx @@ -29,6 +29,13 @@ interface DetectAddressResult { interface SendResult { txid: string; + /** + * Lightning-only. When present, the VHTLC funding tx (`txid`) is on-chain + * but the LN settlement is still routing — treat the payment as submitted, + * not completed. The checkout screen uses this to flip to its "routing" + * state; SendSheet surfaces different success copy. + */ + pendingLnSwapId?: string | null; } type Step = 'form' | 'confirm' | 'sending' | 'success' | 'error'; @@ -55,6 +62,7 @@ function SendSheetContent({ const [scanning, setScanning] = useState(false); const [sendError, setSendError] = useState(null); const [txid, setTxid] = useState(null); + const [lnPending, setLnPending] = useState(false); const amountSats = /^\d+$/.test(amountInput) ? Number(amountInput) : null; const amountFiat = useSatsToFiat(amountSats ?? 0); @@ -311,6 +319,7 @@ function SendSheetContent({ }); } setTxid(result.txid); + setLnPending(Boolean(result.pendingLnSwapId)); setStep('success'); onSuccess(); } catch (err) { @@ -589,14 +598,25 @@ function SendSheetContent({ -

Sent!

-

+

+ {lnPending ? 'Routing…' : 'Sent!'} +

+

{amountSats != null ? formatSats(amountSats) : ''} sats{' '} {addressType === 'ark' ? 'via Ark' : addressType === 'bitcoin' ? 'onchain' : 'via Lightning'} + {lnPending && ( + <> +
+ + Funded but still routing. It may take a minute to settle — + you can close this. + + + )}

{txid && ( + ); + } + + if (status === "unconfigured") { + return ( +

+ {error ?? "WalletConnect not configured."} +

+ ); + } + + if (status === "connected" && address) { + return ( +
+
+ ); + } + + return ( + <> + + + { + if (!o) handleClose(); + }} + > + + + +