From 851af30c1cfeae6ee6af237b72dead9cfe6abf9e Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 17:14:40 +0800 Subject: [PATCH 01/10] feat: add OneKey desktop-channel support via webusb sdk --- package-lock.json | 608 +++++++++++++++++++++++--- package.json | 4 + src-tauri/src/main.rs | 14 + src/assets/hww/icons-modal/onekey.svg | 5 + src/assets/hww/icons/onekey.svg | 16 + src/helpers/devices.ts | 40 ++ src/main.tsx | 5 + src/services/hwiService.ts | 105 +++-- src/services/onekeyService.ts | 330 ++++++++++++++ vite.config.ts | 11 + 10 files changed, 1051 insertions(+), 87 deletions(-) create mode 100644 src/assets/hww/icons-modal/onekey.svg create mode 100644 src/assets/hww/icons/onekey.svg create mode 100644 src/services/onekeyService.ts diff --git a/package-lock.json b/package-lock.json index 97e735d..be88ebc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,12 @@ "version": "0.2.4", "hasInstallScript": true, "dependencies": { + "@onekeyfe/hd-common-connect-sdk": "^1.1.23", + "@onekeyfe/hd-shared": "^1.1.23", "@tanstack/react-query": "^5.52.1", "@tauri-apps/api": "^1", + "buffer": "^6.0.3", + "lodash": "^4.17.23", "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -930,6 +934,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -965,6 +982,112 @@ "node": ">= 8" } }, + "node_modules/@onekeyfe/hd-common-connect-sdk": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-common-connect-sdk/-/hd-common-connect-sdk-1.1.23.tgz", + "integrity": "sha512-4/r6DD2ljbh8CwQ4tD6Gi+5LVdyG4jri3RDqQuzLDcosAk/yCOMAIPJ1oZ3vGb6PLjdyLbwzJItrE48ODBMx9w==", + "license": "ISC", + "dependencies": { + "@onekeyfe/hd-core": "1.1.23", + "@onekeyfe/hd-shared": "1.1.23", + "@onekeyfe/hd-transport-emulator": "1.1.23", + "@onekeyfe/hd-transport-http": "1.1.23", + "@onekeyfe/hd-transport-lowlevel": "1.1.23", + "@onekeyfe/hd-transport-web-device": "1.1.23" + } + }, + "node_modules/@onekeyfe/hd-core": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-core/-/hd-core-1.1.23.tgz", + "integrity": "sha512-s4qxtL/jDbB2o/AQuxoYEudSymZjDX7uicT1efVeurgo+C3JcTVse7j/Rpc3p/LpxThADwHK9JvZFQQWq6MQdA==", + "license": "ISC", + "dependencies": { + "@onekeyfe/hd-shared": "1.1.23", + "@onekeyfe/hd-transport": "1.1.23", + "axios": "1.12.2", + "bignumber.js": "^9.0.2", + "bytebuffer": "^5.0.1", + "jszip": "^3.10.1", + "parse-uri": "^1.0.7", + "semver": "^7.3.7" + }, + "peerDependencies": { + "@noble/hashes": "^1.1.3" + } + }, + "node_modules/@onekeyfe/hd-core/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@onekeyfe/hd-shared": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-shared/-/hd-shared-1.1.23.tgz", + "integrity": "sha512-ieSICOuUj85glB2ia9PWCoHrt7Vk8dDYV0ruGe1TvQc+C/jT1xl7PwIzQs8fQF96py4Z568ABb2UTUaHFRadjA==", + "license": "ISC" + }, + "node_modules/@onekeyfe/hd-transport": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport/-/hd-transport-1.1.23.tgz", + "integrity": "sha512-HHeIRcBDzABok29OoNlxRRlFuSRfe1HfjPWQlTC+FI8hJZLtPYyg4DesYiNnPmpbSXXMxp8BAgjtPnSI1XKChw==", + "license": "ISC", + "dependencies": { + "bytebuffer": "^5.0.1", + "long": "^4.0.0", + "protobufjs": "^6.11.2" + } + }, + "node_modules/@onekeyfe/hd-transport-emulator": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-emulator/-/hd-transport-emulator-1.1.23.tgz", + "integrity": "sha512-8u0Mf5loXg2VMqnW0JwUhN9IhtaNiilLD9FLYgMkf3ZioxPZNyrKF/XFXV9RRn8E9ja/RW1aHBV2wFgO07YXtg==", + "license": "ISC", + "dependencies": { + "@onekeyfe/hd-shared": "1.1.23", + "@onekeyfe/hd-transport": "1.1.23", + "axios": "1.12.2", + "secure-json-parse": "^4.0.0" + } + }, + "node_modules/@onekeyfe/hd-transport-http": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-http/-/hd-transport-http-1.1.23.tgz", + "integrity": "sha512-s5dNhjwUOJKH6wdAGqEu94qK2bgvYviEg/FUpmSPFgbSjbVZxsEFW9Xw1cCpSApGIvON3kFAzwQEdIHr/xv/+A==", + "license": "ISC", + "dependencies": { + "@onekeyfe/hd-shared": "1.1.23", + "@onekeyfe/hd-transport": "1.1.23", + "axios": "1.12.2", + "secure-json-parse": "^4.0.0" + } + }, + "node_modules/@onekeyfe/hd-transport-lowlevel": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-lowlevel/-/hd-transport-lowlevel-1.1.23.tgz", + "integrity": "sha512-ju7nOjaHA0UcH3T/jLcURrrdbr1Vg4VUUan0z7vIITUUDCxjixR4vxGcd3YLq+xuyMvA+W4DxwkR4szV4e+tWA==", + "license": "MIT", + "dependencies": { + "@onekeyfe/hd-shared": "1.1.23", + "@onekeyfe/hd-transport": "1.1.23" + } + }, + "node_modules/@onekeyfe/hd-transport-web-device": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-web-device/-/hd-transport-web-device-1.1.23.tgz", + "integrity": "sha512-VMBTY5W7BkVhZIgWfm/XQwkv7KJuxYmdbsTlfojcEcT7JskRAB6jlm70mjFNSdshLQ6gBhYTe/9HuA9pcHP0ow==", + "license": "MIT", + "dependencies": { + "@onekeyfe/hd-shared": "1.1.23", + "@onekeyfe/hd-transport": "1.1.23" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -987,6 +1110,70 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@remix-run/router": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", @@ -1546,11 +1733,16 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.5.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", - "dev": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2058,7 +2250,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -2076,13 +2268,13 @@ } }, "node_modules/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", - "dev": true, + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -2092,6 +2284,35 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -2152,6 +2373,51 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bytebuffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", + "integrity": "sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ==", + "license": "Apache-2.0", + "dependencies": { + "long": "~3" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/bytebuffer/node_modules/long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.6" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -2171,6 +2437,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2213,7 +2492,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2233,6 +2512,12 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2365,7 +2650,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -2382,6 +2667,20 @@ "node": ">=6.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2461,13 +2760,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2476,7 +2772,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2507,10 +2802,10 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -2519,14 +2814,15 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3106,7 +3402,6 @@ "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, "funding": [ { "type": "individual", @@ -3148,13 +3443,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -3185,7 +3482,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3227,16 +3523,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3245,6 +3546,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -3411,12 +3725,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3462,10 +3776,10 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3477,7 +3791,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -3492,7 +3805,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -3500,6 +3812,26 @@ "node": ">= 0.4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3509,6 +3841,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3548,8 +3886,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.7", @@ -4037,6 +4374,18 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4059,6 +4408,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4074,12 +4432,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4100,6 +4470,15 @@ "yallist": "^3.0.2" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4126,7 +4505,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -4135,7 +4514,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -4473,6 +4852,12 @@ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4485,6 +4870,15 @@ "node": ">=6" } }, + "node_modules/parse-uri": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.16.tgz", + "integrity": "sha512-WMX9ygt2zzbtd3UlChi8S2Uj/dZa0N9QaotTkyRD7v06c50dor4qEWrM5ZvHiiaZYpXal4otRS9hynwwX0DVoA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4640,6 +5034,12 @@ "node": ">=6.0.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4651,11 +5051,36 @@ "react-is": "^16.13.1" } }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/punycode": { "version": "2.3.1", @@ -4762,6 +5187,27 @@ "react-dom": ">=16.8" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -4945,6 +5391,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -4976,6 +5428,22 @@ "loose-envify": "^1.1.0" } }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5017,6 +5485,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5077,6 +5551,15 @@ "node": ">=0.10.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -5552,8 +6035,7 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/update-browserslist-db": { "version": "1.1.3", @@ -5594,6 +6076,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", diff --git a/package.json b/package.json index e175ebc..dab17ea 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,12 @@ "tauri:format:check": "cd src-tauri && cargo fmt -- --check" }, "dependencies": { + "@onekeyfe/hd-common-connect-sdk": "^1.1.23", + "@onekeyfe/hd-shared": "^1.1.23", "@tanstack/react-query": "^5.52.1", "@tauri-apps/api": "^1", + "buffer": "^6.0.3", + "lodash": "^4.17.23", "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1354be7..a71d3e1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -104,6 +104,19 @@ fn emit_to_channel(state: State<'_, AppState>, event_data: Value) -> Result<(), .map_err(|e| e.to_string()) } +#[tauri::command] +fn emit_to_channel_with_network( + state: State<'_, AppState>, + event_data: Value, + network: String, +) -> Result<(), String> { + let state = state.try_lock().map_err(|e| e.to_string())?; + state + .channel + .emit("CHANNEL_MESSAGE", event_data, false, Some(&network)) + .map_err(|e| e.to_string()) +} + // ==================== HWI Commands ==================== #[tauri::command] @@ -535,6 +548,7 @@ fn main() { hwi_register_multisig, hwi_verify_address, emit_to_channel, + emit_to_channel_with_network, hwi_send_pin, hwi_prompt_pin, async_hwi_enumerate, diff --git a/src/assets/hww/icons-modal/onekey.svg b/src/assets/hww/icons-modal/onekey.svg new file mode 100644 index 0000000..f06e410 --- /dev/null +++ b/src/assets/hww/icons-modal/onekey.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/assets/hww/icons/onekey.svg b/src/assets/hww/icons/onekey.svg new file mode 100644 index 0000000..9069067 --- /dev/null +++ b/src/assets/hww/icons/onekey.svg @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/src/helpers/devices.ts b/src/helpers/devices.ts index 83bb241..bf0907c 100644 --- a/src/helpers/devices.ts +++ b/src/helpers/devices.ts @@ -1,11 +1,13 @@ import bitboxIcon from "../assets/hww/icons/bitbox.svg"; import trezorIcon from "../assets/hww/icons/trezor.svg"; import ledgerIcon from "../assets/hww/icons/ledger.svg"; +import onekeyIcon from "../assets/hww/icons/onekey.svg"; import coldcardIcon from "../assets/hww/icons/coldcard.svg"; import jadeIcon from "../assets/hww/icons/jade.svg"; import ledgerIconModal from "../assets/hww/icons-modal/ledger.svg"; import trezorIconModal from "../assets/hww/icons-modal/trezor.svg"; import bitboxIconModal from "../assets/hww/icons-modal/bitbox.svg"; +import onekeyIconModal from "../assets/hww/icons-modal/onekey.svg"; import coldcardIconModal from "../assets/hww/icons-modal/coldcard.svg"; import jadeIconModal from "../assets/hww/icons-modal/jade.svg"; @@ -27,6 +29,10 @@ const HWI_DEVICES = { icon: ledgerIcon, name: "Ledger", }, + onekey: { + icon: onekeyIcon, + name: "OneKey", + }, trezor: { icon: trezorIcon, name: "Trezor", @@ -101,6 +107,40 @@ const deviceContent: Record = { }, }, }, + onekey: { + icon: onekeyIconModal, + content: { + connect: { + text: "Your mobile app is trying to connect to your OneKey. Please connect your OneKey to your computer via USB, unlock it, and open the Bitcoin app on the device.", + list: [], + }, + shareXpubs: { + text: "Keep your OneKey connected to the computer until setup is completed.", + list: [], + }, + healthCheck: { + text: "Your Mobile app is trying to perform a health check. Keep your OneKey connected to the computer until the operation is completed.", + list: [ + "Health check ensures the device holds the keys registered in the mobile app", + ], + }, + signTx: { + text: "Please sign the transaction by approving it on your OneKey.", + list: [ + "Make sure to verify the address and amount shown on your OneKey screen.", + "Only approve the request if the OneKey screen matches the expected details in Keeper.", + ], + }, + registerMultisig: { + text: "Registration via OneKey desktop channel is currently not supported.", + list: [], + }, + verifyAddress: { + text: "Address verification via OneKey desktop channel is currently not supported.", + list: [], + }, + }, + }, trezor: { icon: trezorIconModal, content: { diff --git a/src/main.tsx b/src/main.tsx index 4bb9d6d..7b328ee 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,8 +3,13 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { RouterProvider } from "react-router-dom"; +import { Buffer } from "buffer"; import router from "./routes"; +if (!(globalThis as { Buffer?: typeof Buffer }).Buffer) { + (globalThis as { Buffer?: typeof Buffer }).Buffer = Buffer; +} + const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/src/services/hwiService.ts b/src/services/hwiService.ts index c094b92..de86dd9 100644 --- a/src/services/hwiService.ts +++ b/src/services/hwiService.ts @@ -1,5 +1,6 @@ import { invoke } from "@tauri-apps/api/tauri"; import { HWIDevice, HWIDeviceType } from "../helpers/devices"; +import onekeyService from "./onekeyService"; interface Result { Ok: T; @@ -15,11 +16,28 @@ const emptyTrezorDevice: HWIDevice = { fingerprint: null, }; +let currentDeviceType: string | null = null; + +const emitToChannel = async (eventData: unknown) => { + if (currentDeviceType === "onekey") { + await invoke("emit_to_channel_with_network", { + eventData, + network: onekeyService.getCurrentNetwork(), + }); + return; + } + await invoke("emit_to_channel", { eventData }); +}; + const hwiService = { fetchDevices: async ( deviceType: HWIDeviceType | null = null, network: string | null = null, ): Promise => { + if (deviceType === "onekey") { + return onekeyService.fetchDevices(network); + } + if (network === "mainnet") { network = "bitcoin"; } @@ -50,6 +68,13 @@ const hwiService = { deviceType: string, network: string, ): Promise => { + currentDeviceType = deviceType; + + if (deviceType === "onekey") { + await onekeyService.setHWIClient(fingerprint, network); + return; + } + if (network === "mainnet") { network = "bitcoin"; } @@ -57,13 +82,21 @@ const hwiService = { }, shareXpubs: async (account: number): Promise => { - const eventData = await invoke("hwi_get_xpubs", { account }); - await invoke("emit_to_channel", { eventData }); + const eventData = + currentDeviceType === "onekey" + ? await onekeyService.shareXpubs(account) + : await invoke("hwi_get_xpubs", { account }); + + await emitToChannel(eventData); }, performHealthCheck: async (account: number): Promise => { - const eventData = await invoke("hwi_healthcheck", { account }); - await invoke("emit_to_channel", { eventData }); + const eventData = + currentDeviceType === "onekey" + ? await onekeyService.performHealthCheck(account) + : await invoke("hwi_healthcheck", { account }); + + await emitToChannel(eventData); }, signTx: async ( @@ -72,13 +105,17 @@ const hwiService = { walletName: string | null, hmac: string | null, ): Promise => { - const eventData = await invoke("hwi_sign_tx", { - psbt, - policy, - walletName, - hmac, - }); - await invoke("emit_to_channel", { eventData }); + const eventData = + currentDeviceType === "onekey" + ? await onekeyService.signTx(psbt) + : await invoke("hwi_sign_tx", { + psbt, + policy, + walletName, + hmac, + }); + + await emitToChannel(eventData); }, registerMultisig: async ( @@ -87,13 +124,17 @@ const hwiService = { walletName: string | null, expectedAddress: string, ): Promise => { - const eventData = await invoke("hwi_register_multisig", { - descriptor, - policy, - walletName, - expectedAddress, - }); - await invoke("emit_to_channel", { eventData }); + const eventData = + currentDeviceType === "onekey" + ? await onekeyService.registerMultisig() + : await invoke("hwi_register_multisig", { + descriptor, + policy, + walletName, + expectedAddress, + }); + + await emitToChannel(eventData); }, verifyAddress: async ( @@ -104,22 +145,32 @@ const hwiService = { hmac: string | null, expectedAddress: string, ): Promise => { - const eventData = await invoke("hwi_verify_address", { - descriptor, - policy, - index, - walletName, - hmac, - expectedAddress, - }); - await invoke("emit_to_channel", { eventData }); + const eventData = + currentDeviceType === "onekey" + ? await onekeyService.verifyAddress() + : await invoke("hwi_verify_address", { + descriptor, + policy, + index, + walletName, + hmac, + expectedAddress, + }); + + await emitToChannel(eventData); }, promptPin: async (): Promise => { + if (currentDeviceType === "onekey") { + return; + } await invoke("hwi_prompt_pin"); }, sendPin: async (pin: string): Promise => { + if (currentDeviceType === "onekey") { + return; + } await invoke("hwi_send_pin", { pin }); }, }; diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts new file mode 100644 index 0000000..ea66a09 --- /dev/null +++ b/src/services/onekeyService.ts @@ -0,0 +1,330 @@ +import HardwareSDK from "@onekeyfe/hd-common-connect-sdk"; +import { ONEKEY_WEBUSB_FILTER } from "@onekeyfe/hd-shared"; +import { HWIDevice } from "../helpers/devices"; + +type OneKeyNetwork = "bitcoin" | "testnet"; + +type OneKeySdkResponse = { + success: boolean; + payload: T; +}; + +type OneKeyDeviceInfo = { + connectId?: string; + deviceId?: string; + name?: string; +}; + +type ActiveOneKeyDevice = { + connectId: string; + deviceId: string; +}; + +type WebUsbApi = { + getDevices: () => Promise; + requestDevice: (options: { filters: unknown[] }) => Promise; +}; + +type NavigatorWithUsb = { + usb?: WebUsbApi; +}; + +const NETWORK_TO_COIN = { + bitcoin: "btc", + testnet: "test", +} as const; + +let initialized = false; +let activeNetwork: OneKeyNetwork = "bitcoin"; +let discoveredDevices: OneKeyDeviceInfo[] = []; +let activeDevice: ActiveOneKeyDevice | null = null; + +const normalizeNetwork = ( + network: string | null | undefined, +): OneKeyNetwork => { + if (!network) return "bitcoin"; + return network.toLowerCase() === "mainnet" || + network.toLowerCase() === "bitcoin" + ? "bitcoin" + : "testnet"; +}; + +const getCoinType = (network: OneKeyNetwork) => (network === "bitcoin" ? 0 : 1); + +const formatPath = (purpose: number, coinType: number, account: number) => + `m/${purpose}'/${coinType}'/${account}'`; + +const toMasterFingerprint = (value: number | string | undefined) => { + if (value === undefined) return ""; + if (typeof value === "number") { + return (value >>> 0).toString(16).padStart(8, "0").toUpperCase(); + } + const parsed = Number(value); + if (!Number.isNaN(parsed)) { + return (parsed >>> 0).toString(16).padStart(8, "0").toUpperCase(); + } + return value.toUpperCase(); +}; + +const assertSuccess = ( + response: OneKeySdkResponse, + fallbackError: string, +): T => { + if (!response?.success) { + throw new Error( + (response?.payload as { error?: string; message?: string } | undefined) + ?.error || + (response?.payload as { error?: string; message?: string } | undefined) + ?.message || + fallbackError, + ); + } + return response.payload; +}; + +const buildChannelEventData = ( + action: string, + data: Record, +) => ({ + event: "CHANNEL_MESSAGE", + data: { + responseData: { + action, + data, + }, + }, +}); + +const ensureInitialized = async () => { + if (initialized) return; + await HardwareSDK.init({ + env: "desktop-webusb", + fetchConfig: true, + debug: false, + }); + initialized = true; +}; + +const ensureWebUsbPermission = async () => { + const usb = (navigator as unknown as NavigatorWithUsb).usb; + if (!usb?.getDevices || !usb?.requestDevice) { + throw new Error("WebUSB is not available in this environment"); + } + + const authorizedDevices = await usb.getDevices(); + if (authorizedDevices.length) return; + + try { + await usb.requestDevice({ filters: ONEKEY_WEBUSB_FILTER as unknown[] }); + } catch (error) { + throw new Error( + `OneKey USB authorization failed: ${(error as Error)?.message || String(error)}`, + ); + } +}; + +const ensureActiveDevice = async ( + network?: string | null, + preferredConnectId?: string | null, +) => { + const resolvedNetwork = normalizeNetwork(network); + activeNetwork = resolvedNetwork; + + await ensureInitialized(); + await ensureWebUsbPermission(); + + if (activeDevice && !preferredConnectId) return activeDevice; + + const payload = assertSuccess( + (await HardwareSDK.searchDevices()) as OneKeySdkResponse< + OneKeyDeviceInfo[] + >, + "No OneKey device found", + ); + discoveredDevices = Array.isArray(payload) ? payload : []; + + const selectedDevice = + discoveredDevices.find((item) => item.connectId === preferredConnectId) || + discoveredDevices[0]; + + if (!selectedDevice?.connectId) { + throw new Error("No connected OneKey device available"); + } + + const featurePayload = assertSuccess( + (await HardwareSDK.getFeatures( + selectedDevice.connectId, + )) as OneKeySdkResponse<{ + device_id?: string; + }>, + "Unable to read OneKey device features", + ); + + const resolvedDeviceId = featurePayload?.device_id || selectedDevice.deviceId; + if (!resolvedDeviceId) { + throw new Error("Unable to resolve OneKey deviceId"); + } + + activeDevice = { + connectId: selectedDevice.connectId, + deviceId: resolvedDeviceId, + }; + + return activeDevice; +}; + +const fetchDevices = async (network: string | null): Promise => { + const resolvedNetwork = normalizeNetwork(network); + activeNetwork = resolvedNetwork; + + await ensureInitialized(); + await ensureWebUsbPermission(); + + const payload = assertSuccess( + (await HardwareSDK.searchDevices()) as OneKeySdkResponse< + OneKeyDeviceInfo[] + >, + "No OneKey device found", + ); + + discoveredDevices = Array.isArray(payload) ? payload : []; + + return discoveredDevices.map((item) => ({ + device_type: "onekey", + model: item.name || "OneKey", + path: item.connectId || "", + needs_pin_sent: false, + needs_passphrase_sent: false, + fingerprint: item.connectId || null, + })); +}; + +const setHWIClient = async (fingerprint: string | null, network: string) => { + await ensureActiveDevice(network, fingerprint); +}; + +const getCurrentNetwork = () => activeNetwork; + +const shareXpubs = async (account: number) => { + const device = await ensureActiveDevice(activeNetwork); + const coinType = getCoinType(activeNetwork); + const coin = NETWORK_TO_COIN[activeNetwork]; + + const singleSigPath = formatPath(84, coinType, account); + const multiSigPath = `${formatPath(48, coinType, account)}/2'`; + const taprootPath = formatPath(86, coinType, account); + + const singleSig = assertSuccess( + (await HardwareSDK.btcGetPublicKey(device.connectId, device.deviceId, { + path: singleSigPath, + coin, + showOnOneKey: false, + scriptType: "SPENDWITNESS", + })) as OneKeySdkResponse<{ + xpub?: string; + xpubSegwit?: string; + fingerprint?: number; + }>, + "Unable to fetch OneKey single-sig xpub", + ); + + const multiSig = assertSuccess( + (await HardwareSDK.btcGetPublicKey(device.connectId, device.deviceId, { + path: multiSigPath, + coin, + showOnOneKey: false, + scriptType: "SPENDMULTISIG", + })) as OneKeySdkResponse<{ + xpub?: string; + xpubSegwit?: string; + fingerprint?: number; + }>, + "Unable to fetch OneKey multisig xpub", + ); + + const taproot = assertSuccess( + (await HardwareSDK.btcGetPublicKey(device.connectId, device.deviceId, { + path: taprootPath, + coin, + showOnOneKey: false, + scriptType: "SPENDTAPROOT", + })) as OneKeySdkResponse<{ + xpub?: string; + xpubSegwit?: string; + fingerprint?: number; + }>, + "Unable to fetch OneKey taproot xpub", + ); + + const masterFingerprint = toMasterFingerprint( + singleSig.fingerprint || multiSig.fingerprint || taproot.fingerprint, + ); + if (!masterFingerprint) { + throw new Error("Unable to resolve OneKey master fingerprint"); + } + + const payload = { + singleSigPath, + singleSigXpub: singleSig.xpubSegwit || singleSig.xpub || "", + multiSigPath, + multiSigXpub: multiSig.xpubSegwit || multiSig.xpub || "", + taprootPath, + taprootXpub: taproot.xpub || taproot.xpubSegwit || "", + mfp: masterFingerprint, + }; + + if (!payload.singleSigXpub || !payload.multiSigXpub || !payload.taprootXpub) { + throw new Error("Incomplete OneKey xpub response"); + } + + return buildChannelEventData("ADD_DEVICE", payload); +}; + +const performHealthCheck = async (account: number) => shareXpubs(account); + +const signTx = async (psbt: string) => { + const device = await ensureActiveDevice(activeNetwork); + const coin = NETWORK_TO_COIN[activeNetwork]; + + const signedPayload = assertSuccess( + (await HardwareSDK.btcSignPsbt(device.connectId, device.deviceId, { + psbt, + coin, + })) as OneKeySdkResponse<{ psbt?: string }>, + "OneKey failed to sign PSBT", + ); + + if (!signedPayload?.psbt) { + throw new Error("OneKey did not return signed PSBT"); + } + + return buildChannelEventData("SIGN_TX", { + signedSerializedPSBT: signedPayload.psbt, + hmac: null, + }); +}; + +const registerMultisig = async () => { + throw new Error( + "Register multisig is not yet supported for OneKey on desktop channel", + ); +}; + +const verifyAddress = async () => { + throw new Error( + "Address verification is not yet supported for OneKey on desktop channel", + ); +}; + +const onekeyService = { + fetchDevices, + setHWIClient, + shareXpubs, + performHealthCheck, + signTx, + registerMultisig, + verifyAddress, + getCurrentNetwork, +}; + +export default onekeyService; diff --git a/vite.config.ts b/vite.config.ts index 0c9adff..747b03c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,6 +4,17 @@ import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig(async () => ({ plugins: [react()], + define: { + global: "globalThis", + }, + resolve: { + alias: { + buffer: "buffer", + }, + }, + optimizeDeps: { + include: ["buffer"], + }, // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // From f622a1df89639fb819ddd5e15e1e43fd5c362e34 Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 17:36:39 +0800 Subject: [PATCH 02/10] feat: implement OneKey register and verify on desktop channel --- src/helpers/devices.ts | 13 +- src/services/hwiService.ts | 16 +- src/services/onekeyService.ts | 378 +++++++++++++++++++++++++++++++++- 3 files changed, 394 insertions(+), 13 deletions(-) diff --git a/src/helpers/devices.ts b/src/helpers/devices.ts index bf0907c..88a6159 100644 --- a/src/helpers/devices.ts +++ b/src/helpers/devices.ts @@ -132,12 +132,17 @@ const deviceContent: Record = { ], }, registerMultisig: { - text: "Registration via OneKey desktop channel is currently not supported.", - list: [], + text: "Please approve the registration of the multisig on the connected OneKey device.", + list: [ + "Make sure to verify the public keys and multisig details shown on the OneKey screen match the expected public keys of your cosigners and multisig details.", + ], }, verifyAddress: { - text: "Address verification via OneKey desktop channel is currently not supported.", - list: [], + text: "Clicking below will display the address on your OneKey device, make sure to read it carefully and verify that it matches the address on your Keeper mobile app.", + list: [ + "Only use the address from Keeper mobile app if it matches the address displayed on your OneKey.", + "In case the address on your OneKey is different than the address on the Keeper mobile app please contact support immediately.", + ], }, }, }, diff --git a/src/services/hwiService.ts b/src/services/hwiService.ts index de86dd9..a06ee12 100644 --- a/src/services/hwiService.ts +++ b/src/services/hwiService.ts @@ -126,7 +126,12 @@ const hwiService = { ): Promise => { const eventData = currentDeviceType === "onekey" - ? await onekeyService.registerMultisig() + ? await onekeyService.registerMultisig( + descriptor, + policy, + walletName, + expectedAddress, + ) : await invoke("hwi_register_multisig", { descriptor, policy, @@ -147,7 +152,14 @@ const hwiService = { ): Promise => { const eventData = currentDeviceType === "onekey" - ? await onekeyService.verifyAddress() + ? await onekeyService.verifyAddress( + descriptor, + policy, + index, + walletName, + hmac, + expectedAddress, + ) : await invoke("hwi_verify_address", { descriptor, policy, diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts index ea66a09..0bfda69 100644 --- a/src/services/onekeyService.ts +++ b/src/services/onekeyService.ts @@ -3,6 +3,13 @@ import { ONEKEY_WEBUSB_FILTER } from "@onekeyfe/hd-shared"; import { HWIDevice } from "../helpers/devices"; type OneKeyNetwork = "bitcoin" | "testnet"; +type OneKeyCoin = "btc" | "test"; +type OneKeyScriptType = + | "SPENDADDRESS" + | "SPENDMULTISIG" + | "SPENDWITNESS" + | "SPENDP2SHWITNESS" + | "SPENDTAPROOT"; type OneKeySdkResponse = { success: boolean; @@ -18,6 +25,7 @@ type OneKeyDeviceInfo = { type ActiveOneKeyDevice = { connectId: string; deviceId: string; + masterFingerprint: string; }; type WebUsbApi = { @@ -29,11 +37,45 @@ type NavigatorWithUsb = { usb?: WebUsbApi; }; +type DescriptorKeyOrigin = { + fingerprint: string; + basePath: string; + xpub: string; + suffixSegments: string[]; +}; + +type ResolvedDerivation = { + path: string; + addressN: number[]; +}; + +type OneKeyMultisigPubkey = { + node: string; + address_n: number[]; +}; + +type OneKeyAddressRequest = { + path: string; + coin: OneKeyCoin; + showOnOneKey: boolean; + scriptType: OneKeyScriptType; + multisig?: { + m: number; + signatures: string[]; + pubkeys: OneKeyMultisigPubkey[]; + }; +}; + +const HARDENED_OFFSET = 0x80000000; + const NETWORK_TO_COIN = { bitcoin: "btc", testnet: "test", } as const; +const KEY_ORIGIN_REGEX = + /\[([A-Fa-f0-9]{8})(\/[0-9hH'/]+)\]([1-9A-HJ-NP-Za-km-z]+)((?:\/(?:<\d+;\d+>|\*|\d+['hH]?))*)/g; + let initialized = false; let activeNetwork: OneKeyNetwork = "bitcoin"; let discoveredDevices: OneKeyDeviceInfo[] = []; @@ -66,6 +108,211 @@ const toMasterFingerprint = (value: number | string | undefined) => { return value.toUpperCase(); }; +const normalizeScript = (value: string) => + value.replace(/#.*$/, "").replace(/\s+/g, "").trim(); + +const parsePathSegment = (segment: string) => { + const trimmed = segment.trim(); + const hardened = /['hH]$/.test(trimmed); + const numericPart = trimmed.replace(/['hH]/g, ""); + + if (!/^\d+$/.test(numericPart)) { + throw new Error(`Invalid derivation segment: ${segment}`); + } + + const value = Number.parseInt(numericPart, 10); + if (Number.isNaN(value) || value < 0) { + throw new Error(`Invalid derivation segment value: ${segment}`); + } + + return { + display: `${value}${hardened ? "'" : ""}`, + numeric: hardened ? value + HARDENED_OFFSET : value, + }; +}; + +const resolveSuffixSegment = (segment: string, index: number) => { + if (segment === "*") { + return { display: `${index}`, numeric: index }; + } + + const branchMatch = segment.match(/^<(\d+);(\d+)>$/); + if (branchMatch) { + const externalBranch = Number.parseInt(branchMatch[1], 10); + return { display: `${externalBranch}`, numeric: externalBranch }; + } + + return parsePathSegment(segment); +}; + +const resolveDerivation = ( + keyOrigin: DescriptorKeyOrigin, + index: number, +): ResolvedDerivation => { + const resolvedSuffix = keyOrigin.suffixSegments.map((segment) => + resolveSuffixSegment(segment, index), + ); + + const suffixPath = resolvedSuffix.length + ? `/${resolvedSuffix.map((segment) => segment.display).join("/")}` + : ""; + + return { + path: `m${keyOrigin.basePath}${suffixPath}`, + addressN: resolvedSuffix.map((segment) => segment.numeric), + }; +}; + +const parseDescriptorKeys = (script: string): DescriptorKeyOrigin[] => { + const matches = script.matchAll(KEY_ORIGIN_REGEX); + const keys: DescriptorKeyOrigin[] = []; + const seen = new Set(); + + for (const match of matches) { + const fingerprint = match[1].toUpperCase(); + const basePath = match[2]; + const xpub = match[3]; + const suffixRaw = match[4] || ""; + const suffixSegments = suffixRaw + .split("/") + .map((segment) => segment.trim()) + .filter(Boolean); + + const dedupeKey = `${fingerprint}:${basePath}:${xpub}:${suffixSegments.join("/")}`; + if (seen.has(dedupeKey)) { + continue; + } + + seen.add(dedupeKey); + keys.push({ + fingerprint, + basePath, + xpub, + suffixSegments, + }); + } + + return keys; +}; + +const getMultisigThreshold = (script: string): number | null => { + const sortedMultiMatch = script.match(/(?:^|[,(])sortedmulti\((\d+),/i); + if (sortedMultiMatch) { + return Number.parseInt(sortedMultiMatch[1], 10); + } + + const multiMatch = script.match(/(?:^|[,(])multi\((\d+),/i); + if (multiMatch) { + return Number.parseInt(multiMatch[1], 10); + } + + const simpleThreshMatch = script.match( + /^wsh\(thresh\((\d+),(pk\([^()]+\))(,pk\([^()]+\))*\)\)$/i, + ); + if (simpleThreshMatch) { + return Number.parseInt(simpleThreshMatch[1], 10); + } + + return null; +}; + +const detectScriptType = ( + script: string, +): { + scriptType: OneKeyScriptType; + multisigThreshold: number | null; +} => { + const multisigThreshold = getMultisigThreshold(script); + if (multisigThreshold !== null) { + return { + scriptType: "SPENDMULTISIG", + multisigThreshold, + }; + } + + if (script.startsWith("wpkh(")) { + return { scriptType: "SPENDWITNESS", multisigThreshold: null }; + } + + if (script.startsWith("sh(wpkh(")) { + return { scriptType: "SPENDP2SHWITNESS", multisigThreshold: null }; + } + + if (script.startsWith("tr(")) { + return { scriptType: "SPENDTAPROOT", multisigThreshold: null }; + } + + if (script.startsWith("pkh(")) { + return { scriptType: "SPENDADDRESS", multisigThreshold: null }; + } + + throw new Error("Unsupported descriptor/policy for OneKey address display"); +}; + +const buildAddressRequest = ({ + script, + index, + coin, + masterFingerprint, +}: { + script: string; + index: number; + coin: OneKeyCoin; + masterFingerprint: string; +}): OneKeyAddressRequest => { + const normalizedScript = normalizeScript(script); + const keys = parseDescriptorKeys(normalizedScript); + if (!keys.length) { + throw new Error("No key origin found in descriptor/policy"); + } + + const { scriptType, multisigThreshold } = detectScriptType(normalizedScript); + + const currentKey = + keys.find((key) => key.fingerprint === masterFingerprint) || + (keys.length === 1 ? keys[0] : null); + + if (!currentKey) { + throw new Error("Unable to match OneKey fingerprint in descriptor/policy"); + } + + const currentPath = resolveDerivation(currentKey, index); + + if (scriptType === "SPENDMULTISIG") { + const threshold = multisigThreshold ?? 0; + if (threshold <= 0 || threshold > keys.length) { + throw new Error("Invalid multisig threshold in descriptor/policy"); + } + + const pubkeys = keys.map((key) => { + const resolved = resolveDerivation(key, index); + return { + node: key.xpub, + address_n: resolved.addressN, + }; + }); + + return { + path: currentPath.path, + coin, + showOnOneKey: true, + scriptType, + multisig: { + m: threshold, + signatures: new Array(pubkeys.length).fill(""), + pubkeys, + }, + }; + } + + return { + path: currentPath.path, + coin, + showOnOneKey: true, + scriptType, + }; +}; + const assertSuccess = ( response: OneKeySdkResponse, fallbackError: string, @@ -123,6 +370,32 @@ const ensureWebUsbPermission = async () => { } }; +const resolveDeviceMasterFingerprint = async ( + connectId: string, + deviceId: string, + network: OneKeyNetwork, +) => { + const accountPath = formatPath(84, getCoinType(network), 0); + const coin = NETWORK_TO_COIN[network]; + + const payload = assertSuccess( + (await HardwareSDK.btcGetPublicKey(connectId, deviceId, { + path: accountPath, + coin, + showOnOneKey: false, + scriptType: "SPENDWITNESS", + })) as OneKeySdkResponse<{ fingerprint?: number | string }>, + "Unable to read OneKey master fingerprint", + ); + + const masterFingerprint = toMasterFingerprint(payload.fingerprint); + if (!masterFingerprint) { + throw new Error("Unable to resolve OneKey master fingerprint"); + } + + return masterFingerprint; +}; + const ensureActiveDevice = async ( network?: string | null, preferredConnectId?: string | null, @@ -165,9 +438,16 @@ const ensureActiveDevice = async ( throw new Error("Unable to resolve OneKey deviceId"); } + const masterFingerprint = await resolveDeviceMasterFingerprint( + selectedDevice.connectId, + resolvedDeviceId, + resolvedNetwork, + ); + activeDevice = { connectId: selectedDevice.connectId, deviceId: resolvedDeviceId, + masterFingerprint, }; return activeDevice; @@ -263,6 +543,10 @@ const shareXpubs = async (account: number) => { throw new Error("Unable to resolve OneKey master fingerprint"); } + if (activeDevice) { + activeDevice.masterFingerprint = masterFingerprint; + } + const payload = { singleSigPath, singleSigXpub: singleSig.xpubSegwit || singleSig.xpub || "", @@ -304,16 +588,96 @@ const signTx = async (psbt: string) => { }); }; -const registerMultisig = async () => { - throw new Error( - "Register multisig is not yet supported for OneKey on desktop channel", +const showAddressOnDevice = async ({ + script, + index, + expectedAddress, +}: { + script: string; + index: number; + expectedAddress: string; +}) => { + const device = await ensureActiveDevice(activeNetwork); + const coin = NETWORK_TO_COIN[activeNetwork]; + + const request = buildAddressRequest({ + script, + index, + coin, + masterFingerprint: device.masterFingerprint, + }); + + const addressPayload = assertSuccess( + (await HardwareSDK.btcGetAddress( + device.connectId, + device.deviceId, + request, + )) as OneKeySdkResponse<{ address?: string }>, + "OneKey failed to display address", ); + + if (!addressPayload?.address) { + throw new Error("OneKey did not return address"); + } + + if ( + expectedAddress && + addressPayload.address.toLowerCase() !== expectedAddress.toLowerCase() + ) { + throw new Error( + "Address received from device does not match the expected address", + ); + } + + return addressPayload.address; }; -const verifyAddress = async () => { - throw new Error( - "Address verification is not yet supported for OneKey on desktop channel", - ); +const registerMultisig = async ( + descriptor: string | null, + policy: string | null, + _walletName: string | null, + expectedAddress: string, +) => { + const script = descriptor || policy; + if (!script) { + throw new Error("Either descriptor or policy must be provided"); + } + + const address = await showAddressOnDevice({ + script, + index: 0, + expectedAddress, + }); + + return buildChannelEventData("REGISTER_MULTISIG", { + address, + hmac: null, + }); +}; + +const verifyAddress = async ( + descriptor: string | null, + policy: string | null, + index: number | null, + _walletName: string | null, + hmac: string | null, + expectedAddress: string, +) => { + const script = descriptor || policy; + if (!script) { + throw new Error("Either descriptor or policy must be provided"); + } + + const address = await showAddressOnDevice({ + script, + index: index ?? 0, + expectedAddress, + }); + + return buildChannelEventData("VERIFY_ADDRESS", { + address, + hmac: hmac ?? null, + }); }; const onekeyService = { From 2a1347d8361161c45dbc78c52ad217a37afb6a42 Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 17:52:33 +0800 Subject: [PATCH 03/10] fix: normalize hardened paths parsed from descriptors --- src/services/onekeyService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts index 0bfda69..e0e5234 100644 --- a/src/services/onekeyService.ts +++ b/src/services/onekeyService.ts @@ -170,7 +170,7 @@ const parseDescriptorKeys = (script: string): DescriptorKeyOrigin[] => { for (const match of matches) { const fingerprint = match[1].toUpperCase(); - const basePath = match[2]; + const rawBasePath = match[2]; const xpub = match[3]; const suffixRaw = match[4] || ""; const suffixSegments = suffixRaw @@ -178,6 +178,13 @@ const parseDescriptorKeys = (script: string): DescriptorKeyOrigin[] => { .map((segment) => segment.trim()) .filter(Boolean); + const normalizedBasePathSegments = rawBasePath + .split("/") + .map((segment) => segment.trim()) + .filter(Boolean) + .map((segment) => parsePathSegment(segment).display); + const basePath = `/${normalizedBasePathSegments.join("/")}`; + const dedupeKey = `${fingerprint}:${basePath}:${xpub}:${suffixSegments.join("/")}`; if (seen.has(dedupeKey)) { continue; From fdbe83cbfc2ac259808c48726b7dc32a7ea9ffba Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 18:02:22 +0800 Subject: [PATCH 04/10] feat: add OneKey miniscript capability guard --- src/services/onekeyService.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts index e0e5234..7f1816d 100644 --- a/src/services/onekeyService.ts +++ b/src/services/onekeyService.ts @@ -223,6 +223,20 @@ const getMultisigThreshold = (script: string): number | null => { return null; }; +const isComplexMiniscript = (script: string) => { + const indicators = [ + "after(", + "and_", + "or_", + "older(", + "sha256(", + "hash256(", + "ripemd160(", + "hash160(", + ]; + return indicators.some((indicator) => script.includes(indicator)); +}; + const detectScriptType = ( script: string, ): { @@ -253,6 +267,12 @@ const detectScriptType = ( return { scriptType: "SPENDADDRESS", multisigThreshold: null }; } + if (isComplexMiniscript(script)) { + throw new Error( + "OneKey desktop channel does not yet support timelock or nested miniscript policies", + ); + } + throw new Error("Unsupported descriptor/policy for OneKey address display"); }; From e86c66db377effc14258ba742e01ab12f6747598 Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 18:25:14 +0800 Subject: [PATCH 05/10] Align OneKey desktop register flow with Trezor behavior --- src/helpers/devices.ts | 6 ++---- src/hooks/useDeviceActions.tsx | 4 ++++ src/screens/ConnectScreen/ConnectScreen.tsx | 4 ++++ src/services/hwiService.ts | 24 +++++++++------------ src/services/onekeyService.ts | 24 --------------------- 5 files changed, 20 insertions(+), 42 deletions(-) diff --git a/src/helpers/devices.ts b/src/helpers/devices.ts index 88a6159..1709ece 100644 --- a/src/helpers/devices.ts +++ b/src/helpers/devices.ts @@ -132,10 +132,8 @@ const deviceContent: Record = { ], }, registerMultisig: { - text: "Please approve the registration of the multisig on the connected OneKey device.", - list: [ - "Make sure to verify the public keys and multisig details shown on the OneKey screen match the expected public keys of your cosigners and multisig details.", - ], + text: "Operation not supported on OneKey", + list: [], }, verifyAddress: { text: "Clicking below will display the address on your OneKey device, make sure to read it carefully and verify that it matches the address on your Keeper mobile app.", diff --git a/src/hooks/useDeviceActions.tsx b/src/hooks/useDeviceActions.tsx index bf8ab94..b45a308 100644 --- a/src/hooks/useDeviceActions.tsx +++ b/src/hooks/useDeviceActions.tsx @@ -72,6 +72,10 @@ export const useDeviceActions = ({ onActionSuccess(); break; case "registerMultisig": + if (deviceType === "onekey") { + onError("Register multisig is not supported on OneKey"); + return; + } if (!descriptor && !miniscriptPolicy) { onError("Descriptor or miniscript policy is required"); return; diff --git a/src/screens/ConnectScreen/ConnectScreen.tsx b/src/screens/ConnectScreen/ConnectScreen.tsx index 38d4ae5..e50e343 100644 --- a/src/screens/ConnectScreen/ConnectScreen.tsx +++ b/src/screens/ConnectScreen/ConnectScreen.tsx @@ -155,6 +155,10 @@ const ConnectScreen = () => { } break; case "REGISTER_MULTISIG": + if (data.signerType?.toLowerCase() === "onekey") { + handleError("Register multisig is not supported on OneKey"); + return; + } setActionType("registerMultisig"); if (data.descriptorString) { setDescriptor(data.descriptorString.replace(/\*\*/g, "0/0")); diff --git a/src/services/hwiService.ts b/src/services/hwiService.ts index a06ee12..8d1ce42 100644 --- a/src/services/hwiService.ts +++ b/src/services/hwiService.ts @@ -124,20 +124,16 @@ const hwiService = { walletName: string | null, expectedAddress: string, ): Promise => { - const eventData = - currentDeviceType === "onekey" - ? await onekeyService.registerMultisig( - descriptor, - policy, - walletName, - expectedAddress, - ) - : await invoke("hwi_register_multisig", { - descriptor, - policy, - walletName, - expectedAddress, - }); + if (currentDeviceType === "onekey") { + throw new Error("Operation not supported on OneKey"); + } + + const eventData = await invoke("hwi_register_multisig", { + descriptor, + policy, + walletName, + expectedAddress, + }); await emitToChannel(eventData); }, diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts index 7f1816d..c6f4f56 100644 --- a/src/services/onekeyService.ts +++ b/src/services/onekeyService.ts @@ -659,29 +659,6 @@ const showAddressOnDevice = async ({ return addressPayload.address; }; -const registerMultisig = async ( - descriptor: string | null, - policy: string | null, - _walletName: string | null, - expectedAddress: string, -) => { - const script = descriptor || policy; - if (!script) { - throw new Error("Either descriptor or policy must be provided"); - } - - const address = await showAddressOnDevice({ - script, - index: 0, - expectedAddress, - }); - - return buildChannelEventData("REGISTER_MULTISIG", { - address, - hmac: null, - }); -}; - const verifyAddress = async ( descriptor: string | null, policy: string | null, @@ -713,7 +690,6 @@ const onekeyService = { shareXpubs, performHealthCheck, signTx, - registerMultisig, verifyAddress, getCurrentNetwork, }; From 309bcc095e4b1e75f7362370ac913d2a75bbde56 Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 19:05:00 +0800 Subject: [PATCH 06/10] Guard OneKey verify against complex miniscript policies --- src/services/onekeyService.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts index c6f4f56..de23480 100644 --- a/src/services/onekeyService.ts +++ b/src/services/onekeyService.ts @@ -75,6 +75,9 @@ const NETWORK_TO_COIN = { const KEY_ORIGIN_REGEX = /\[([A-Fa-f0-9]{8})(\/[0-9hH'/]+)\]([1-9A-HJ-NP-Za-km-z]+)((?:\/(?:<\d+;\d+>|\*|\d+['hH]?))*)/g; +const TOP_LEVEL_MULTI_REGEX = /^wsh\((?:sortedmulti|multi)\((\d+),.+\)\)$/i; +const SIMPLE_THRESH_REGEX = + /^wsh\(thresh\((\d+),(pk\([^()]+\))(,pk\([^()]+\))*\)\)$/i; let initialized = false; let activeNetwork: OneKeyNetwork = "bitcoin"; @@ -203,19 +206,12 @@ const parseDescriptorKeys = (script: string): DescriptorKeyOrigin[] => { }; const getMultisigThreshold = (script: string): number | null => { - const sortedMultiMatch = script.match(/(?:^|[,(])sortedmulti\((\d+),/i); - if (sortedMultiMatch) { - return Number.parseInt(sortedMultiMatch[1], 10); + const topLevelMultiMatch = script.match(TOP_LEVEL_MULTI_REGEX); + if (topLevelMultiMatch) { + return Number.parseInt(topLevelMultiMatch[1], 10); } - const multiMatch = script.match(/(?:^|[,(])multi\((\d+),/i); - if (multiMatch) { - return Number.parseInt(multiMatch[1], 10); - } - - const simpleThreshMatch = script.match( - /^wsh\(thresh\((\d+),(pk\([^()]+\))(,pk\([^()]+\))*\)\)$/i, - ); + const simpleThreshMatch = script.match(SIMPLE_THRESH_REGEX); if (simpleThreshMatch) { return Number.parseInt(simpleThreshMatch[1], 10); } @@ -225,8 +221,15 @@ const getMultisigThreshold = (script: string): number | null => { const isComplexMiniscript = (script: string) => { const indicators = [ + "thresh(", "after(", + "and_v(", "and_", + "andor(", + "or_b(", + "or_c(", + "or_d(", + "or_i(", "or_", "older(", "sha256(", From fa952ccae2891b982d795cfe6217d2e023bf4f6c Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 19:40:39 +0800 Subject: [PATCH 07/10] Remove OneKey register modal metadata --- src/helpers/devices.ts | 6 +-- .../DeviceActionModal/DeviceActionModal.tsx | 38 +++++++++++++------ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/helpers/devices.ts b/src/helpers/devices.ts index 1709ece..cc928ed 100644 --- a/src/helpers/devices.ts +++ b/src/helpers/devices.ts @@ -62,7 +62,7 @@ interface HWIDevice { interface DeviceContent { icon: string; - content: Record; + content: Partial>; } type HWIDeviceType = keyof typeof HWI_DEVICES; @@ -131,10 +131,6 @@ const deviceContent: Record = { "Only approve the request if the OneKey screen matches the expected details in Keeper.", ], }, - registerMultisig: { - text: "Operation not supported on OneKey", - list: [], - }, verifyAddress: { text: "Clicking below will display the address on your OneKey device, make sure to read it carefully and verify that it matches the address on your Keeper mobile app.", list: [ diff --git a/src/modals/DeviceActionModal/DeviceActionModal.tsx b/src/modals/DeviceActionModal/DeviceActionModal.tsx index 756a8a5..af629e0 100644 --- a/src/modals/DeviceActionModal/DeviceActionModal.tsx +++ b/src/modals/DeviceActionModal/DeviceActionModal.tsx @@ -34,14 +34,26 @@ interface DeviceActionModalProps { onError: (error: string) => void; } -const actionTitle = (deviceType: HWIDeviceType) => ({ - connect: `Connect ${HWI_DEVICES[deviceType].name}`, - shareXpubs: `Setting up ${HWI_DEVICES[deviceType].name}`, - healthCheck: `${HWI_DEVICES[deviceType].name} Health Check`, - signTx: "Sign Transaction", - registerMultisig: `Register Multisig on ${HWI_DEVICES[deviceType].name}`, - verifyAddress: `Verify Address on your ${HWI_DEVICES[deviceType].name}`, -}); +const getActionTitle = (deviceType: HWIDeviceType, actionType: HWI_ACTION) => { + if (actionType === "registerMultisig" && deviceType === "onekey") { + return "Unsupported Action"; + } + + switch (actionType) { + case "connect": + return `Connect ${HWI_DEVICES[deviceType].name}`; + case "shareXpubs": + return `Setting up ${HWI_DEVICES[deviceType].name}`; + case "healthCheck": + return `${HWI_DEVICES[deviceType].name} Health Check`; + case "signTx": + return "Sign Transaction"; + case "registerMultisig": + return `Register Multisig on ${HWI_DEVICES[deviceType].name}`; + case "verifyAddress": + return `Verify Address on your ${HWI_DEVICES[deviceType].name}`; + } +}; const DeviceActionModal = ({ isOpen, @@ -84,6 +96,10 @@ const DeviceActionModal = ({ const iconSrc = isVerifyAddress ? verifyAddressIcon : content.icon; const modalContent = useMemo(() => { + const actionContent = content.content[actionType] ?? { + text: "Operation not supported on this device", + list: [], + }; const iconStyle = isVerifyAddress ? { width: "173px", height: "137px", marginBottom: "-20px" } : {}; @@ -93,7 +109,7 @@ const DeviceActionModal = ({ deviceType === "coldcard" && actionType === "registerMultisig" ? "Please approve the registration of the wallet on the connected Coldcard device" - : content.content[actionType].text; + : actionContent.text; const listContent = miniscriptPolicy && @@ -102,7 +118,7 @@ const DeviceActionModal = ({ ? [ "Make sure to verify the public keys and wallet details shown on the Coldcard screen match the expected public keys of your cosigners and wallet details.", ] - : content.content[actionType].list; + : actionContent.list; const hasListItems = listContent.length > 0; @@ -123,7 +139,7 @@ const DeviceActionModal = ({ marginLeft: hasListItems ? "25px" : "0px", }} > - {actionTitle(deviceType)[actionType]} + {getActionTitle(deviceType, actionType)} ), content: ( From d8f99c59bfcf0e3b98eceb95887ad3f51891f5eb Mon Sep 17 00:00:00 2001 From: wabicai Date: Tue, 24 Feb 2026 19:44:31 +0800 Subject: [PATCH 08/10] Document OneKey SDK path vs HWI sidecar path --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index ff2ec83..814dc7d 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,28 @@ You can adjust the log level by changing `info` to `debug`, `warn`, or `error` a Log messages will appear in the terminal where you run the command. +## Hardware Integration Notes + +Keeper Desktop currently uses two hardware integration paths: + +1. **Rust HWI sidecar path** (Trezor / Ledger / BitBox02 / etc.) + - Routed through Tauri commands in `src-tauri/src/main.rs` (`hwi_*` commands). + - Backed by the `hwi` sidecar binary configured in `src-tauri/tauri.conf.json`. +2. **OneKey SDK path** (OneKey only) + - Routed through `src/services/onekeyService.ts`. + - Uses `@onekeyfe/hd-common-connect-sdk` with WebUSB. + +### Why OneKey is not on the HWI sidecar path yet + +Even though HWI supports common `--device-type` arguments, the current bundled sidecar path does not provide a native OneKey hardware implementation in this repository's Rust/HWI flow. +Because of that, OneKey is integrated through its official SDK path for reliable device discovery, xpub sharing, signing, and address verification. + +### Current OneKey capability scope + +- Supported: connect, share xpubs, health check, sign transaction, verify address +- Not supported: register multisig +- Not supported (for now): complex miniscript policy address verification (timelock / nested thresh) + ## Code Quality and CI We use several tools to maintain code quality and consistency: From 029a9d1e0a7c28157f2dbbf9c436bdcb15508af0 Mon Sep 17 00:00:00 2001 From: wabicai Date: Thu, 26 Feb 2026 11:31:17 +0800 Subject: [PATCH 09/10] refactor: remove OneKey WebUSB fallback and keep HWI sidecar only --- README.md | 13 +- package-lock.json | 468 ++--------------------- package.json | 3 - src-tauri/src/main.rs | 14 - src/main.tsx | 5 - src/services/hwiService.ts | 78 +--- src/services/onekeyService.ts | 700 ---------------------------------- vite.config.ts | 8 - 8 files changed, 48 insertions(+), 1241 deletions(-) delete mode 100644 src/services/onekeyService.ts diff --git a/README.md b/README.md index 814dc7d..c9ef252 100644 --- a/README.md +++ b/README.md @@ -65,19 +65,12 @@ Log messages will appear in the terminal where you run the command. ## Hardware Integration Notes -Keeper Desktop currently uses two hardware integration paths: +Keeper Desktop uses a single hardware integration path: -1. **Rust HWI sidecar path** (Trezor / Ledger / BitBox02 / etc.) +1. **Rust HWI sidecar path** (Trezor / Ledger / BitBox02 / OneKey / etc.) - Routed through Tauri commands in `src-tauri/src/main.rs` (`hwi_*` commands). - Backed by the `hwi` sidecar binary configured in `src-tauri/tauri.conf.json`. -2. **OneKey SDK path** (OneKey only) - - Routed through `src/services/onekeyService.ts`. - - Uses `@onekeyfe/hd-common-connect-sdk` with WebUSB. - -### Why OneKey is not on the HWI sidecar path yet - -Even though HWI supports common `--device-type` arguments, the current bundled sidecar path does not provide a native OneKey hardware implementation in this repository's Rust/HWI flow. -Because of that, OneKey is integrated through its official SDK path for reliable device discovery, xpub sharing, signing, and address verification. + - No WebUSB fallback path in the desktop frontend. ### Current OneKey capability scope diff --git a/package-lock.json b/package-lock.json index be88ebc..db32e95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,8 @@ "version": "0.2.4", "hasInstallScript": true, "dependencies": { - "@onekeyfe/hd-common-connect-sdk": "^1.1.23", - "@onekeyfe/hd-shared": "^1.1.23", "@tanstack/react-query": "^5.52.1", "@tauri-apps/api": "^1", - "buffer": "^6.0.3", "lodash": "^4.17.23", "qrcode.react": "^3.1.0", "react": "^18.2.0", @@ -934,19 +931,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -982,112 +966,6 @@ "node": ">= 8" } }, - "node_modules/@onekeyfe/hd-common-connect-sdk": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-common-connect-sdk/-/hd-common-connect-sdk-1.1.23.tgz", - "integrity": "sha512-4/r6DD2ljbh8CwQ4tD6Gi+5LVdyG4jri3RDqQuzLDcosAk/yCOMAIPJ1oZ3vGb6PLjdyLbwzJItrE48ODBMx9w==", - "license": "ISC", - "dependencies": { - "@onekeyfe/hd-core": "1.1.23", - "@onekeyfe/hd-shared": "1.1.23", - "@onekeyfe/hd-transport-emulator": "1.1.23", - "@onekeyfe/hd-transport-http": "1.1.23", - "@onekeyfe/hd-transport-lowlevel": "1.1.23", - "@onekeyfe/hd-transport-web-device": "1.1.23" - } - }, - "node_modules/@onekeyfe/hd-core": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-core/-/hd-core-1.1.23.tgz", - "integrity": "sha512-s4qxtL/jDbB2o/AQuxoYEudSymZjDX7uicT1efVeurgo+C3JcTVse7j/Rpc3p/LpxThADwHK9JvZFQQWq6MQdA==", - "license": "ISC", - "dependencies": { - "@onekeyfe/hd-shared": "1.1.23", - "@onekeyfe/hd-transport": "1.1.23", - "axios": "1.12.2", - "bignumber.js": "^9.0.2", - "bytebuffer": "^5.0.1", - "jszip": "^3.10.1", - "parse-uri": "^1.0.7", - "semver": "^7.3.7" - }, - "peerDependencies": { - "@noble/hashes": "^1.1.3" - } - }, - "node_modules/@onekeyfe/hd-core/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@onekeyfe/hd-shared": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-shared/-/hd-shared-1.1.23.tgz", - "integrity": "sha512-ieSICOuUj85glB2ia9PWCoHrt7Vk8dDYV0ruGe1TvQc+C/jT1xl7PwIzQs8fQF96py4Z568ABb2UTUaHFRadjA==", - "license": "ISC" - }, - "node_modules/@onekeyfe/hd-transport": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport/-/hd-transport-1.1.23.tgz", - "integrity": "sha512-HHeIRcBDzABok29OoNlxRRlFuSRfe1HfjPWQlTC+FI8hJZLtPYyg4DesYiNnPmpbSXXMxp8BAgjtPnSI1XKChw==", - "license": "ISC", - "dependencies": { - "bytebuffer": "^5.0.1", - "long": "^4.0.0", - "protobufjs": "^6.11.2" - } - }, - "node_modules/@onekeyfe/hd-transport-emulator": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-emulator/-/hd-transport-emulator-1.1.23.tgz", - "integrity": "sha512-8u0Mf5loXg2VMqnW0JwUhN9IhtaNiilLD9FLYgMkf3ZioxPZNyrKF/XFXV9RRn8E9ja/RW1aHBV2wFgO07YXtg==", - "license": "ISC", - "dependencies": { - "@onekeyfe/hd-shared": "1.1.23", - "@onekeyfe/hd-transport": "1.1.23", - "axios": "1.12.2", - "secure-json-parse": "^4.0.0" - } - }, - "node_modules/@onekeyfe/hd-transport-http": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-http/-/hd-transport-http-1.1.23.tgz", - "integrity": "sha512-s5dNhjwUOJKH6wdAGqEu94qK2bgvYviEg/FUpmSPFgbSjbVZxsEFW9Xw1cCpSApGIvON3kFAzwQEdIHr/xv/+A==", - "license": "ISC", - "dependencies": { - "@onekeyfe/hd-shared": "1.1.23", - "@onekeyfe/hd-transport": "1.1.23", - "axios": "1.12.2", - "secure-json-parse": "^4.0.0" - } - }, - "node_modules/@onekeyfe/hd-transport-lowlevel": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-lowlevel/-/hd-transport-lowlevel-1.1.23.tgz", - "integrity": "sha512-ju7nOjaHA0UcH3T/jLcURrrdbr1Vg4VUUan0z7vIITUUDCxjixR4vxGcd3YLq+xuyMvA+W4DxwkR4szV4e+tWA==", - "license": "MIT", - "dependencies": { - "@onekeyfe/hd-shared": "1.1.23", - "@onekeyfe/hd-transport": "1.1.23" - } - }, - "node_modules/@onekeyfe/hd-transport-web-device": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@onekeyfe/hd-transport-web-device/-/hd-transport-web-device-1.1.23.tgz", - "integrity": "sha512-VMBTY5W7BkVhZIgWfm/XQwkv7KJuxYmdbsTlfojcEcT7JskRAB6jlm70mjFNSdshLQ6gBhYTe/9HuA9pcHP0ow==", - "license": "MIT", - "dependencies": { - "@onekeyfe/hd-shared": "1.1.23", - "@onekeyfe/hd-transport": "1.1.23" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1110,70 +988,6 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, "node_modules/@remix-run/router": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", @@ -1733,16 +1547,11 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, "node_modules/@types/node": { "version": "22.5.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dev": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2250,6 +2059,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, "license": "MIT" }, "node_modules/available-typed-arrays": { @@ -2271,6 +2081,7 @@ "version": "1.12.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -2284,35 +2095,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -2373,51 +2155,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/bytebuffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", - "integrity": "sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ==", - "license": "Apache-2.0", - "dependencies": { - "long": "~3" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/bytebuffer/node_modules/long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.6" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -2441,6 +2178,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2492,6 +2230,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -2512,12 +2251,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2650,6 +2383,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -2671,6 +2405,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2763,6 +2498,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2772,6 +2508,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -2805,6 +2542,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2817,6 +2555,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3402,6 +3141,7 @@ "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, "funding": [ { "type": "individual", @@ -3446,6 +3186,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3482,6 +3223,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3526,6 +3268,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3550,6 +3293,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3728,6 +3472,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3779,6 +3524,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3791,6 +3537,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -3805,6 +3552,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -3812,26 +3560,6 @@ "node": ">= 0.4" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3841,12 +3569,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3886,7 +3608,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/internal-slot": { "version": "1.0.7", @@ -4374,18 +4097,6 @@ "node": ">=4.0" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4408,15 +4119,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4444,12 +4146,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4474,6 +4170,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4505,6 +4202,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4514,6 +4212,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -4852,12 +4551,6 @@ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4870,15 +4563,6 @@ "node": ">=6" } }, - "node_modules/parse-uri": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.16.tgz", - "integrity": "sha512-WMX9ygt2zzbtd3UlChi8S2Uj/dZa0N9QaotTkyRD7v06c50dor4qEWrM5ZvHiiaZYpXal4otRS9hynwwX0DVoA==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5034,12 +4718,6 @@ "node": ">=6.0.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5051,36 +4729,11 @@ "react-is": "^16.13.1" } }, - "node_modules/protobufjs": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", - "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/punycode": { "version": "2.3.1", @@ -5187,27 +4840,6 @@ "react-dom": ">=16.8" } }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -5391,12 +5023,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -5428,22 +5054,6 @@ "loose-envify": "^1.1.0" } }, - "node_modules/secure-json-parse": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", - "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5485,12 +5095,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5551,15 +5155,6 @@ "node": ">=0.10.0" } }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6035,7 +5630,8 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true }, "node_modules/update-browserslist-db": { "version": "1.1.3", @@ -6076,12 +5672,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", diff --git a/package.json b/package.json index dab17ea..1e25bdb 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,8 @@ "tauri:format:check": "cd src-tauri && cargo fmt -- --check" }, "dependencies": { - "@onekeyfe/hd-common-connect-sdk": "^1.1.23", - "@onekeyfe/hd-shared": "^1.1.23", "@tanstack/react-query": "^5.52.1", "@tauri-apps/api": "^1", - "buffer": "^6.0.3", "lodash": "^4.17.23", "qrcode.react": "^3.1.0", "react": "^18.2.0", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a71d3e1..1354be7 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -104,19 +104,6 @@ fn emit_to_channel(state: State<'_, AppState>, event_data: Value) -> Result<(), .map_err(|e| e.to_string()) } -#[tauri::command] -fn emit_to_channel_with_network( - state: State<'_, AppState>, - event_data: Value, - network: String, -) -> Result<(), String> { - let state = state.try_lock().map_err(|e| e.to_string())?; - state - .channel - .emit("CHANNEL_MESSAGE", event_data, false, Some(&network)) - .map_err(|e| e.to_string()) -} - // ==================== HWI Commands ==================== #[tauri::command] @@ -548,7 +535,6 @@ fn main() { hwi_register_multisig, hwi_verify_address, emit_to_channel, - emit_to_channel_with_network, hwi_send_pin, hwi_prompt_pin, async_hwi_enumerate, diff --git a/src/main.tsx b/src/main.tsx index 7b328ee..4bb9d6d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,13 +3,8 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { RouterProvider } from "react-router-dom"; -import { Buffer } from "buffer"; import router from "./routes"; -if (!(globalThis as { Buffer?: typeof Buffer }).Buffer) { - (globalThis as { Buffer?: typeof Buffer }).Buffer = Buffer; -} - const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/src/services/hwiService.ts b/src/services/hwiService.ts index 8d1ce42..be1002c 100644 --- a/src/services/hwiService.ts +++ b/src/services/hwiService.ts @@ -1,6 +1,5 @@ import { invoke } from "@tauri-apps/api/tauri"; import { HWIDevice, HWIDeviceType } from "../helpers/devices"; -import onekeyService from "./onekeyService"; interface Result { Ok: T; @@ -19,13 +18,6 @@ const emptyTrezorDevice: HWIDevice = { let currentDeviceType: string | null = null; const emitToChannel = async (eventData: unknown) => { - if (currentDeviceType === "onekey") { - await invoke("emit_to_channel_with_network", { - eventData, - network: onekeyService.getCurrentNetwork(), - }); - return; - } await invoke("emit_to_channel", { eventData }); }; @@ -34,10 +26,6 @@ const hwiService = { deviceType: HWIDeviceType | null = null, network: string | null = null, ): Promise => { - if (deviceType === "onekey") { - return onekeyService.fetchDevices(network); - } - if (network === "mainnet") { network = "bitcoin"; } @@ -70,11 +58,6 @@ const hwiService = { ): Promise => { currentDeviceType = deviceType; - if (deviceType === "onekey") { - await onekeyService.setHWIClient(fingerprint, network); - return; - } - if (network === "mainnet") { network = "bitcoin"; } @@ -82,20 +65,12 @@ const hwiService = { }, shareXpubs: async (account: number): Promise => { - const eventData = - currentDeviceType === "onekey" - ? await onekeyService.shareXpubs(account) - : await invoke("hwi_get_xpubs", { account }); - + const eventData = await invoke("hwi_get_xpubs", { account }); await emitToChannel(eventData); }, performHealthCheck: async (account: number): Promise => { - const eventData = - currentDeviceType === "onekey" - ? await onekeyService.performHealthCheck(account) - : await invoke("hwi_healthcheck", { account }); - + const eventData = await invoke("hwi_healthcheck", { account }); await emitToChannel(eventData); }, @@ -105,16 +80,12 @@ const hwiService = { walletName: string | null, hmac: string | null, ): Promise => { - const eventData = - currentDeviceType === "onekey" - ? await onekeyService.signTx(psbt) - : await invoke("hwi_sign_tx", { - psbt, - policy, - walletName, - hmac, - }); - + const eventData = await invoke("hwi_sign_tx", { + psbt, + policy, + walletName, + hmac, + }); await emitToChannel(eventData); }, @@ -146,39 +117,22 @@ const hwiService = { hmac: string | null, expectedAddress: string, ): Promise => { - const eventData = - currentDeviceType === "onekey" - ? await onekeyService.verifyAddress( - descriptor, - policy, - index, - walletName, - hmac, - expectedAddress, - ) - : await invoke("hwi_verify_address", { - descriptor, - policy, - index, - walletName, - hmac, - expectedAddress, - }); - + const eventData = await invoke("hwi_verify_address", { + descriptor, + policy, + index, + walletName, + hmac, + expectedAddress, + }); await emitToChannel(eventData); }, promptPin: async (): Promise => { - if (currentDeviceType === "onekey") { - return; - } await invoke("hwi_prompt_pin"); }, sendPin: async (pin: string): Promise => { - if (currentDeviceType === "onekey") { - return; - } await invoke("hwi_send_pin", { pin }); }, }; diff --git a/src/services/onekeyService.ts b/src/services/onekeyService.ts deleted file mode 100644 index de23480..0000000 --- a/src/services/onekeyService.ts +++ /dev/null @@ -1,700 +0,0 @@ -import HardwareSDK from "@onekeyfe/hd-common-connect-sdk"; -import { ONEKEY_WEBUSB_FILTER } from "@onekeyfe/hd-shared"; -import { HWIDevice } from "../helpers/devices"; - -type OneKeyNetwork = "bitcoin" | "testnet"; -type OneKeyCoin = "btc" | "test"; -type OneKeyScriptType = - | "SPENDADDRESS" - | "SPENDMULTISIG" - | "SPENDWITNESS" - | "SPENDP2SHWITNESS" - | "SPENDTAPROOT"; - -type OneKeySdkResponse = { - success: boolean; - payload: T; -}; - -type OneKeyDeviceInfo = { - connectId?: string; - deviceId?: string; - name?: string; -}; - -type ActiveOneKeyDevice = { - connectId: string; - deviceId: string; - masterFingerprint: string; -}; - -type WebUsbApi = { - getDevices: () => Promise; - requestDevice: (options: { filters: unknown[] }) => Promise; -}; - -type NavigatorWithUsb = { - usb?: WebUsbApi; -}; - -type DescriptorKeyOrigin = { - fingerprint: string; - basePath: string; - xpub: string; - suffixSegments: string[]; -}; - -type ResolvedDerivation = { - path: string; - addressN: number[]; -}; - -type OneKeyMultisigPubkey = { - node: string; - address_n: number[]; -}; - -type OneKeyAddressRequest = { - path: string; - coin: OneKeyCoin; - showOnOneKey: boolean; - scriptType: OneKeyScriptType; - multisig?: { - m: number; - signatures: string[]; - pubkeys: OneKeyMultisigPubkey[]; - }; -}; - -const HARDENED_OFFSET = 0x80000000; - -const NETWORK_TO_COIN = { - bitcoin: "btc", - testnet: "test", -} as const; - -const KEY_ORIGIN_REGEX = - /\[([A-Fa-f0-9]{8})(\/[0-9hH'/]+)\]([1-9A-HJ-NP-Za-km-z]+)((?:\/(?:<\d+;\d+>|\*|\d+['hH]?))*)/g; -const TOP_LEVEL_MULTI_REGEX = /^wsh\((?:sortedmulti|multi)\((\d+),.+\)\)$/i; -const SIMPLE_THRESH_REGEX = - /^wsh\(thresh\((\d+),(pk\([^()]+\))(,pk\([^()]+\))*\)\)$/i; - -let initialized = false; -let activeNetwork: OneKeyNetwork = "bitcoin"; -let discoveredDevices: OneKeyDeviceInfo[] = []; -let activeDevice: ActiveOneKeyDevice | null = null; - -const normalizeNetwork = ( - network: string | null | undefined, -): OneKeyNetwork => { - if (!network) return "bitcoin"; - return network.toLowerCase() === "mainnet" || - network.toLowerCase() === "bitcoin" - ? "bitcoin" - : "testnet"; -}; - -const getCoinType = (network: OneKeyNetwork) => (network === "bitcoin" ? 0 : 1); - -const formatPath = (purpose: number, coinType: number, account: number) => - `m/${purpose}'/${coinType}'/${account}'`; - -const toMasterFingerprint = (value: number | string | undefined) => { - if (value === undefined) return ""; - if (typeof value === "number") { - return (value >>> 0).toString(16).padStart(8, "0").toUpperCase(); - } - const parsed = Number(value); - if (!Number.isNaN(parsed)) { - return (parsed >>> 0).toString(16).padStart(8, "0").toUpperCase(); - } - return value.toUpperCase(); -}; - -const normalizeScript = (value: string) => - value.replace(/#.*$/, "").replace(/\s+/g, "").trim(); - -const parsePathSegment = (segment: string) => { - const trimmed = segment.trim(); - const hardened = /['hH]$/.test(trimmed); - const numericPart = trimmed.replace(/['hH]/g, ""); - - if (!/^\d+$/.test(numericPart)) { - throw new Error(`Invalid derivation segment: ${segment}`); - } - - const value = Number.parseInt(numericPart, 10); - if (Number.isNaN(value) || value < 0) { - throw new Error(`Invalid derivation segment value: ${segment}`); - } - - return { - display: `${value}${hardened ? "'" : ""}`, - numeric: hardened ? value + HARDENED_OFFSET : value, - }; -}; - -const resolveSuffixSegment = (segment: string, index: number) => { - if (segment === "*") { - return { display: `${index}`, numeric: index }; - } - - const branchMatch = segment.match(/^<(\d+);(\d+)>$/); - if (branchMatch) { - const externalBranch = Number.parseInt(branchMatch[1], 10); - return { display: `${externalBranch}`, numeric: externalBranch }; - } - - return parsePathSegment(segment); -}; - -const resolveDerivation = ( - keyOrigin: DescriptorKeyOrigin, - index: number, -): ResolvedDerivation => { - const resolvedSuffix = keyOrigin.suffixSegments.map((segment) => - resolveSuffixSegment(segment, index), - ); - - const suffixPath = resolvedSuffix.length - ? `/${resolvedSuffix.map((segment) => segment.display).join("/")}` - : ""; - - return { - path: `m${keyOrigin.basePath}${suffixPath}`, - addressN: resolvedSuffix.map((segment) => segment.numeric), - }; -}; - -const parseDescriptorKeys = (script: string): DescriptorKeyOrigin[] => { - const matches = script.matchAll(KEY_ORIGIN_REGEX); - const keys: DescriptorKeyOrigin[] = []; - const seen = new Set(); - - for (const match of matches) { - const fingerprint = match[1].toUpperCase(); - const rawBasePath = match[2]; - const xpub = match[3]; - const suffixRaw = match[4] || ""; - const suffixSegments = suffixRaw - .split("/") - .map((segment) => segment.trim()) - .filter(Boolean); - - const normalizedBasePathSegments = rawBasePath - .split("/") - .map((segment) => segment.trim()) - .filter(Boolean) - .map((segment) => parsePathSegment(segment).display); - const basePath = `/${normalizedBasePathSegments.join("/")}`; - - const dedupeKey = `${fingerprint}:${basePath}:${xpub}:${suffixSegments.join("/")}`; - if (seen.has(dedupeKey)) { - continue; - } - - seen.add(dedupeKey); - keys.push({ - fingerprint, - basePath, - xpub, - suffixSegments, - }); - } - - return keys; -}; - -const getMultisigThreshold = (script: string): number | null => { - const topLevelMultiMatch = script.match(TOP_LEVEL_MULTI_REGEX); - if (topLevelMultiMatch) { - return Number.parseInt(topLevelMultiMatch[1], 10); - } - - const simpleThreshMatch = script.match(SIMPLE_THRESH_REGEX); - if (simpleThreshMatch) { - return Number.parseInt(simpleThreshMatch[1], 10); - } - - return null; -}; - -const isComplexMiniscript = (script: string) => { - const indicators = [ - "thresh(", - "after(", - "and_v(", - "and_", - "andor(", - "or_b(", - "or_c(", - "or_d(", - "or_i(", - "or_", - "older(", - "sha256(", - "hash256(", - "ripemd160(", - "hash160(", - ]; - return indicators.some((indicator) => script.includes(indicator)); -}; - -const detectScriptType = ( - script: string, -): { - scriptType: OneKeyScriptType; - multisigThreshold: number | null; -} => { - const multisigThreshold = getMultisigThreshold(script); - if (multisigThreshold !== null) { - return { - scriptType: "SPENDMULTISIG", - multisigThreshold, - }; - } - - if (script.startsWith("wpkh(")) { - return { scriptType: "SPENDWITNESS", multisigThreshold: null }; - } - - if (script.startsWith("sh(wpkh(")) { - return { scriptType: "SPENDP2SHWITNESS", multisigThreshold: null }; - } - - if (script.startsWith("tr(")) { - return { scriptType: "SPENDTAPROOT", multisigThreshold: null }; - } - - if (script.startsWith("pkh(")) { - return { scriptType: "SPENDADDRESS", multisigThreshold: null }; - } - - if (isComplexMiniscript(script)) { - throw new Error( - "OneKey desktop channel does not yet support timelock or nested miniscript policies", - ); - } - - throw new Error("Unsupported descriptor/policy for OneKey address display"); -}; - -const buildAddressRequest = ({ - script, - index, - coin, - masterFingerprint, -}: { - script: string; - index: number; - coin: OneKeyCoin; - masterFingerprint: string; -}): OneKeyAddressRequest => { - const normalizedScript = normalizeScript(script); - const keys = parseDescriptorKeys(normalizedScript); - if (!keys.length) { - throw new Error("No key origin found in descriptor/policy"); - } - - const { scriptType, multisigThreshold } = detectScriptType(normalizedScript); - - const currentKey = - keys.find((key) => key.fingerprint === masterFingerprint) || - (keys.length === 1 ? keys[0] : null); - - if (!currentKey) { - throw new Error("Unable to match OneKey fingerprint in descriptor/policy"); - } - - const currentPath = resolveDerivation(currentKey, index); - - if (scriptType === "SPENDMULTISIG") { - const threshold = multisigThreshold ?? 0; - if (threshold <= 0 || threshold > keys.length) { - throw new Error("Invalid multisig threshold in descriptor/policy"); - } - - const pubkeys = keys.map((key) => { - const resolved = resolveDerivation(key, index); - return { - node: key.xpub, - address_n: resolved.addressN, - }; - }); - - return { - path: currentPath.path, - coin, - showOnOneKey: true, - scriptType, - multisig: { - m: threshold, - signatures: new Array(pubkeys.length).fill(""), - pubkeys, - }, - }; - } - - return { - path: currentPath.path, - coin, - showOnOneKey: true, - scriptType, - }; -}; - -const assertSuccess = ( - response: OneKeySdkResponse, - fallbackError: string, -): T => { - if (!response?.success) { - throw new Error( - (response?.payload as { error?: string; message?: string } | undefined) - ?.error || - (response?.payload as { error?: string; message?: string } | undefined) - ?.message || - fallbackError, - ); - } - return response.payload; -}; - -const buildChannelEventData = ( - action: string, - data: Record, -) => ({ - event: "CHANNEL_MESSAGE", - data: { - responseData: { - action, - data, - }, - }, -}); - -const ensureInitialized = async () => { - if (initialized) return; - await HardwareSDK.init({ - env: "desktop-webusb", - fetchConfig: true, - debug: false, - }); - initialized = true; -}; - -const ensureWebUsbPermission = async () => { - const usb = (navigator as unknown as NavigatorWithUsb).usb; - if (!usb?.getDevices || !usb?.requestDevice) { - throw new Error("WebUSB is not available in this environment"); - } - - const authorizedDevices = await usb.getDevices(); - if (authorizedDevices.length) return; - - try { - await usb.requestDevice({ filters: ONEKEY_WEBUSB_FILTER as unknown[] }); - } catch (error) { - throw new Error( - `OneKey USB authorization failed: ${(error as Error)?.message || String(error)}`, - ); - } -}; - -const resolveDeviceMasterFingerprint = async ( - connectId: string, - deviceId: string, - network: OneKeyNetwork, -) => { - const accountPath = formatPath(84, getCoinType(network), 0); - const coin = NETWORK_TO_COIN[network]; - - const payload = assertSuccess( - (await HardwareSDK.btcGetPublicKey(connectId, deviceId, { - path: accountPath, - coin, - showOnOneKey: false, - scriptType: "SPENDWITNESS", - })) as OneKeySdkResponse<{ fingerprint?: number | string }>, - "Unable to read OneKey master fingerprint", - ); - - const masterFingerprint = toMasterFingerprint(payload.fingerprint); - if (!masterFingerprint) { - throw new Error("Unable to resolve OneKey master fingerprint"); - } - - return masterFingerprint; -}; - -const ensureActiveDevice = async ( - network?: string | null, - preferredConnectId?: string | null, -) => { - const resolvedNetwork = normalizeNetwork(network); - activeNetwork = resolvedNetwork; - - await ensureInitialized(); - await ensureWebUsbPermission(); - - if (activeDevice && !preferredConnectId) return activeDevice; - - const payload = assertSuccess( - (await HardwareSDK.searchDevices()) as OneKeySdkResponse< - OneKeyDeviceInfo[] - >, - "No OneKey device found", - ); - discoveredDevices = Array.isArray(payload) ? payload : []; - - const selectedDevice = - discoveredDevices.find((item) => item.connectId === preferredConnectId) || - discoveredDevices[0]; - - if (!selectedDevice?.connectId) { - throw new Error("No connected OneKey device available"); - } - - const featurePayload = assertSuccess( - (await HardwareSDK.getFeatures( - selectedDevice.connectId, - )) as OneKeySdkResponse<{ - device_id?: string; - }>, - "Unable to read OneKey device features", - ); - - const resolvedDeviceId = featurePayload?.device_id || selectedDevice.deviceId; - if (!resolvedDeviceId) { - throw new Error("Unable to resolve OneKey deviceId"); - } - - const masterFingerprint = await resolveDeviceMasterFingerprint( - selectedDevice.connectId, - resolvedDeviceId, - resolvedNetwork, - ); - - activeDevice = { - connectId: selectedDevice.connectId, - deviceId: resolvedDeviceId, - masterFingerprint, - }; - - return activeDevice; -}; - -const fetchDevices = async (network: string | null): Promise => { - const resolvedNetwork = normalizeNetwork(network); - activeNetwork = resolvedNetwork; - - await ensureInitialized(); - await ensureWebUsbPermission(); - - const payload = assertSuccess( - (await HardwareSDK.searchDevices()) as OneKeySdkResponse< - OneKeyDeviceInfo[] - >, - "No OneKey device found", - ); - - discoveredDevices = Array.isArray(payload) ? payload : []; - - return discoveredDevices.map((item) => ({ - device_type: "onekey", - model: item.name || "OneKey", - path: item.connectId || "", - needs_pin_sent: false, - needs_passphrase_sent: false, - fingerprint: item.connectId || null, - })); -}; - -const setHWIClient = async (fingerprint: string | null, network: string) => { - await ensureActiveDevice(network, fingerprint); -}; - -const getCurrentNetwork = () => activeNetwork; - -const shareXpubs = async (account: number) => { - const device = await ensureActiveDevice(activeNetwork); - const coinType = getCoinType(activeNetwork); - const coin = NETWORK_TO_COIN[activeNetwork]; - - const singleSigPath = formatPath(84, coinType, account); - const multiSigPath = `${formatPath(48, coinType, account)}/2'`; - const taprootPath = formatPath(86, coinType, account); - - const singleSig = assertSuccess( - (await HardwareSDK.btcGetPublicKey(device.connectId, device.deviceId, { - path: singleSigPath, - coin, - showOnOneKey: false, - scriptType: "SPENDWITNESS", - })) as OneKeySdkResponse<{ - xpub?: string; - xpubSegwit?: string; - fingerprint?: number; - }>, - "Unable to fetch OneKey single-sig xpub", - ); - - const multiSig = assertSuccess( - (await HardwareSDK.btcGetPublicKey(device.connectId, device.deviceId, { - path: multiSigPath, - coin, - showOnOneKey: false, - scriptType: "SPENDMULTISIG", - })) as OneKeySdkResponse<{ - xpub?: string; - xpubSegwit?: string; - fingerprint?: number; - }>, - "Unable to fetch OneKey multisig xpub", - ); - - const taproot = assertSuccess( - (await HardwareSDK.btcGetPublicKey(device.connectId, device.deviceId, { - path: taprootPath, - coin, - showOnOneKey: false, - scriptType: "SPENDTAPROOT", - })) as OneKeySdkResponse<{ - xpub?: string; - xpubSegwit?: string; - fingerprint?: number; - }>, - "Unable to fetch OneKey taproot xpub", - ); - - const masterFingerprint = toMasterFingerprint( - singleSig.fingerprint || multiSig.fingerprint || taproot.fingerprint, - ); - if (!masterFingerprint) { - throw new Error("Unable to resolve OneKey master fingerprint"); - } - - if (activeDevice) { - activeDevice.masterFingerprint = masterFingerprint; - } - - const payload = { - singleSigPath, - singleSigXpub: singleSig.xpubSegwit || singleSig.xpub || "", - multiSigPath, - multiSigXpub: multiSig.xpubSegwit || multiSig.xpub || "", - taprootPath, - taprootXpub: taproot.xpub || taproot.xpubSegwit || "", - mfp: masterFingerprint, - }; - - if (!payload.singleSigXpub || !payload.multiSigXpub || !payload.taprootXpub) { - throw new Error("Incomplete OneKey xpub response"); - } - - return buildChannelEventData("ADD_DEVICE", payload); -}; - -const performHealthCheck = async (account: number) => shareXpubs(account); - -const signTx = async (psbt: string) => { - const device = await ensureActiveDevice(activeNetwork); - const coin = NETWORK_TO_COIN[activeNetwork]; - - const signedPayload = assertSuccess( - (await HardwareSDK.btcSignPsbt(device.connectId, device.deviceId, { - psbt, - coin, - })) as OneKeySdkResponse<{ psbt?: string }>, - "OneKey failed to sign PSBT", - ); - - if (!signedPayload?.psbt) { - throw new Error("OneKey did not return signed PSBT"); - } - - return buildChannelEventData("SIGN_TX", { - signedSerializedPSBT: signedPayload.psbt, - hmac: null, - }); -}; - -const showAddressOnDevice = async ({ - script, - index, - expectedAddress, -}: { - script: string; - index: number; - expectedAddress: string; -}) => { - const device = await ensureActiveDevice(activeNetwork); - const coin = NETWORK_TO_COIN[activeNetwork]; - - const request = buildAddressRequest({ - script, - index, - coin, - masterFingerprint: device.masterFingerprint, - }); - - const addressPayload = assertSuccess( - (await HardwareSDK.btcGetAddress( - device.connectId, - device.deviceId, - request, - )) as OneKeySdkResponse<{ address?: string }>, - "OneKey failed to display address", - ); - - if (!addressPayload?.address) { - throw new Error("OneKey did not return address"); - } - - if ( - expectedAddress && - addressPayload.address.toLowerCase() !== expectedAddress.toLowerCase() - ) { - throw new Error( - "Address received from device does not match the expected address", - ); - } - - return addressPayload.address; -}; - -const verifyAddress = async ( - descriptor: string | null, - policy: string | null, - index: number | null, - _walletName: string | null, - hmac: string | null, - expectedAddress: string, -) => { - const script = descriptor || policy; - if (!script) { - throw new Error("Either descriptor or policy must be provided"); - } - - const address = await showAddressOnDevice({ - script, - index: index ?? 0, - expectedAddress, - }); - - return buildChannelEventData("VERIFY_ADDRESS", { - address, - hmac: hmac ?? null, - }); -}; - -const onekeyService = { - fetchDevices, - setHWIClient, - shareXpubs, - performHealthCheck, - signTx, - verifyAddress, - getCurrentNetwork, -}; - -export default onekeyService; diff --git a/vite.config.ts b/vite.config.ts index 747b03c..2ae2470 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,14 +7,6 @@ export default defineConfig(async () => ({ define: { global: "globalThis", }, - resolve: { - alias: { - buffer: "buffer", - }, - }, - optimizeDeps: { - include: ["buffer"], - }, // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // From 345e44c42e40fa75fca2b0b81acaea2a6e68fd65 Mon Sep 17 00:00:00 2001 From: wabicai Date: Thu, 26 Feb 2026 17:30:31 +0800 Subject: [PATCH 10/10] feat: split OneKey PIN flow by model --- src/helpers/devices.ts | 45 ++++++ src/hooks/useModalState.tsx | 1 + src/modals/ModalManager.tsx | 15 ++ .../OneKeyPinModal/OneKeyPinModal.module.css | 71 ++++++++++ src/modals/OneKeyPinModal/OneKeyPinModal.tsx | 134 ++++++++++++++++++ src/modals/TrezorPinModal/TrezorPinModal.tsx | 34 +++-- src/modals/index.ts | 1 + src/screens/ConnectScreen/ConnectScreen.tsx | 17 ++- 8 files changed, 305 insertions(+), 13 deletions(-) create mode 100644 src/modals/OneKeyPinModal/OneKeyPinModal.module.css create mode 100644 src/modals/OneKeyPinModal/OneKeyPinModal.tsx diff --git a/src/helpers/devices.ts b/src/helpers/devices.ts index cc928ed..03eede2 100644 --- a/src/helpers/devices.ts +++ b/src/helpers/devices.ts @@ -67,6 +67,49 @@ interface DeviceContent { type HWIDeviceType = keyof typeof HWI_DEVICES; +type PinInteractionType = "host" | "device"; + +const ONEKEY_HOST_PIN_MODELS = new Set([ + "classic", + "classic1s", + "classicpure", + "onekey1", + "onekeyclassic", + "onekeyclassic1s", + "onekeyclassicpure", +]); + +const ONEKEY_DEVICE_PIN_MODELS = new Set([ + "touch", + "pro", + "t", + "onekeyt", + "onekeytouch", + "onekeypro", +]); + +const normalizeDeviceModel = (model: string | null | undefined) => + (model ?? "").toLowerCase().replace(/[^a-z0-9]/g, ""); + +const getPinInteractionType = ( + deviceType: HWIDeviceType, + model: string | null | undefined, +): PinInteractionType => { + if (deviceType !== "onekey") { + return "host"; + } + + const normalizedModel = normalizeDeviceModel(model); + if (ONEKEY_DEVICE_PIN_MODELS.has(normalizedModel)) { + return "device"; + } + if (ONEKEY_HOST_PIN_MODELS.has(normalizedModel)) { + return "host"; + } + + return "host"; +}; + const deviceContent: Record = { ledger: { icon: ledgerIconModal, @@ -298,7 +341,9 @@ export { HWI_DEVICES, HWI_ACTIONS, deviceContent, + getPinInteractionType, type HWI_ACTION, + type PinInteractionType, type HWIDeviceType, type HWIDevice, type NetworkType, diff --git a/src/hooks/useModalState.tsx b/src/hooks/useModalState.tsx index 9c12977..f5fbd6a 100644 --- a/src/hooks/useModalState.tsx +++ b/src/hooks/useModalState.tsx @@ -7,6 +7,7 @@ export type ModalType = | "multipleDevices" | "error" | "pin" + | "onekeyPin" | null; const useModalState = () => { diff --git a/src/modals/ModalManager.tsx b/src/modals/ModalManager.tsx index a6058c7..657945f 100644 --- a/src/modals/ModalManager.tsx +++ b/src/modals/ModalManager.tsx @@ -4,6 +4,7 @@ import { DeviceNotFoundModal, MultipleDevicesModal, ErrorModal, + OneKeyPinModal, TrezorPinModal, } from "./index"; import { @@ -30,6 +31,7 @@ interface ModalsManagerProps { hmac: string | null; expectedAddress: string | null; pairingCode: string | null; + currentDevice: HWIDevice | null; errorMessage: string; handleConnectResult: (devices: HWIDevice[]) => Promise; handleActionSuccess: () => void; @@ -54,6 +56,7 @@ const ModalsManager = ({ hmac, expectedAddress, pairingCode, + currentDevice, errorMessage, handleConnectResult, handleActionSuccess, @@ -125,7 +128,19 @@ const ModalsManager = ({ /> { + openModalHandler("deviceActionSuccess"); + }} + /> + + { openModalHandler("deviceActionSuccess"); diff --git a/src/modals/OneKeyPinModal/OneKeyPinModal.module.css b/src/modals/OneKeyPinModal/OneKeyPinModal.module.css new file mode 100644 index 0000000..68c5b65 --- /dev/null +++ b/src/modals/OneKeyPinModal/OneKeyPinModal.module.css @@ -0,0 +1,71 @@ +.errorContainer { + position: absolute; + top: 0; + left: 0; + right: 0; + display: flex; + padding: 10px; + min-width: 180px; + max-width: 250px; + opacity: 0; + transition: opacity 0.3s ease-in-out; + pointer-events: none; +} + +.errorContainer.show { + opacity: 1; +} + +.error { + background-color: #e54545; + color: white; + font-size: 12px; + padding: 10px 20px; + border-radius: 6px; + display: flex; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + width: 100%; +} + +.errorIcon { + width: 21px; + height: 21px; + margin-right: 10px; +} + +.text { + width: 220px; + margin: 0 auto; + text-align: center; +} + +.model { + margin-top: 12px; + font-size: 12px; + color: #3e524d; +} + +.continueButton { + margin-top: 12px; +} + +.continueButton:disabled { + opacity: 0.5; +} + +.loadingSpinner { + width: 24px; + height: 24px; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/src/modals/OneKeyPinModal/OneKeyPinModal.tsx b/src/modals/OneKeyPinModal/OneKeyPinModal.tsx new file mode 100644 index 0000000..f0f08ba --- /dev/null +++ b/src/modals/OneKeyPinModal/OneKeyPinModal.tsx @@ -0,0 +1,134 @@ +import { useState } from "react"; +import BaseModal from "../BaseModal/BaseModal"; +import styles from "./OneKeyPinModal.module.css"; +import baseStyles from "../BaseModal/BaseModal.module.css"; +import loader from "../../assets/loader.svg"; +import ErrorIcon from "../../assets/error-popup-icon.svg"; +import hwiService from "../../services/hwiService"; +import { + deviceContent, + HWI_DEVICES, + HWIDevice, + HWIDeviceType, + NetworkType, +} from "../../helpers/devices"; + +interface OneKeyPinModalProps { + isOpen: boolean; + deviceType: HWIDeviceType; + network: NetworkType | null; + model: string | null; + onClose: () => void; + onSuccess: () => void; +} + +const OneKeyPinModal = ({ + isOpen, + deviceType, + network, + model, + onClose, + onSuccess, +}: OneKeyPinModalProps) => { + const [error, setError] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + const showError = (message: string) => { + setError(message); + const timer = setTimeout(() => { + setError(""); + }, 4000); + return () => clearTimeout(timer); + }; + + const handleUnlocked = async () => { + if (!network) { + return showError("Network is not set"); + } + + setIsLoading(true); + await new Promise((resolve) => setTimeout(resolve, 100)); + + try { + const devices = await hwiService.fetchDevices( + deviceType, + network.toLowerCase(), + ); + const unlockedDevice = devices.find( + (device: HWIDevice) => !device.needs_pin_sent, + ); + + if (!unlockedDevice) { + return showError( + "Device is still locked. Please finish PIN entry on your device and retry.", + ); + } + + await hwiService.setHWIClient( + unlockedDevice.fingerprint, + deviceType, + network.toLowerCase(), + ); + onSuccess(); + } catch { + return showError("Failed to verify device unlock. Please try again."); + } finally { + setIsLoading(false); + } + }; + + const deviceName = HWI_DEVICES[deviceType].name; + + const modalContent = { + image: ( + {deviceName} + ), + title: ( +

+ Unlock {deviceName} on Device +

+ ), + content: ( + <> +
+
+ Error + {error} +
+
+

+ Please enter your PIN directly on the {deviceName} screen. After the + device is unlocked, click the button below. +

+ {model &&

Model: {model}

} + + ), + button: ( + + ), + }; + + return ( + + ); +}; + +export default OneKeyPinModal; diff --git a/src/modals/TrezorPinModal/TrezorPinModal.tsx b/src/modals/TrezorPinModal/TrezorPinModal.tsx index 9e11a05..0658c85 100644 --- a/src/modals/TrezorPinModal/TrezorPinModal.tsx +++ b/src/modals/TrezorPinModal/TrezorPinModal.tsx @@ -4,12 +4,14 @@ import styles from "./TrezorPinModal.module.css"; import baseStyles from "../BaseModal/BaseModal.module.css"; import loader from "../../assets/loader.svg"; import TrezorIcon from "../../assets/hww/icons-modal/trezor.svg"; +import OneKeyIcon from "../../assets/hww/icons-modal/onekey.svg"; import ErrorIcon from "../../assets/error-popup-icon.svg"; import hwiService from "../../services/hwiService"; -import { NetworkType } from "../../helpers/devices"; +import { HWIDeviceType, NetworkType } from "../../helpers/devices"; interface TrezorPinModalProps { isOpen: boolean; + deviceType: HWIDeviceType; network: NetworkType | null; onClose: () => void; onSuccess: () => void; @@ -17,6 +19,7 @@ interface TrezorPinModalProps { const TrezorPinModal = ({ isOpen, + deviceType, network, onClose, onSuccess, @@ -30,6 +33,9 @@ const TrezorPinModal = ({ }; const handlePinSubmit = async () => { + if (!network) { + return showError("Network is not set"); + } if (pin.length < 4) { return showError("PIN must be at least 4 digits"); } @@ -38,13 +44,17 @@ const TrezorPinModal = ({ try { await hwiService.sendPin(pin); const devices = await hwiService.fetchDevices( - "trezor", + deviceType, network?.toLowerCase(), ); + const unlockedDevice = devices.find((device) => !device.needs_pin_sent); + if (!unlockedDevice) { + throw new Error("Device is still locked"); + } await hwiService.setHWIClient( - devices[0].fingerprint, - "trezor", - network!.toLowerCase(), + unlockedDevice.fingerprint, + deviceType, + network.toLowerCase(), ); onSuccess(); } catch { @@ -65,16 +75,20 @@ const TrezorPinModal = ({ return () => clearTimeout(timer); } + const isOneKey = deviceType === "onekey"; + const icon = isOneKey ? OneKeyIcon : TrezorIcon; + const deviceName = isOneKey ? "OneKey" : "Trezor"; + const modalContent = { image: ( Trezor ), title: ( -

Enter the pin

+

Enter the PIN

), content: ( <> @@ -86,7 +100,7 @@ const TrezorPinModal = ({

- Follow the keypad layout on your Trezor + Follow the keypad layout on your {deviceName}

@@ -115,7 +129,7 @@ const TrezorPinModal = ({ className={styles.loadingSpinner} /> ) : ( - "Enter Pin" + "Enter PIN" )} ), diff --git a/src/modals/index.ts b/src/modals/index.ts index 7ef0f5d..c20f014 100644 --- a/src/modals/index.ts +++ b/src/modals/index.ts @@ -4,4 +4,5 @@ export { default as DeviceNotFoundModal } from "./DeviceNotFoundModal/DeviceNotF export { default as MultipleDevicesModal } from "./MultipleDevicesModal/MultipleDevicesModal"; export { default as ErrorModal } from "./ErrorModal/ErrorModal"; export { default as TrezorPinModal } from "./TrezorPinModal/TrezorPinModal"; +export { default as OneKeyPinModal } from "./OneKeyPinModal/OneKeyPinModal"; export { default as SubscriptionsModal } from "./SubscriptionsModal/SubscriptionsModal"; diff --git a/src/screens/ConnectScreen/ConnectScreen.tsx b/src/screens/ConnectScreen/ConnectScreen.tsx index e50e343..d6bddda 100644 --- a/src/screens/ConnectScreen/ConnectScreen.tsx +++ b/src/screens/ConnectScreen/ConnectScreen.tsx @@ -10,6 +10,7 @@ import { listen } from "@tauri-apps/api/event"; import QRCode from "qrcode.react"; import useModalState from "../../hooks/useModalState"; import { + getPinInteractionType, HWI_ACTION, HWIDevice, HWIDeviceType, @@ -57,6 +58,7 @@ const ConnectScreen = () => { const [expectedAddress, setExpectedAddress] = useState(null); const [errorMessage, setErrorMessage] = useState(""); const [pairingCode, setPairingCode] = useState(null); + const [currentDevice, setCurrentDevice] = useState(null); // Subscriptions state variables const [isSubscriptionsModalOpen, setSubscriptionsModalOpen] = useState(false); @@ -70,14 +72,22 @@ const ConnectScreen = () => { openModalHandler("notFound"); } else { if (deviceType && network) { + const device = devices[0]; + setCurrentDevice(device); await hwiService.setHWIClient( - devices[0].fingerprint, + device.fingerprint, deviceType, network.toLowerCase(), ); - if (devices[0].needs_pin_sent) { + if (device.needs_pin_sent) { await hwiService.promptPin(); - openModalHandler("pin"); + const pinInteractionType = getPinInteractionType( + deviceType, + device.model, + ); + openModalHandler( + pinInteractionType === "device" ? "onekeyPin" : "pin", + ); } else { openModalHandler("deviceActionSuccess"); } @@ -333,6 +343,7 @@ const ConnectScreen = () => { setCurrentAction={setCurrentAction} openModalHandler={openModalHandler} pairingCode={pairingCode} + currentDevice={currentDevice} /> )}
Version {version}