Merge pull request #84 from MutinyWallet/upgrade-to-3

Upgrade to 3
This commit is contained in:
Paul Miller
2023-05-09 10:34:05 -05:00
committed by GitHub
35 changed files with 740 additions and 254 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2023 Mutiny Wallet Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,6 +1,7 @@
{
"name": "mws",
"version": "0.1.1",
"version": "0.2.0",
"license": "MIT",
"scripts": {
"dev": "solid-start dev",
"build": "solid-start build",
@@ -9,9 +10,9 @@
},
"type": "module",
"devDependencies": {
"@types/node": "^18.16.5",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"@types/node": "^18.16.6",
"@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.59.5",
"autoprefixer": "^10.4.14",
"esbuild": "^0.14.54",
"eslint": "^8.40.0",
@@ -32,7 +33,7 @@
"@kobalte/core": "^0.8.2",
"@kobalte/tailwindcss": "^0.5.0",
"@modular-forms/solid": "^0.13.2",
"@mutinywallet/mutiny-wasm": "^0.2.8",
"@mutinywallet/mutiny-wasm": "^0.3.0",
"@mutinywallet/waila-wasm": "^0.1.5",
"@solid-primitives/upload": "^0.0.111",
"@solidjs/meta": "^0.28.4",

148
pnpm-lock.yaml generated
View File

@@ -11,8 +11,8 @@ dependencies:
specifier: ^0.13.2
version: 0.13.2(solid-js@1.7.5)
'@mutinywallet/mutiny-wasm':
specifier: ^0.2.8
version: 0.2.8
specifier: ^0.3.0
version: 0.3.0
'@mutinywallet/waila-wasm':
specifier: ^0.1.5
version: 0.1.5
@@ -52,14 +52,14 @@ dependencies:
devDependencies:
'@types/node':
specifier: ^18.16.5
version: 18.16.5
specifier: ^18.16.6
version: 18.16.6
'@typescript-eslint/eslint-plugin':
specifier: ^5.59.2
version: 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.40.0)(typescript@4.9.5)
specifier: ^5.59.5
version: 5.59.5(@typescript-eslint/parser@5.59.5)(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/parser':
specifier: ^5.59.2
version: 5.59.2(eslint@8.40.0)(typescript@4.9.5)
specifier: ^5.59.5
version: 5.59.5(eslint@8.40.0)(typescript@4.9.5)
autoprefixer:
specifier: ^10.4.14
version: 10.4.14(postcss@8.4.23)
@@ -74,7 +74,7 @@ devDependencies:
version: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.40.0)
eslint-plugin-import:
specifier: 2.27.5
version: 2.27.5(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0)
version: 2.27.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0)
eslint-plugin-prettier:
specifier: 4.2.1
version: 4.2.1(eslint@8.40.0)(prettier@2.8.8)
@@ -95,7 +95,7 @@ devDependencies:
version: 4.9.5
vite:
specifier: ^4.3.5
version: 4.3.5(@types/node@18.16.5)
version: 4.3.5(@types/node@18.16.6)
vite-plugin-pwa:
specifier: ^0.14.7
version: 0.14.7(vite@4.3.5)(workbox-build@6.5.4)(workbox-window@6.5.4)
@@ -1590,8 +1590,8 @@ packages:
solid-js: 1.7.5
dev: false
/@mutinywallet/mutiny-wasm@0.2.8:
resolution: {integrity: sha512-SaLdJTsK4XTW0xmD3Du8AnHw/hvbomxCbyANyA2IbV4aybtPPgRDcGwRsyCegPioYQHP0skqzwsi1E6k44GIBw==}
/@mutinywallet/mutiny-wasm@0.3.0:
resolution: {integrity: sha512-K+u2u/XMX1269U8af3T/ZvS+SzzrQcVYrdMi420dWCa14gke0vPWbGp+01zN7SCqBL4jp929emHTUZ4YBEpkzQ==}
dev: false
/@mutinywallet/waila-wasm@0.1.5:
@@ -1955,8 +1955,8 @@ packages:
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
dev: true
/@types/node@18.16.5:
resolution: {integrity: sha512-seOA34WMo9KB+UA78qaJoCO20RJzZGVXQ5Sh6FWu0g/hfT44nKXnej3/tCQl7FL97idFpBhisLYCTB50S0EirA==}
/@types/node@18.16.6:
resolution: {integrity: sha512-N7KINmeB8IN3vRR8dhgHEp+YpWvGFcpDoh5XZ8jB5a00AdFKCKEyyGTOPTddUf4JqU1ZKTVxkOxakDvchNVI2Q==}
/@types/offscreencanvas@2019.7.0:
resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==}
@@ -1965,22 +1965,22 @@ packages:
/@types/resolve@1.17.1:
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
dependencies:
'@types/node': 18.16.5
'@types/node': 18.16.6
dev: true
/@types/resolve@1.20.2:
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
/@types/semver@7.3.13:
resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
/@types/semver@7.5.0:
resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==}
dev: true
/@types/trusted-types@2.0.3:
resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==}
dev: true
/@typescript-eslint/eslint-plugin@5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-yVrXupeHjRxLDcPKL10sGQ/QlVrA8J5IYOEWVqk0lJaSZP7X5DfnP7Ns3cc74/blmbipQ1htFNVGsHX6wsYm0A==}
/@typescript-eslint/eslint-plugin@5.59.5(@typescript-eslint/parser@5.59.5)(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-feA9xbVRWJZor+AnLNAr7A8JRWeZqHUf4T9tlP+TN04b05pFVhO5eN7/O93Y/1OUlLMHKbnJisgDURs/qvtqdg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
'@typescript-eslint/parser': ^5.0.0
@@ -1991,10 +1991,10 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.5.1
'@typescript-eslint/parser': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/scope-manager': 5.59.2
'@typescript-eslint/type-utils': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/utils': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/scope-manager': 5.59.5
'@typescript-eslint/type-utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
debug: 4.3.4
eslint: 8.40.0
grapheme-splitter: 1.0.4
@@ -2007,8 +2007,8 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser@5.59.2(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==}
/@typescript-eslint/parser@5.59.5(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-NJXQC4MRnF9N9yWqQE2/KLRSOLvrrlZb48NGVfBa+RuPMN6B7ZcK5jZOvhuygv4D64fRKnZI4L4p8+M+rfeQuw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -2017,9 +2017,9 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 5.59.2
'@typescript-eslint/types': 5.59.2
'@typescript-eslint/typescript-estree': 5.59.2(typescript@4.9.5)
'@typescript-eslint/scope-manager': 5.59.5
'@typescript-eslint/types': 5.59.5
'@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5)
debug: 4.3.4
eslint: 8.40.0
typescript: 4.9.5
@@ -2027,16 +2027,16 @@ packages:
- supports-color
dev: true
/@typescript-eslint/scope-manager@5.59.2:
resolution: {integrity: sha512-dB1v7ROySwQWKqQ8rEWcdbTsFjh2G0vn8KUyvTXdPoyzSL6lLGkiXEV5CvpJsEe9xIdKV+8Zqb7wif2issoOFA==}
/@typescript-eslint/scope-manager@5.59.5:
resolution: {integrity: sha512-jVecWwnkX6ZgutF+DovbBJirZcAxgxC0EOHYt/niMROf8p4PwxxG32Qdhj/iIQQIuOflLjNkxoXyArkcIP7C3A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': 5.59.2
'@typescript-eslint/visitor-keys': 5.59.2
'@typescript-eslint/types': 5.59.5
'@typescript-eslint/visitor-keys': 5.59.5
dev: true
/@typescript-eslint/type-utils@5.59.2(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-b1LS2phBOsEy/T381bxkkywfQXkV1dWda/z0PhnIy3bC5+rQWQDS7fk9CSpcXBccPY27Z6vBEuaPBCKCgYezyQ==}
/@typescript-eslint/type-utils@5.59.5(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-4eyhS7oGym67/pSxA2mmNq7X164oqDYNnZCUayBwJZIRVvKpBCMBzFnFxjeoDeShjtO6RQBHBuwybuX3POnDqg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '*'
@@ -2045,8 +2045,8 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.59.2(typescript@4.9.5)
'@typescript-eslint/utils': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5)
'@typescript-eslint/utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
debug: 4.3.4
eslint: 8.40.0
tsutils: 3.21.0(typescript@4.9.5)
@@ -2055,13 +2055,13 @@ packages:
- supports-color
dev: true
/@typescript-eslint/types@5.59.2:
resolution: {integrity: sha512-LbJ/HqoVs2XTGq5shkiKaNTuVv5tTejdHgfdjqRUGdYhjW1crm/M7og2jhVskMt8/4wS3T1+PfFvL1K3wqYj4w==}
/@typescript-eslint/types@5.59.5:
resolution: {integrity: sha512-xkfRPHbqSH4Ggx4eHRIO/eGL8XL4Ysb4woL8c87YuAo8Md7AUjyWKa9YMwTL519SyDPrfEgKdewjkxNCVeJW7w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@typescript-eslint/typescript-estree@5.59.2(typescript@4.9.5):
resolution: {integrity: sha512-+j4SmbwVmZsQ9jEyBMgpuBD0rKwi9RxRpjX71Brr73RsYnEr3Lt5QZ624Bxphp8HUkSKfqGnPJp1kA5nl0Sh7Q==}
/@typescript-eslint/typescript-estree@5.59.5(typescript@4.9.5):
resolution: {integrity: sha512-+XXdLN2CZLZcD/mO7mQtJMvCkzRfmODbeSKuMY/yXbGkzvA9rJyDY5qDYNoiz2kP/dmyAxXquL2BvLQLJFPQIg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
@@ -2069,8 +2069,8 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 5.59.2
'@typescript-eslint/visitor-keys': 5.59.2
'@typescript-eslint/types': 5.59.5
'@typescript-eslint/visitor-keys': 5.59.5
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
@@ -2081,18 +2081,18 @@ packages:
- supports-color
dev: true
/@typescript-eslint/utils@5.59.2(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-kSuF6/77TZzyGPhGO4uVp+f0SBoYxCDf+lW3GKhtKru/L8k/Hd7NFQxyWUeY7Z/KGB2C6Fe3yf2vVi4V9TsCSQ==}
/@typescript-eslint/utils@5.59.5(eslint@8.40.0)(typescript@4.9.5):
resolution: {integrity: sha512-sCEHOiw+RbyTii9c3/qN74hYDPNORb8yWCoPLmB7BIflhplJ65u2PBpdRla12e3SSTJ2erRkPjz7ngLHhUegxA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.40.0)
'@types/json-schema': 7.0.11
'@types/semver': 7.3.13
'@typescript-eslint/scope-manager': 5.59.2
'@typescript-eslint/types': 5.59.2
'@typescript-eslint/typescript-estree': 5.59.2(typescript@4.9.5)
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 5.59.5
'@typescript-eslint/types': 5.59.5
'@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5)
eslint: 8.40.0
eslint-scope: 5.1.1
semver: 7.5.0
@@ -2101,11 +2101,11 @@ packages:
- typescript
dev: true
/@typescript-eslint/visitor-keys@5.59.2:
resolution: {integrity: sha512-EEpsO8m3RASrKAHI9jpavNv9NlEUebV4qmF1OWxSTtKSFBpC1NCmWazDQHFivRf0O1DV11BA645yrLEVQ0/Lig==}
/@typescript-eslint/visitor-keys@5.59.5:
resolution: {integrity: sha512-qL+Oz+dbeBRTeyJTIy0eniD3uvqU7x+y1QceBismZ41hd4aBSRh8UAw4pZP0+XzLuPZmx4raNMq/I+59W2lXKA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': 5.59.2
'@typescript-eslint/types': 5.59.5
eslint-visitor-keys: 3.4.1
dev: true
@@ -2343,7 +2343,7 @@ packages:
hasBin: true
dependencies:
caniuse-lite: 1.0.30001486
electron-to-chromium: 1.4.385
electron-to-chromium: 1.4.387
node-releases: 2.0.10
update-browserslist-db: 1.0.11(browserslist@4.21.5)
@@ -2626,8 +2626,8 @@ packages:
jake: 10.8.5
dev: true
/electron-to-chromium@1.4.385:
resolution: {integrity: sha512-L9zlje9bIw0h+CwPQumiuVlfMcV4boxRjFIWDcLfFqTZNbkwOExBzfmswytHawObQX4OUhtNv8gIiB21kOurIg==}
/electron-to-chromium@1.4.387:
resolution: {integrity: sha512-tutLf+alr1/0YqJwKPdstVvDLmxmLb5xNyDLNS0RZmenHcEYk9qKfpKDCVZEKJ00JVbnayJm1MZAbYhYDFpcOw==}
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2990,7 +2990,7 @@ packages:
dependencies:
debug: 4.3.4
eslint: 8.40.0
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0)
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0)
glob: 7.2.3
is-glob: 4.0.3
resolve: 1.22.2
@@ -2999,7 +2999,7 @@ packages:
- supports-color
dev: true
/eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0):
/eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'}
peerDependencies:
@@ -3020,7 +3020,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
debug: 3.2.7
eslint: 8.40.0
eslint-import-resolver-node: 0.3.7
@@ -3029,7 +3029,7 @@ packages:
- supports-color
dev: true
/eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0):
/eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0):
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
engines: {node: '>=4'}
peerDependencies:
@@ -3039,7 +3039,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
array-includes: 3.1.6
array.prototype.flat: 1.3.1
array.prototype.flatmap: 1.3.1
@@ -3047,7 +3047,7 @@ packages:
doctrine: 2.1.0
eslint: 8.40.0
eslint-import-resolver-node: 0.3.7
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0)
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.40.0)
has: 1.0.3
is-core-module: 2.12.0
is-glob: 4.0.3
@@ -3084,7 +3084,7 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
'@typescript-eslint/utils': 5.59.2(eslint@8.40.0)(typescript@4.9.5)
'@typescript-eslint/utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
eslint: 8.40.0
is-html: 2.0.0
jsx-ast-utils: 3.3.3
@@ -3767,7 +3767,7 @@ packages:
resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
engines: {node: '>= 10.13.0'}
dependencies:
'@types/node': 18.16.5
'@types/node': 18.16.6
merge-stream: 2.0.0
supports-color: 7.2.0
dev: true
@@ -4577,7 +4577,7 @@ packages:
solid-start: 0.2.26(@solidjs/meta@0.28.4)(@solidjs/router@0.8.2)(solid-js@1.7.5)(solid-start-node@0.2.26)(vite@4.3.5)
terser: 5.17.2
undici: 5.22.0
vite: 4.3.5(@types/node@18.16.5)
vite: 4.3.5(@types/node@18.16.6)
transitivePeerDependencies:
- supports-color
@@ -4647,8 +4647,8 @@ packages:
solid-start-node: 0.2.26(solid-start@0.2.26)(undici@5.22.0)(vite@4.3.5)
terser: 5.17.2
undici: 5.22.0
vite: 4.3.5(@types/node@18.16.5)
vite-plugin-inspect: 0.7.25(rollup@3.21.5)(vite@4.3.5)
vite: 4.3.5(@types/node@18.16.6)
vite-plugin-inspect: 0.7.26(rollup@3.21.5)(vite@4.3.5)
vite-plugin-solid: 2.7.0(solid-js@1.7.5)(vite@4.3.5)
wait-on: 6.0.1(debug@4.3.4)
transitivePeerDependencies:
@@ -5044,8 +5044,8 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
/vite-plugin-inspect@0.7.25(rollup@3.21.5)(vite@4.3.5):
resolution: {integrity: sha512-11j3hG3stRfFkoI+adIDX+KvZueWNgd9lFGdh7lgm0IjGqpP6luCQAMSSnHHV7AZXaTE06X+bUG3M68diz8ZyA==}
/vite-plugin-inspect@0.7.26(rollup@3.21.5)(vite@4.3.5):
resolution: {integrity: sha512-gRjBay+OxLr/Dr+HXlfJVXZH0cqhE5hkkBvo2du2cA1LGUBnV8Aym89AdPrURkSpTk3Rvw9dNWM2VLIuw6RKJg==}
engines: {node: '>=14'}
peerDependencies:
vite: ^3.1.0 || ^4.0.0
@@ -5056,7 +5056,7 @@ packages:
fs-extra: 11.1.1
picocolors: 1.0.0
sirv: 2.0.3
vite: 4.3.5(@types/node@18.16.5)
vite: 4.3.5(@types/node@18.16.6)
transitivePeerDependencies:
- rollup
- supports-color
@@ -5073,7 +5073,7 @@ packages:
fast-glob: 3.2.12
pretty-bytes: 6.1.0
rollup: 3.21.5
vite: 4.3.5(@types/node@18.16.5)
vite: 4.3.5(@types/node@18.16.6)
workbox-build: 6.5.4
workbox-window: 6.5.4
transitivePeerDependencies:
@@ -5093,7 +5093,7 @@ packages:
merge-anything: 5.1.6
solid-js: 1.7.5
solid-refresh: 0.5.2(solid-js@1.7.5)
vite: 4.3.5(@types/node@18.16.5)
vite: 4.3.5(@types/node@18.16.6)
vitefu: 0.2.4(vite@4.3.5)
transitivePeerDependencies:
- supports-color
@@ -5103,10 +5103,10 @@ packages:
peerDependencies:
vite: ^2 || ^3 || ^4
dependencies:
vite: 4.3.5(@types/node@18.16.5)
vite: 4.3.5(@types/node@18.16.6)
dev: true
/vite@4.3.5(@types/node@18.16.5):
/vite@4.3.5(@types/node@18.16.6):
resolution: {integrity: sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
@@ -5131,7 +5131,7 @@ packages:
terser:
optional: true
dependencies:
'@types/node': 18.16.5
'@types/node': 18.16.6
esbuild: 0.17.18
postcss: 8.4.23
rollup: 3.21.5
@@ -5146,7 +5146,7 @@ packages:
vite:
optional: true
dependencies:
vite: 4.3.5(@types/node@18.16.5)
vite: 4.3.5(@types/node@18.16.6)
/wait-on@6.0.1(debug@4.3.4):
resolution: {integrity: sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

18
src/assets/icons/coin.svg Normal file
View File

@@ -0,0 +1,18 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#a)">
<mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
<path fill-rule="evenodd" clip-rule="evenodd" d="M.667 8A7.333 7.333 0 0 1 8 .667c.368 0 .667.298.667.666v13.334a.667.667 0 0 1-.667.666A7.333 7.333 0 0 1 .667 8Zm6.666-5.963a6 6 0 0 0 0 11.926V2.037Z" fill="#fff"/>
<path d="M8 1.333a6.667 6.667 0 1 1 0 13.334V1.333Z" fill="#fff"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.333 1.333c0-.368.299-.666.667-.666a7.333 7.333 0 1 1 0 14.666.667.667 0 0 1-.667-.666V1.333Zm1.334.704v11.926a6 6 0 0 0 0-11.926Z" fill="#fff"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.333 4c0-.368.299-.667.667-.667h5a.667.667 0 0 1 0 1.334H3A.667.667 0 0 1 2.333 4ZM1 6.667C1 6.299 1.298 6 1.667 6H8a.667.667 0 0 1 0 1.333H1.667A.667.667 0 0 1 1 6.667Zm0 2.666c0-.368.298-.666.667-.666H8A.667.667 0 1 1 8 10H1.667A.667.667 0 0 1 1 9.333ZM2.333 12c0-.368.299-.667.667-.667h5a.667.667 0 0 1 0 1.334H3A.667.667 0 0 1 2.333 12Z" fill="#fff"/>
</mask>
<g mask="url(#b)">
<path d="M0 0h16v16H0V0Z" fill="#fff"/>
</g>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h16v16H0z"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

16
src/assets/icons/rs.svg Normal file
View File

@@ -0,0 +1,16 @@
<svg width="64" height="57" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="b" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="30" y="10" width="34" height="38">
<path fill="url(#a)" d="M30 10h34v38H30z"/>
</mask>
<g mask="url(#b)">
<path d="M49.294 46.586c8.25 0 13.219-4.008 13.219-10.313v-.023c0-5.273-3.07-8.133-10.102-9.586l-3.656-.75c-4.078-.844-5.93-2.25-5.93-4.64v-.024c0-2.695 2.461-4.547 6.422-4.57 3.797 0 6.399 1.758 6.797 4.71l.047.282h5.79l-.024-.399c-.352-5.789-5.18-9.68-12.563-9.68-7.289 0-12.515 4.032-12.539 9.985v.024c0 5.039 3.281 8.132 9.938 9.515l3.632.75c4.36.914 6.118 2.274 6.118 4.805v.023c0 2.907-2.672 4.805-6.938 4.805-4.242 0-7.219-1.805-7.664-4.71l-.047-.282h-5.789l.024.351c.398 6.07 5.507 9.727 13.265 9.727Z" fill="#F61D5B" fill-opacity=".5"/>
</g>
<path d="M2.977 46h6.046V33.344h6.54L22.218 46h6.89l-7.382-13.57c3.937-1.43 6.375-5.11 6.375-9.657v-.046c0-6.54-4.407-10.547-11.625-10.547h-13.5V46Zm6.046-17.438V17.078h6.704c3.796 0 6.187 2.156 6.187 5.695v.047c0 3.633-2.25 5.742-6.07 5.742h-6.82Z" fill="#F61D5B"/>
<path d="M44.86 46.586c8.25 0 13.218-4.008 13.218-10.313v-.023c0-5.273-3.07-8.133-10.101-9.586l-3.657-.75c-4.078-.844-5.93-2.25-5.93-4.64v-.024c0-2.695 2.462-4.547 6.422-4.57 3.797 0 6.399 1.758 6.797 4.71l.047.282h5.79l-.024-.399c-.352-5.789-5.18-9.68-12.563-9.68-7.289 0-12.515 4.032-12.539 9.985v.024c0 5.039 3.282 8.132 9.938 9.515l3.633.75c4.359.914 6.117 2.274 6.117 4.805v.023c0 2.907-2.672 4.805-6.938 4.805-4.242 0-7.218-1.805-7.664-4.71l-.047-.282H31.57l.024.351c.398 6.07 5.508 9.727 13.265 9.727Z" fill="#F61D5B" fill-opacity=".75"/>
<defs>
<linearGradient id="a" x1="64" y1="29.292" x2="30" y2="29.292" gradientUnits="userSpaceOnUse">
<stop stop-color="#black" stop-opacity=".5"/>
<stop offset="1" stop-color="#black"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

BIN
src/assets/wave.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,17 +1,21 @@
import send from '~/assets/icons/send.svg';
import receive from '~/assets/icons/receive.svg';
import { Card, LoadingSpinner, NiceP, SmallAmount, SmallHeader, VStack } from './layout';
import { For, Match, ParentComponent, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
import { ButtonLink, Card, LoadingSpinner, NiceP, SmallAmount, SmallHeader, VStack } from './layout';
import { For, Match, ParentComponent, Show, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
import { useMegaStore } from '~/state/megaStore';
import { MutinyInvoice } from '@mutinywallet/mutiny-wasm';
import { prettyPrintTime } from '~/utils/prettyPrintTime';
import { JsonModal } from '~/components/JsonModal';
import mempoolTxUrl from '~/utils/mempoolTxUrl';
import wave from "~/assets/wave.gif"
import utxoIcon from '~/assets/icons/coin.svg';
import { getRedshifted } from '~/utils/fakeLabels';
export const THREE_COLUMNS = 'grid grid-cols-[auto,1fr,auto] gap-4 py-2 px-2 border-b border-neutral-800 last:border-b-0'
export const CENTER_COLUMN = 'min-w-0 overflow-hidden max-w-full'
export const MISSING_LABEL = 'py-1 px-2 bg-white/10 rounded inline-block text-sm'
export const RIGHT_COLUMN = 'flex flex-col items-right text-right max-w-[9rem]'
export const REDSHIFT_LABEL = 'py-1 px-2 bg-white text-m-red rounded inline-block text-sm'
export const RIGHT_COLUMN = 'flex flex-col items-right text-right max-w-[8rem]'
export type OnChainTx = {
txid: string
@@ -26,14 +30,15 @@ export type OnChainTx = {
}
}
type Utxo = {
export type UtxoItem = {
outpoint: string
txout: {
value: number
script_pubkey: string
}
keychain: string
is_spent: boolean
is_spent: boolean,
redshifted?: boolean
}
const SubtleText: ParentComponent = (props) => {
@@ -58,8 +63,7 @@ function OnChainItem(props: { item: OnChainTx }) {
</div>
<div class={CENTER_COLUMN}>
<h2 class={MISSING_LABEL}>Unknown</h2>
{isReceive() ? <SmallAmount amount={props.item.received} sign="+" /> : <SmallAmount amount={props.item.sent} />}
{/* <h2 class="truncate">Txid: {props.item.txid}</h2> */}
{isReceive() ? <SmallAmount amount={props.item.received} /> : <SmallAmount amount={props.item.sent} />}
</div>
<div class={RIGHT_COLUMN}>
<SmallHeader>
@@ -99,23 +103,31 @@ function InvoiceItem(props: { item: MutinyInvoice }) {
)
}
function Utxo(props: { item: Utxo }) {
function Utxo(props: { item: UtxoItem }) {
const spent = createMemo(() => props.item.is_spent);
const [open, setOpen] = createSignal(false)
const redshifted = createMemo(() => getRedshifted(props.item.outpoint));
return (
<>
<JsonModal open={open()} data={props.item} title="Unspent Transaction Output" setOpen={setOpen} />
<div class={THREE_COLUMNS} onClick={() => setOpen(!open())}>
<img src={receive} alt="receive arrow" />
<div class="flex items-center">
<img src={utxoIcon} alt="coin" />
</div>
<div class={CENTER_COLUMN}>
<h2 class={MISSING_LABEL}>Unknown</h2>
<div class="flex gap-2">
<Show when={redshifted()} fallback={<h2 class={MISSING_LABEL}>Unknown</h2>}>
<h2 class={REDSHIFT_LABEL}>Redshift</h2>
</Show>
</div>
<SmallAmount amount={props.item.txout.value} />
</div>
<div class={RIGHT_COLUMN}>
<SmallHeader class={spent() ? "text-m-red" : "text-m-green"}>
{spent() ? "SPENT" : "UNSPENT"}
{/* {spent() ? "SPENT" : "UNSPENT"} */}
</SmallHeader>
</div>
</div>
@@ -128,19 +140,19 @@ export function Activity() {
const getTransactions = async () => {
console.log("Getting onchain txs");
const txs = await state.node_manager?.list_onchain() as OnChainTx[];
const txs = await state.mutiny_wallet?.list_onchain() as OnChainTx[];
return txs.reverse();
}
const getInvoices = async () => {
console.log("Getting invoices");
const invoices = await state.node_manager?.list_invoices() as MutinyInvoice[];
const invoices = await state.mutiny_wallet?.list_invoices() as MutinyInvoice[];
return invoices.filter((inv) => inv.paid).reverse();
}
const getUtXos = async () => {
console.log("Getting utxos");
const utxos = await state.node_manager?.list_utxos() as Utxo[];
const utxos = await state.mutiny_wallet?.list_utxos() as UtxoItem[];
return utxos;
}
@@ -201,6 +213,7 @@ export function Activity() {
</For>
</Match>
</Switch>
<ButtonLink href="/redshift" layout="small" class="flex items-center gap-2 self-center hover:text-m-red">Redshift <img src={wave} class="h-4" alt="redshift"></img></ButtonLink>
</Card>
</Suspense>
</VStack>
@@ -220,8 +233,8 @@ export function CombinedActivity(props: { limit?: number }) {
const getAllActivity = async () => {
console.log("Getting all activity");
const txs = await state.node_manager?.list_onchain() as OnChainTx[];
const invoices = await state.node_manager?.list_invoices() as MutinyInvoice[];
const txs = await state.mutiny_wallet?.list_onchain() as OnChainTx[];
const invoices = await state.mutiny_wallet?.list_invoices() as MutinyInvoice[];
const activity: ActivityItem[] = [];

View File

@@ -1,5 +1,5 @@
import logo from '~/assets/icons/mutiny-logo.svg';
import { Card, DefaultMain, NodeManagerGuard, SafeArea, VStack } from "~/components/layout";
import { DefaultMain, MutinyWalletGuard, SafeArea, VStack, Card } from "~/components/layout";
import BalanceBox from "~/components/BalanceBox";
import NavBar from "~/components/NavBar";
import ReloadPrompt from "~/components/Reload";
@@ -10,7 +10,7 @@ import userClock from '~/assets/icons/user-clock.svg';
export default function App() {
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<header class="w-full flex justify-between items-center mt-4 mb-2">
@@ -30,6 +30,6 @@ export default function App() {
</DefaultMain>
<NavBar activeTab="home" />
</SafeArea>
</NodeManagerGuard>
</MutinyWalletGuard>
);
}

View File

@@ -2,6 +2,8 @@ import { createSignal } from "solid-js";
import { ConfirmDialog } from "~/components/Dialog";
import { Button } from "~/components/layout";
import { showToast } from "~/components/Toaster";
import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify";
export function deleteDb(name: string) {
const req = indexedDB.deleteDatabase(name);
@@ -20,18 +22,7 @@ export function deleteDb(name: string) {
}
export function DeleteEverything() {
async function resetNode() {
setConfirmLoading(true);
deleteDb("gossip")
deleteDb("wallet")
localStorage.clear();
showToast({ title: "Deleted", description: `Deleted all data` })
setConfirmOpen(false);
setConfirmLoading(false);
setTimeout(() => {
window.location.href = "/";
}, 3000);
}
const [_state, actions] = useMegaStore();
async function confirmReset() {
setConfirmOpen(true);
@@ -40,6 +31,25 @@ export function DeleteEverything() {
const [confirmOpen, setConfirmOpen] = createSignal(false);
const [confirmLoading, setConfirmLoading] = createSignal(false);
async function resetNode() {
try {
setConfirmLoading(true);
await actions.deleteMutinyWallet();
showToast({ title: "Deleted", description: `Deleted all data` })
setTimeout(() => {
window.location.href = "/";
}, 1000);
} catch (e) {
console.error(e)
showToast(eify(e))
} finally {
setConfirmOpen(false);
setConfirmLoading(false);
}
}
return (
<>
<Button onClick={confirmReset}>Delete Everything</Button>
@@ -48,4 +58,4 @@ export function DeleteEverything() {
</ConfirmDialog>
</>
)
}
}

View File

@@ -1,5 +1,5 @@
import { Title } from "solid-start";
import { ButtonLink, DefaultMain, LargeHeader, SafeArea, SmallHeader } from "~/components/layout";
import { Button, ButtonLink, DefaultMain, LargeHeader, SafeArea, SmallHeader } from "~/components/layout";
export default function ErrorDisplay(props: { error: Error }) {
return (
@@ -13,7 +13,7 @@ export default function ErrorDisplay(props: { error: Error }) {
{props.error.name}</span>: {props.error.message}
</p>
<div class="h-full" />
<ButtonLink href="/" intent="red">Dangit</ButtonLink>
<Button onClick={() => window.location.href = "/"} intent="red">Dangit</Button>
</DefaultMain>
</SafeArea>
);

View File

@@ -6,13 +6,13 @@ import { showToast } from "./Toaster";
import { downloadTextFile } from "~/utils/download";
import { createFileUploader } from "@solid-primitives/upload"
import { ConfirmDialog } from "./Dialog";
import { NodeManager } from "@mutinywallet/mutiny-wasm";
import { MutinyWallet } from "@mutinywallet/mutiny-wasm";
export function ImportExport() {
const [state, _] = useMegaStore()
async function handleSave() {
const json = await state.node_manager?.export_json()
const json = await state.mutiny_wallet?.export_json()
downloadTextFile(json || "", "mutiny-state.json")
}
@@ -32,7 +32,7 @@ export function ImportExport() {
// This should throw if there's a parse error, so we won't end up clearing
JSON.parse(text);
NodeManager.import_json(text);
MutinyWallet.import_json(text);
window.location.href = "/"

View File

@@ -17,13 +17,13 @@ function PeerItem(props: { peer: MutinyPeer }) {
const [state, _] = useMegaStore()
const handleDisconnectPeer = async () => {
const nodes = await state.node_manager?.list_nodes();
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = nodes[0] as string || ""
if (props.peer.is_connected) {
await state.node_manager?.disconnect_peer(firstNode, props.peer.pubkey);
await state.mutiny_wallet?.disconnect_peer(firstNode, props.peer.pubkey);
} else {
await state.node_manager?.delete_peer(firstNode, props.peer.pubkey);
await state.mutiny_wallet?.delete_peer(firstNode, props.peer.pubkey);
}
};
@@ -50,7 +50,7 @@ function PeersList() {
const [state, _] = useMegaStore()
const getPeers = async () => {
return await state.node_manager?.list_peers() as Promise<MutinyPeer[]>
return await state.mutiny_wallet?.list_peers() as Promise<MutinyPeer[]>
};
const [peers, { refetch }] = createResource(getPeers);
@@ -95,10 +95,10 @@ function ConnectPeer(props: { refetchPeers: RefetchPeersType }) {
e.preventDefault();
const peerConnectString = value().trim();
const nodes = await state.node_manager?.list_nodes();
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = nodes[0] as string || ""
await state.node_manager?.connect_to_peer(firstNode, peerConnectString)
await state.mutiny_wallet?.connect_to_peer(firstNode, peerConnectString)
await props.refetchPeers()
@@ -140,7 +140,7 @@ function ChannelItem(props: { channel: MutinyChannel, network?: string }) {
async function confirmCloseChannel() {
setConfirmLoading(true);
try {
await state.node_manager?.close_channel(props.channel.outpoint as string)
await state.mutiny_wallet?.close_channel(props.channel.outpoint as string)
} catch (e) {
console.error(e);
showToast(eify(e));
@@ -179,7 +179,7 @@ function ChannelsList() {
const [state, _] = useMegaStore()
const getChannels = async () => {
return await state.node_manager?.list_channels() as Promise<MutinyChannel[]>
return await state.mutiny_wallet?.list_channels() as Promise<MutinyChannel[]>
};
const [channels, { refetch }] = createResource(getChannels);
@@ -194,7 +194,7 @@ function ChannelsList() {
});
})
const network = state.node_manager?.get_network();
const network = state.mutiny_wallet?.get_network();
return (
<>
@@ -236,10 +236,10 @@ function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) {
const pubkey = peerPubkey().trim();
const bigAmount = BigInt(amount());
const nodes = await state.node_manager?.list_nodes();
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = nodes[0] as string || ""
const new_channel = await state.node_manager?.open_channel(firstNode, pubkey, bigAmount)
const new_channel = await state.mutiny_wallet?.open_channel(firstNode, pubkey, bigAmount)
setNewChannel(new_channel)
@@ -303,7 +303,7 @@ function LnUrlAuth() {
e.preventDefault();
const lnurl = value().trim();
await state.node_manager?.lnurl_auth(0, lnurl)
await state.mutiny_wallet?.lnurl_auth(0, lnurl)
setValue("");
};

View File

@@ -2,11 +2,12 @@ import mutiny_m from '~/assets/icons/m.svg';
import airplane from '~/assets/icons/airplane.svg';
import settings from '~/assets/icons/settings.svg';
import receive from '~/assets/icons/big-receive.svg';
import redshift from '~/assets/icons/rs.svg';
import userClock from '~/assets/icons/user-clock.svg';
import { A } from "solid-start";
type ActiveTab = 'home' | 'scan' | 'send' | 'receive' | 'settings' | 'activity' | 'none';
type ActiveTab = 'home' | 'scan' | 'send' | 'receive' | 'settings' | 'redshift' | 'activity' | 'none';
export default function NavBar(props: { activeTab: ActiveTab }) {
const activeStyle = 'border-t-0 border-b-0 p-2 bg-black rounded-lg'
@@ -34,6 +35,11 @@ export default function NavBar(props: { activeTab: ActiveTab }) {
<img src={userClock} alt="activity" />
</A>
</li>
<li class={props.activeTab === "redshift" ? activeStyle : inactiveStyle}>
<A href="/redshift">
<img src={redshift} alt="redshift" width={36} />
</A>
</li>
<li class={props.activeTab === "settings" ? activeStyle : inactiveStyle}>
<A href="/settings">
<img src={settings} alt="settings" />

View File

@@ -3,7 +3,7 @@ import { As, Dialog } from "@kobalte/core";
import { Button, Card } from "~/components/layout";
import { useMegaStore } from "~/state/megaStore";
import { Show, createResource } from "solid-js";
import { getExistingSettings } from "~/logic/nodeManagerSetup";
import { getExistingSettings } from "~/logic/mutinyWalletSetup";
import getHostname from "~/utils/getHostname";
const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
@@ -15,9 +15,9 @@ export default function PeerConnectModal() {
const [state, _] = useMegaStore()
const getPeerConnectString = async () => {
if (state.node_manager) {
if (state.mutiny_wallet) {
const { proxy } = getExistingSettings();
const nodes = await state.node_manager.list_nodes();
const nodes = await state.mutiny_wallet.list_nodes();
const firstNode = nodes[0] as string || ""
const hostName = getHostname(proxy || "")
const connectString = `mutiny:${firstNode}@${hostName}`

View File

@@ -1,36 +1,22 @@
import { createForm, url } from '@modular-forms/solid';
import { TextField } from '~/components/layout/TextField';
import { NodeManagerSettingStrings, getExistingSettings } from '~/logic/nodeManagerSetup';
import { Button } from '~/components/layout';
import { createSignal } from 'solid-js';
import { deleteDb } from '~/components/DeleteEverything';
import { MutinyWalletSettingStrings, getExistingSettings } from '~/logic/mutinyWalletSetup';
import { Button, SmallHeader } from '~/components/layout';
import { showToast } from './Toaster';
import eify from '~/utils/eify';
import { ConfirmDialog } from "~/components/Dialog";
import { useMegaStore } from '~/state/megaStore';
export function SettingsStringsEditor() {
const existingSettings = getExistingSettings();
const [_settingsForm, { Form, Field }] = createForm<NodeManagerSettingStrings>({ initialValues: existingSettings });
const [confirmOpen, setConfirmOpen] = createSignal(false);
const [settingsTemp, setSettingsTemp] = createSignal<NodeManagerSettingStrings>();
const [_settingsForm, { Form, Field }] = createForm<MutinyWalletSettingStrings>({ initialValues: existingSettings });
const [_store, actions] = useMegaStore();
async function handleSubmit(values: NodeManagerSettingStrings) {
async function handleSubmit(values: MutinyWalletSettingStrings) {
try {
const existing = getExistingSettings();
const newSettings = { ...existing, ...values }
if (existing.network !== values.network) {
// If the network changes we need to confirm the wipe
// Save the settings so we can get them later
setSettingsTemp(newSettings);
setConfirmOpen(true);
} else {
await actions.setupNodeManager(newSettings);
window.location.reload();
}
await actions.setupMutinyWallet(newSettings);
window.location.reload();
} catch (e) {
console.error(e)
showToast(eify(e))
@@ -38,42 +24,15 @@ export function SettingsStringsEditor() {
console.log(values)
}
async function confirmStateReset() {
try {
deleteDb("gossip")
localStorage.clear();
showToast({ title: "Deleted", description: `Deleted all data` })
const loadedValues = settingsTemp();
await actions.setupNodeManager(loadedValues);
window.location.reload();
} catch (e) {
console.error(e)
showToast(eify(e))
}
setConfirmOpen(false);
}
return <Form onSubmit={handleSubmit} class="flex flex-col gap-4">
<ConfirmDialog loading={false} isOpen={confirmOpen()} onConfirm={confirmStateReset} onCancel={() => setConfirmOpen(false)}>
Are you sure? Changing networks will delete your node's state. This can't be undone!
</ConfirmDialog>
<h2 class="text-2xl font-light">Don't trust us! Use your own servers to back Mutiny.</h2>
<Field name="network">
{(field, props) => (
// TODO: make a cool select component
<div class="flex flex-col gap-2">
<label class="text-sm font-semibold uppercase">Network</label>
<select {...field} {...props} class="bg-black rounded-xl border border-white px-4 py-2">
<option value="bitcoin">Mainnet</option>
<option value="testnet">Testnet</option>
<option value="regtest">Regtest</option>
<option value="signet">Signet</option>
</select>
</div>
)}
</Field>
<div class="flex flex-col gap-2">
<SmallHeader>Network</SmallHeader>
<pre>
{existingSettings.network}
</pre>
</div>
<Field name="proxy" validate={[url("Should be a url starting with wss://")]}>
{(field, props) => (
<TextField {...props} value={field.value} error={field.error} label="Websockets Proxy" />

View File

@@ -71,11 +71,11 @@ export const FullscreenLoader = () => {
);
}
export const NodeManagerGuard: ParentComponent = (props) => {
export const MutinyWalletGuard: ParentComponent = (props) => {
const [state, _] = useMegaStore();
return (
<Suspense fallback={<FullscreenLoader />}>
<Show when={state.node_manager}>
<Show when={state.mutiny_wallet}>
{props.children}
</Show>
</Suspense>
@@ -133,4 +133,4 @@ export const Indicator: ParentComponent = (props) => {
return (
<div class="box-border animate-pulse px-2 py-1 -my-1 bg-white/70 rounded text-xs uppercase text-black">{props.children}</div>
)
}
}

View File

@@ -1,17 +1,17 @@
import initNodeManager, { NodeManager } from '@mutinywallet/mutiny-wasm';
import initMutinyWallet, { MutinyWallet } from '@mutinywallet/mutiny-wasm';
import initWaila from '@mutinywallet/waila-wasm'
// export type NodeManagerSettingStrings = {
// export type MutinyWalletSettingStrings = {
// network?: string, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
// }
type Network = "bitcoin" | "testnet" | "regtest" | "signet";
export type NodeManagerSettingStrings = {
export type MutinyWalletSettingStrings = {
network?: Network, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
}
export function getExistingSettings(): NodeManagerSettingStrings {
export function getExistingSettings(): MutinyWalletSettingStrings {
const network = localStorage.getItem('MUTINY_SETTINGS_network') || import.meta.env.VITE_NETWORK;
const proxy = localStorage.getItem('MUTINY_SETTINGS_proxy') || import.meta.env.VITE_PROXY;
const esplora = localStorage.getItem('MUTINY_SETTINGS_esplora') || import.meta.env.VITE_ESPLORA;
@@ -21,7 +21,7 @@ export function getExistingSettings(): NodeManagerSettingStrings {
return { network, proxy, esplora, rgs, lsp }
}
export async function setAndGetMutinySettings(settings?: NodeManagerSettingStrings): Promise<NodeManagerSettingStrings> {
export async function setAndGetMutinySettings(settings?: MutinyWalletSettingStrings): Promise<MutinyWalletSettingStrings> {
let { network, proxy, esplora, rgs, lsp } = settings || {};
const existingSettings = getExistingSettings();
@@ -70,29 +70,29 @@ export async function checkForWasm() {
}
}
export async function setupNodeManager(settings?: NodeManagerSettingStrings): Promise<NodeManager> {
await initNodeManager();
export async function setupMutinyWallet(settings?: MutinyWalletSettingStrings): Promise<MutinyWallet> {
await initMutinyWallet();
// Might as well init waila while we're at it
await initWaila();
console.time("Setup");
console.log("Starting setup...")
const { network, proxy, esplora, rgs, lsp } = await setAndGetMutinySettings(settings)
console.log("Initializing Node Manager")
console.log("Initializing Mutiny Manager")
console.log("Using network", network);
console.log("Using proxy", proxy);
console.log("Using esplora address", esplora);
console.log("Using rgs address", rgs);
console.log("Using lsp address", lsp);
const nodeManager = await new NodeManager("", undefined, proxy, network, esplora, rgs, lsp)
const mutinyWallet = await new MutinyWallet("", undefined, proxy, network, esplora, rgs, lsp)
const nodes = await nodeManager.list_nodes();
const nodes = await mutinyWallet.list_nodes();
// If we don't have any nodes yet, create one
if (!nodes.length) {
await nodeManager?.new_node()
await mutinyWallet?.new_node()
}
return nodeManager
return mutinyWallet
}

View File

@@ -1,6 +1,6 @@
import { For, Show, createResource } from "solid-js";
import NavBar from "~/components/NavBar";
import { Button, Card, DefaultMain, LargeHeader, NiceP, NodeManagerGuard, SafeArea, VStack } from "~/components/layout";
import { Button, Card, DefaultMain, LargeHeader, NiceP, MutinyWalletGuard, SafeArea, VStack } from "~/components/layout";
import { BackLink } from "~/components/layout/BackLink";
import { CombinedActivity } from "~/components/Activity";
import { A } from "solid-start";
@@ -43,7 +43,7 @@ const TAB = "flex-1 inline-block px-8 py-4 text-lg font-semibold rounded-lg ui-s
export default function Activity() {
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink />
@@ -73,6 +73,6 @@ export default function Activity() {
</DefaultMain>
<NavBar activeTab="activity" />
</SafeArea>
</NodeManagerGuard>
</MutinyWalletGuard>
)
}

View File

@@ -1,12 +1,12 @@
import { DeleteEverything } from "~/components/DeleteEverything";
import KitchenSink from "~/components/KitchenSink";
import NavBar from "~/components/NavBar";
import { Card, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader, VStack } from "~/components/layout";
import { Card, DefaultMain, LargeHeader, MutinyWalletGuard, SafeArea, SmallHeader, VStack } from "~/components/layout";
import { BackLink } from "~/components/layout/BackLink";
export default function Admin() {
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink href="/settings" title="Settings" />
@@ -22,6 +22,6 @@ export default function Admin() {
</DefaultMain>
<NavBar activeTab="none" />
</SafeArea>
</NodeManagerGuard>
</MutinyWalletGuard>
)
}

View File

@@ -1,4 +1,4 @@
import { Button, DefaultMain, LargeHeader, NiceP, NodeManagerGuard, SafeArea, VStack } from "~/components/layout";
import { Button, DefaultMain, LargeHeader, NiceP, MutinyWalletGuard, SafeArea, VStack } from "~/components/layout";
import NavBar from "~/components/NavBar";
import { useNavigate } from 'solid-start';
import { SeedWords } from '~/components/SeedWords';
@@ -18,7 +18,7 @@ export default function App() {
}
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink />
@@ -30,7 +30,7 @@ export default function App() {
If you clear your browser history, or lose your device, these 12 words are the only way you can restore your wallet.
</NiceP>
<NiceP>Mutiny is self-custodial. It's all up to you...</NiceP>
<SeedWords words={store.node_manager?.show_seed() || ""} setHasSeen={setHasSeenBackup} />
<SeedWords words={store.mutiny_wallet?.show_seed() || ""} setHasSeen={setHasSeenBackup} />
<Show when={hasSeenBackup()}>
<NiceP>You are responsible for your funds!</NiceP>
</Show>
@@ -39,6 +39,6 @@ export default function App() {
</DefaultMain>
<NavBar activeTab="none" />
</SafeArea>
</NodeManagerGuard>
</MutinyWalletGuard>
);
}

View File

@@ -1,7 +1,7 @@
import { MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { createEffect, createMemo, createResource, createSignal, Match, onCleanup, onMount, Show, Switch } from "solid-js";
import { QRCodeSVG } from "solid-qr-code";
import { Button, Card, Indicator, LargeHeader, NodeManagerGuard, SafeArea } from "~/components/layout";
import { Button, Card, Indicator, LargeHeader, MutinyWalletGuard, SafeArea } from "~/components/layout";
import NavBar from "~/components/NavBar";
import { useMegaStore } from "~/state/megaStore";
import { objectToSearchParams } from "~/utils/objectToSearchParams";
@@ -104,13 +104,13 @@ export default function Receive() {
async function getUnifiedQr(amount: string) {
const bigAmount = BigInt(amount);
try {
const raw = await state.node_manager?.create_bip21(bigAmount);
// FIXME: actual labels
const raw = await state.mutiny_wallet?.create_bip21(bigAmount, []);
// Save the raw info so we can watch the address and invoice
setBip21Raw(raw);
const params = objectToSearchParams({
amount: raw?.btc_amount,
label: raw?.description,
lightning: raw?.invoice
})
@@ -138,7 +138,7 @@ export default function Receive() {
const lightning = bip21.invoice
const address = bip21.address
const invoice = await state.node_manager?.get_invoice(lightning)
const invoice = await state.mutiny_wallet?.get_invoice(lightning)
if (invoice && invoice.paid) {
setReceiveState("paid")
@@ -146,7 +146,7 @@ export default function Receive() {
return "lightning_paid"
}
const tx = await state.node_manager?.check_address(address) as OnChainTx | undefined;
const tx = await state.mutiny_wallet?.check_address(address) as OnChainTx | undefined;
if (tx) {
setReceiveState("paid")
@@ -168,7 +168,7 @@ export default function Receive() {
});
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<main class="max-w-[600px] flex flex-col flex-1 gap-4 mx-auto p-4 overflow-y-auto">
<Show when={receiveState() === "show"} fallback={<BackLink />}>
@@ -229,6 +229,6 @@ export default function Receive() {
</main>
<NavBar activeTab="receive" />
</SafeArea >
</NodeManagerGuard>
</MutinyWalletGuard>
)
}

409
src/routes/Redshift.tsx Normal file
View File

@@ -0,0 +1,409 @@
import { Component, createEffect, createMemo, createResource, createSignal, For, Match, onCleanup, onMount, ParentComponent, Show, Suspense, Switch } from "solid-js";
import { CENTER_COLUMN, MISSING_LABEL, REDSHIFT_LABEL, RIGHT_COLUMN, THREE_COLUMNS, UtxoItem } from "~/components/Activity";
import { Card, DefaultMain, LargeHeader, LoadingSpinner, NiceP, MutinyWalletGuard, SafeArea, SmallAmount, SmallHeader, VStack } from "~/components/layout";
import { BackLink } from "~/components/layout/BackLink";
import { StyledRadioGroup } from "~/components/layout/Radio";
import NavBar from "~/components/NavBar";
import { useMegaStore } from "~/state/megaStore";
import wave from "~/assets/wave.gif"
import utxoIcon from '~/assets/icons/coin.svg';
import { Button } from "~/components/layout/Button";
import { ProgressBar } from "~/components/layout/ProgressBar";
import { MutinyChannel } from "@mutinywallet/mutiny-wasm";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { Amount } from "~/components/Amount";
import { getRedshifted, setRedshifted } from "~/utils/fakeLabels";
type ShiftOption = "utxo" | "lightning"
type ShiftStage = "choose" | "observe" | "success" | "failure"
type OutPoint = string; // Replace with the actual TypeScript type for OutPoint
type RedshiftStatus = string; // Replace with the actual TypeScript type for RedshiftStatus
type RedshiftRecipient = any; // Replace with the actual TypeScript type for RedshiftRecipient
type PublicKey = any; // Replace with the actual TypeScript type for PublicKey
interface RedshiftResult {
id: string;
input_utxo: OutPoint;
status: RedshiftStatus;
recipient: RedshiftRecipient;
output_utxo?: OutPoint;
introduction_channel?: OutPoint;
output_channel?: OutPoint;
introduction_node: PublicKey;
amount_sats: bigint;
change_amt?: bigint;
fees_paid: bigint;
}
const dummyRedshift: RedshiftResult = {
id: "44036599c37d590899e8d5d920860286",
input_utxo: "44036599c37d590899e8d5d92086028695d2c2966fdc354ce1da9a9eac610a53:1",
status: "Completed", // Replace with a dummy value for RedshiftStatus
recipient: {}, // Replace with a dummy value for RedshiftRecipient
output_utxo: "44036599c37d590899e8d5d92086028695d2c2966fdc354ce1da9a9eac610a53:1",
introduction_channel: "a7773e57f8595848a635e9af105927cac9ecaf292d71a76456ae0455bd3c9c64:0",
output_channel: "a7773e57f8595848a635e9af105927cac9ecaf292d71a76456ae0455bd3c9c64:0",
introduction_node: {}, // Replace with a dummy value for PublicKey
amount_sats: BigInt(1000000),
change_amt: BigInt(12345),
fees_paid: BigInt(2500),
};
function RedshiftReport(props: { redshift: RedshiftResult, utxo: UtxoItem }) {
const [state, _actions] = useMegaStore();
const getUtXos = async () => {
console.log("Getting utxos");
return await state.mutiny_wallet?.list_utxos() as UtxoItem[];
}
function findUtxoByOutpoint(outpoint?: string, utxos: UtxoItem[] = []): UtxoItem | undefined {
if (!outpoint) return undefined;
return utxos.find((utxo) => utxo.outpoint === outpoint);
}
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
const inputUtxo = createMemo(() => {
console.log(utxos())
const foundUtxo = findUtxoByOutpoint(props.redshift.input_utxo, utxos())
console.log("Found utxo:", foundUtxo)
return foundUtxo
})
async function checkRedshift(id: string) {
// const rs = redshiftItems[0] as RedshiftResult;
console.log("Checking redshift", id)
const redshift = await state.mutiny_wallet?.get_redshift(id);
console.log(redshift)
return redshift;
}
const [redshiftResource, { refetch }] = createResource(props.redshift.id, checkRedshift);
onMount(() => {
const interval = setInterval(() => {
if (redshiftResource()) refetch();
// if (sentAmount() === 200000) {
// clearInterval(interval)
// props.setShiftStage("success");
// // setSentAmount((0))
// } else {
// setSentAmount((sentAmount() + 50000))
// }
}, 1000)
})
const outputUtxo = createMemo(() => {
return findUtxoByOutpoint(redshiftResource()?.output_utxo, utxos())
})
createEffect(() => {
setRedshifted(true, redshiftResource()?.output_utxo)
})
return (
<VStack biggap>
{/* <VStack>
<NiceP>We did it. Here's your new UTXO:</NiceP>
<Show when={utxos() && outputUtxo()}>
<Card>
<Utxo item={outputUtxo()!} />
</Card>
</Show>
</VStack> */}
<VStack>
<NiceP>What happened?</NiceP>
<Show when={redshiftResource()}>
<Card>
<VStack biggap>
{/* <KV key="Input utxo">
<Show when={utxos() && inputUtxo()}>
<Utxo item={inputUtxo()!} />
</Show>
</KV> */}
<KV key="Starting amount">
<Amount amountSats={redshiftResource().amount_sats} />
</KV>
<KV key="Fees paid">
<Amount amountSats={redshiftResource().fees_paid} />
</KV>
<KV key="Change">
<Amount amountSats={redshiftResource().change_amt} />
</KV>
<KV key="Outbound channel">
<VStack>
<pre class="whitespace-pre-wrap break-all">{redshiftResource().introduction_channel}</pre>
<a class="" href={mempoolTxUrl(redshiftResource().introduction_channel?.split(":")[0], "signet")} target="_blank" rel="noreferrer">
View on mempool
</a>
</VStack>
</KV>
<Show when={redshiftResource().output_channel}>
<KV key="Return channel">
<VStack>
<pre class="whitespace-pre-wrap break-all">{redshiftResource().output_channel}</pre>
<a class="" href={mempoolTxUrl(redshiftResource().output_channel?.split(":")[0], "signet")} target="_blank" rel="noreferrer">
View on mempool
</a>
</VStack>
</KV>
</Show>
</VStack>
</Card>
</Show>
<SmallHeader></SmallHeader>
</VStack>
</VStack>
)
}
const SHIFT_OPTIONS = [{ value: "utxo", label: "UTXO", caption: "Trade your UTXO for a fresh UTXO" }, { value: "lightning", label: "Lightning", caption: "Convert your UTXO into Lightning" }]
export function Utxo(props: { item: UtxoItem, onClick?: () => void }) {
const redshifted = createMemo(() => getRedshifted(props.item.outpoint));
return (
<>
<div class={THREE_COLUMNS} onClick={props.onClick}>
<div class="flex items-center">
<img src={utxoIcon} alt="coin" />
</div>
<div class={CENTER_COLUMN}>
<div class="flex gap-2">
<Show when={redshifted()} fallback={<h2 class={MISSING_LABEL}>Unknown</h2>}>
<h2 class={REDSHIFT_LABEL}>Redshift</h2>
</Show>
</div>
<SmallAmount amount={props.item.txout.value} />
</div>
<div class={RIGHT_COLUMN}>
<SmallHeader class={props.item?.is_spent ? "text-m-red" : "text-m-green"}>
{/* {props.item?.is_spent ? "SPENT" : "UNSPENT"} */}
</SmallHeader>
</div>
</div>
</>
)
}
const FAKE_STATES = ["Creating a new node", "Opening a channel", "Sending funds through", "Closing the channel", "Redshift complete"]
function ShiftObserver(props: { setShiftStage: (stage: ShiftStage) => void, redshiftId: String }) {
const [state, _actions] = useMegaStore();
const [fakeStage, setFakeStage] = createSignal(2);
const [sentAmount, setSentAmount] = createSignal(0);
onMount(() => {
const interval = setInterval(() => {
if (sentAmount() === 200000) {
clearInterval(interval)
props.setShiftStage("success");
// setSentAmount((0))
} else {
setSentAmount((sentAmount() + 50000))
}
}, 1000)
})
async function checkRedshift(id: string) {
console.log("Checking redshift", id)
const redshift = await state.mutiny_wallet?.get_redshift(id);
console.log(redshift)
return redshift
}
const [redshiftResource, { refetch }] = createResource(props.redshiftId, checkRedshift);
// onMount(() => {
// const interval = setInterval(() => {
// if (redshiftResource()) refetch();
// // if (sentAmount() === 200000) {
// // clearInterval(interval)
// // props.setShiftStage("success");
// // // setSentAmount((0))
// // } else {
// // setSentAmount((sentAmount() + 50000))
// // }
// }, 1000)
// })
// createEffect(() => {
// const interval = setInterval(() => {
// if (chosenUtxo()) refetch();
// }, 1000); // Poll every second
// onCleanup(() => {
// clearInterval(interval);
// });
// });
return (
<>
<NiceP>Watch it go!</NiceP>
<Card>
<VStack>
<pre class="self-center">{FAKE_STATES[fakeStage()]}</pre>
<ProgressBar value={sentAmount()} max={200000} />
<img src={wave} class="h-4 self-center" alt="sine wave" />
</VStack>
</Card>
</>
)
}
const KV: ParentComponent<{ key: string }> = (props) => {
return (
<div class="flex flex-col gap-2">
<p class="text-sm font-semibold uppercase">{props.key}</p>
{props.children}
</div>
)
}
export default function Redshift() {
const [state, _actions] = useMegaStore();
const [shiftStage, setShiftStage] = createSignal<ShiftStage>("choose");
const [shiftType, setShiftType] = createSignal<ShiftOption>("utxo");
const [chosenUtxo, setChosenUtxo] = createSignal<UtxoItem>();
const getUtXos = async () => {
console.log("Getting utxos");
return await state.mutiny_wallet?.list_utxos() as UtxoItem[];
}
const getChannels = async () => {
console.log("Getting channels");
await state.mutiny_wallet?.sync()
const channels = await state.mutiny_wallet?.list_channels() as Promise<MutinyChannel[]>;
console.log(channels)
return channels
}
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
const [channels, { refetch: _refetchChannels }] = createResource(getChannels);
const redshiftedUtxos = createMemo(() => {
return utxos()?.filter((utxo) => getRedshifted(utxo.outpoint))
})
const unredshiftedUtxos = createMemo(() => {
return utxos()?.filter((utxo) => !getRedshifted(utxo.outpoint))
})
function resetState() {
setShiftStage("choose");
setShiftType("utxo");
setChosenUtxo(undefined);
}
async function redshiftUtxo(utxo: UtxoItem) {
console.log("Redshifting utxo", utxo.outpoint)
const redshift = await state.mutiny_wallet?.init_redshift(utxo.outpoint);
console.log("Redshift initialized:")
console.log(redshift)
return redshift
}
const [initializedRedshift, { refetch: _refetchRedshift }] = createResource(chosenUtxo, redshiftUtxo);
createEffect(() => {
if (chosenUtxo() && initializedRedshift()) {
// window.location.href = "/"
setShiftStage("observe");
}
})
return (
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink />
<LargeHeader>Redshift</LargeHeader>
<VStack biggap>
{/* <pre>{JSON.stringify(redshiftResource(), null, 2)}</pre> */}
<Switch>
<Match when={shiftStage() === "choose"}>
<VStack>
<NiceP>Where is this going?</NiceP>
<StyledRadioGroup red value={shiftType()} onValueChange={(newValue) => setShiftType(newValue as ShiftOption)} choices={SHIFT_OPTIONS} />
</VStack>
<VStack>
<NiceP>Choose your <span class="inline-block"><img class="h-4" src={wave} alt="sine wave" /></span> UTXO to begin</NiceP>
<Suspense>
<Card title="Unshifted UTXOs">
<Switch>
<Match when={utxos.loading}>
<LoadingSpinner wide />
</Match>
<Match when={utxos.state === "ready" && unredshiftedUtxos()?.length === 0}>
<code>No utxos (empty state)</code>
</Match>
<Match when={utxos.state === "ready" && unredshiftedUtxos() && unredshiftedUtxos()!.length >= 0}>
<For each={unredshiftedUtxos()}>
{(utxo) =>
<Utxo item={utxo} onClick={() => setChosenUtxo(utxo)} />
}
</For>
</Match>
</Switch>
</Card>
</Suspense>
<Suspense>
<Card titleElement={<SmallHeader><span class="text-m-red">Redshifted </span>UTXOs</SmallHeader>}>
<Switch>
<Match when={utxos.loading}>
<LoadingSpinner wide />
</Match>
<Match when={utxos.state === "ready" && redshiftedUtxos()?.length === 0}>
<code>No utxos (empty state)</code>
</Match>
<Match when={utxos.state === "ready" && redshiftedUtxos() && redshiftedUtxos()!.length >= 0}>
<For each={redshiftedUtxos()}>
{(utxo) =>
<Utxo item={utxo} />
}
</For>
</Match>
</Switch>
</Card>
</Suspense>
</VStack>
</Match>
<Match when={shiftStage() === "observe" && chosenUtxo()}>
<ShiftObserver setShiftStage={setShiftStage} utxo={chosenUtxo()!} />
</Match>
<Match when={shiftStage() === "success" && chosenUtxo()}>
<VStack biggap>
<RedshiftReport redshift={dummyRedshift} utxo={chosenUtxo()!} />
<Button intent="red" onClick={resetState}>Nice</Button>
</VStack>
</Match>
<Match when={shiftStage() === "failure"}>
<NiceP>Oh dear</NiceP>
<NiceP>Here's what happened:</NiceP>
<Button intent="red" onClick={resetState}>Dangit</Button>
</Match>
</Switch>
</VStack>
</DefaultMain>
<NavBar activeTab="redshift" />
</SafeArea>
</MutinyWalletGuard>
)
}

View File

@@ -83,7 +83,7 @@ export default function Scanner() {
// When we have a nice result we can head over to the send screen
createEffect(() => {
if (scanResult()) {
const network = state.node_manager?.get_network() || "signet";
const network = state.mutiny_wallet?.get_network() || "signet";
const result = toParsedParams(scanResult() || "", network);
if (!result.ok) {
showToast(result.error);

View File

@@ -1,7 +1,7 @@
import { Match, Show, Switch, createEffect, createMemo, createSignal, onMount } from "solid-js";
import { Amount } from "~/components/Amount";
import NavBar from "~/components/NavBar";
import { Button, ButtonLink, Card, DefaultMain, HStack, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader, VStack } from "~/components/layout";
import { Button, ButtonLink, Card, DefaultMain, HStack, LargeHeader, MutinyWalletGuard, SafeArea, SmallHeader, VStack } from "~/components/layout";
import { Paste } from "~/assets/svg/Paste";
import { Scan } from "~/assets/svg/Scan";
import { useMegaStore } from "~/state/megaStore";
@@ -14,8 +14,8 @@ import { FullscreenModal } from "~/components/layout/FullscreenModal";
import megacheck from "~/assets/icons/megacheck.png"
import megaex from "~/assets/icons/megaex.png";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { useNavigate } from "solid-start";
import { BackLink } from "~/components/layout/BackLink";
import { useNavigate } from "solid-start";
import { TagEditor } from "~/components/TagEditor";
import { TagItem, createUniqueId, listTags } from "~/state/contacts";
import { StringShower } from "~/components/ShareCard";
@@ -170,7 +170,7 @@ export default function Send() {
if (source.memo) setDescription(source.memo);
if (source.invoice) {
state.node_manager?.decode_invoice(source.invoice).then(invoice => {
state.mutiny_wallet?.decode_invoice(source.invoice).then(invoice => {
if (invoice?.amount_sats) setAmountSats(invoice.amount_sats);
setInvoice(invoice)
setSource("lightning")
@@ -193,7 +193,7 @@ export default function Send() {
function parsePaste(text: string) {
if (text) {
const network = state.node_manager?.get_network() || "signet";
const network = state.mutiny_wallet?.get_network() || "signet";
const result = toParsedParams(text || "", network);
if (!result.ok) {
showToast(result.error);
@@ -230,21 +230,21 @@ export default function Send() {
const bolt11 = invoice()?.bolt11;
const sentDetails: Partial<SentDetails> = {};
if (source() === "lightning" && invoice() && bolt11) {
const nodes = await state.node_manager?.list_nodes();
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = nodes[0] as string || ""
sentDetails.destination = bolt11;
// If the invoice has sats use that, otherwise we pass the user-defined amount
if (invoice()?.amount_sats) {
await state.node_manager?.pay_invoice(firstNode, bolt11);
await state.mutiny_wallet?.pay_invoice(firstNode, bolt11);
sentDetails.amount = invoice()?.amount_sats;
} else {
await state.node_manager?.pay_invoice(firstNode, bolt11, amountSats());
await state.mutiny_wallet?.pay_invoice(firstNode, bolt11, amountSats());
sentDetails.amount = amountSats();
}
} else if (source() === "lightning" && nodePubkey()) {
const nodes = await state.node_manager?.list_nodes();
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = nodes[0] as string || ""
const payment = await state.node_manager?.keysend(firstNode, nodePubkey()!, amountSats());
const payment = await state.mutiny_wallet?.keysend(firstNode, nodePubkey()!, amountSats());
console.log(payment?.value)
// TODO: handle timeouts
@@ -254,8 +254,9 @@ export default function Send() {
sentDetails.amount = amountSats();
}
} else if (source() === "onchain" && address()) {
// FIXME: actual labels
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const txid = await state.node_manager?.send_to_address(address()!, amountSats());
const txid = await state.mutiny_wallet?.send_to_address(address()!, amountSats(), []);
sentDetails.amount = amountSats();
sentDetails.destination = address();
// TODO: figure out if this is necessary, it takes forever
@@ -280,7 +281,7 @@ export default function Send() {
})
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink />
@@ -302,7 +303,7 @@ export default function Send() {
<img src={megacheck} alt="success" class="w-1/2 mx-auto max-w-[50vh]" />
<Amount amountSats={sentDetails()?.amount} showFiat />
<Show when={sentDetails()?.txid}>
<a href={mempoolTxUrl(sentDetails()?.txid, state.node_manager?.get_network())} target="_blank" rel="noreferrer">
<a href={mempoolTxUrl(sentDetails()?.txid, state.mutiny_wallet?.get_network())} target="_blank" rel="noreferrer">
Mempool Link
</a>
</Show>
@@ -335,6 +336,6 @@ export default function Send() {
</DefaultMain>
<NavBar activeTab="send" />
</SafeArea >
</NodeManagerGuard >
</MutinyWalletGuard >
)
}

View File

@@ -1,4 +1,4 @@
import { ButtonLink, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, VStack } from "~/components/layout";
import { ButtonLink, DefaultMain, LargeHeader, MutinyWalletGuard, SafeArea, VStack } from "~/components/layout";
import { BackLink } from "~/components/layout/BackLink";
import NavBar from "~/components/NavBar";
import { SeedWords } from "~/components/SeedWords";
@@ -9,7 +9,7 @@ export default function Settings() {
const [store, _actions] = useMegaStore();
return (
<NodeManagerGuard>
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink />
@@ -17,7 +17,7 @@ export default function Settings() {
<VStack biggap>
<VStack>
<p class="text-2xl font-light">Write down these words or you'll die!</p>
<SeedWords words={store.node_manager?.show_seed() || ""} />
<SeedWords words={store.mutiny_wallet?.show_seed() || ""} />
</VStack>
<SettingsStringsEditor />
<ButtonLink href="/admin">"I know what I'm doing"</ButtonLink>
@@ -25,6 +25,6 @@ export default function Settings() {
</DefaultMain>
<NavBar activeTab="settings" />
</SafeArea>
</NodeManagerGuard>
</MutinyWalletGuard>
)
}

View File

@@ -3,8 +3,8 @@
// Inspired by https://github.com/solidjs/solid-realworld/blob/main/src/store/index.js
import { ParentComponent, createContext, createEffect, onCleanup, onMount, useContext } from "solid-js";
import { createStore } from "solid-js/store";
import { NodeManagerSettingStrings, setupNodeManager } from "~/logic/nodeManagerSetup";
import { MutinyBalance, NodeManager } from "@mutinywallet/mutiny-wasm";
import { MutinyWalletSettingStrings, setupMutinyWallet } from "~/logic/mutinyWalletSetup";
import { MutinyBalance, MutinyWallet } from "@mutinywallet/mutiny-wasm";
import { ParsedParams } from "~/routes/Scanner";
const MegaStoreContext = createContext<MegaStore>();
@@ -13,7 +13,8 @@ type UserStatus = undefined | "new_here" | "waitlisted" | "approved" | "paid"
export type MegaStore = [{
waitlist_id?: string;
node_manager?: NodeManager;
mutiny_wallet?: MutinyWallet;
deleting: boolean;
user_status: UserStatus;
scan_result?: ParsedParams;
balance?: MutinyBalance;
@@ -24,7 +25,8 @@ export type MegaStore = [{
dismissed_restore_prompt: boolean
}, {
fetchUserStatus(): Promise<UserStatus>;
setupNodeManager(settings?: NodeManagerSettingStrings): Promise<void>;
setupMutinyWallet(settings?: MutinyWalletSettingStrings): Promise<void>;
deleteMutinyWallet(): Promise<void>;
setWaitlistId(waitlist_id: string): void;
setScanResult(scan_result: ParsedParams | undefined): void;
sync(): Promise<void>;
@@ -35,7 +37,8 @@ export type MegaStore = [{
export const Provider: ParentComponent = (props) => {
const [state, setState] = createStore({
waitlist_id: localStorage.getItem("waitlist_id"),
node_manager: undefined as NodeManager | undefined,
mutiny_wallet: undefined as MutinyWallet | undefined,
deleting: false,
user_status: undefined as UserStatus,
scan_result: undefined as ParsedParams | undefined,
// TODO: wire this up to real price once we have caching
@@ -66,24 +69,34 @@ export const Provider: ParentComponent = (props) => {
return "new_here"
}
},
async setupNodeManager(settings?: NodeManagerSettingStrings): Promise<void> {
async setupMutinyWallet(settings?: MutinyWalletSettingStrings): Promise<void> {
try {
const nodeManager = await setupNodeManager(settings)
setState({ node_manager: nodeManager })
const mutinyWallet = await setupMutinyWallet(settings)
setState({ mutiny_wallet: mutinyWallet })
} catch (e) {
console.error(e)
}
},
async deleteMutinyWallet(): Promise<void> {
await state.mutiny_wallet?.stop();
setState((prevState) => ({
...prevState,
mutiny_wallet: undefined,
deleting: true,
}));
MutinyWallet.import_json("{}");
localStorage.clear();
},
setWaitlistId(waitlist_id: string) {
setState({ waitlist_id })
},
async sync(): Promise<void> {
console.time("BDK Sync Time")
try {
if (state.node_manager && !state.is_syncing) {
if (state.mutiny_wallet && !state.is_syncing) {
setState({ is_syncing: true })
await state.node_manager?.sync()
const balance = await state.node_manager?.get_balance();
await state.mutiny_wallet?.sync()
const balance = await state.mutiny_wallet?.get_balance();
setState({ balance, last_sync: Date.now() })
}
} catch (e) {
@@ -115,9 +128,9 @@ export const Provider: ParentComponent = (props) => {
// Only load node manager when status is approved
createEffect(() => {
if (state.user_status === "approved" && !state.node_manager) {
if (state.user_status === "approved" && !state.mutiny_wallet && !state.deleting) {
console.log("running setup node manager...")
actions.setupNodeManager().then(() => console.log("node manager setup done"))
actions.setupMutinyWallet().then(() => console.log("node manager setup done"))
}
})
@@ -152,4 +165,4 @@ export function useMegaStore() {
throw new Error("useMegaStore: cannot find a MegaStoreContext")
}
return context;
}
}

View File

@@ -1,11 +1,11 @@
import { NodeManager } from "@mutinywallet/mutiny-wasm";
import { MutinyWallet } from "@mutinywallet/mutiny-wasm";
export function satsToUsd(amount: number | undefined, price: number, formatted: boolean): string {
if (typeof amount !== "number" || isNaN(amount)) {
return ""
}
try {
const btc = NodeManager.convert_sats_to_btc(BigInt(Math.floor(amount)));
const btc = MutinyWallet.convert_sats_to_btc(BigInt(Math.floor(amount)));
const usd = btc * price;
if (formatted) {
@@ -26,7 +26,7 @@ export function usdToSats(amount: number | undefined, price: number, formatted:
}
try {
const btc = price / amount;
const sats = NodeManager.convert_btc_to_sats(btc);
const sats = MutinyWallet.convert_btc_to_sats(btc);
if (formatted) {
return parseInt(sats.toString()).toLocaleString();
} else {

19
src/utils/fakeLabels.ts Normal file
View File

@@ -0,0 +1,19 @@
// Simple storage for fake labels
// For each outpoint string, we can store a boolean whether it's redshifted or not
function setRedshifted(redshifted: boolean, outpoint?: string,) {
if (outpoint === undefined) return;
localStorage.setItem(outpoint, redshifted.toString())
}
function getRedshifted(outpoint: string): boolean {
const redshifted = localStorage.getItem(outpoint)
if (redshifted === null) {
return false
}
return redshifted === 'true'
}
const TEST_UTXO = "47651763fbd74488a478aad80e4205c3e34bbadcfc42b5cd9557ef12a15ab00c:1"
export { setRedshifted, getRedshifted, TEST_UTXO }

View File

@@ -52,7 +52,7 @@ export default defineConfig({
},
optimizeDeps: {
// Don't want vite to bundle these late during dev causing reload
include: ["qr-scanner", "nostr-tools", "class-variance-authority"],
include: ["qr-scanner", "nostr-tools", "class-variance-authority", "@kobalte/core", "@solid-primitives/upload"],
// This is necessary because otherwise `vite dev` can't find the wasm
exclude: ["@mutinywallet/mutiny-wasm", "@mutinywallet/waila-wasm"],
},