From 6b7030918a484b2e7ab0c9de5d270d5023202ae6 Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Thu, 25 Apr 2024 09:55:20 +0700 Subject: [PATCH 1/8] update: add @metacraft/crab dep --- package.json | 1 + yarn.lock | 122 +++++++++++++++++++++++++++------------------------ 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 285040f6e..2c823dc3f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "reset": "find . -type dir -name node_modules | xargs rm -rf && rm -rf yarn.lock" }, "dependencies": { + "@metacraft/crab": "^0.0.4", "@tkey/chrome-storage": "9.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index bd8ed3499..baaf0b362 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6137,6 +6137,13 @@ __metadata: languageName: node linkType: hard +"@metacraft/crab@npm:^0.0.4": + version: 0.0.4 + resolution: "@metacraft/crab@npm:0.0.4" + checksum: 8a230e56162566ad9761634d1c41c600d503c7d1cf9357c2768cd3bd1a0a92346fc93fa57bad0d7e512e8c75feb90d4a2d505ab458a191ea76a600827ba1ca47 + languageName: node + linkType: hard + "@metaplex-foundation/beet-solana@npm:0.4.0": version: 0.4.0 resolution: "@metaplex-foundation/beet-solana@npm:0.4.0" @@ -27548,63 +27555,64 @@ __metadata: version: 0.0.0-use.local resolution: "walless@workspace:." dependencies: - "@babel/core": "npm:^7.20.0" - "@babel/plugin-proposal-private-methods": "npm:^7.18.6" - "@babel/preset-env": "npm:^7.20.0" - "@babel/runtime": "npm:^7.20.0" - "@graphql-codegen/cli": "npm:5.0.0" - "@graphql-codegen/client-preset": "npm:4.1.0" - "@graphql-codegen/typescript": "npm:^4.0.0" - "@graphql-codegen/typescript-resolvers": "npm:^4.0.0" - "@jest/globals": "npm:^29.4.3" - "@next/eslint-plugin-next": "npm:^13.5.4" - "@react-native-clipboard/clipboard": "npm:^1.13.0" - "@solana/spl-account-compression": "npm:^0.2.0" - "@tkey/chrome-storage": "npm:9.0.0" - "@types/chrome": "npm:^0.0.231" - "@types/lodash": "npm:^4.14.191" - "@types/node": "npm:^17.0.12" - "@types/numeral": "npm:^2.0.2" - "@types/pouchdb-adapter-http": "npm:^6.1.3" - "@types/pouchdb-adapter-idb": "npm:^6.1.4" - "@types/pouchdb-adapter-websql": "npm:^6.1.4" - "@types/pouchdb-core": "npm:^7.0.11" - "@types/pouchdb-mapreduce": "npm:^6.1.7" - "@types/pouchdb-replication": "npm:^6.4.4" - "@types/react": "npm:^18.0.28" - "@types/react-dom": "npm:^18.0.7" - "@types/react-native": "npm:^0.71.2" - "@typescript-eslint/eslint-plugin": "npm:^6.7.4" - "@typescript-eslint/parser": "npm:^6.7.4" - "@walless/eslint-config": "npm:*" - "@walless/tsconfig": "npm:*" - "@wasm-tool/wasm-pack-plugin": "npm:^1.6.0" - archiver: "npm:^5.3.1" - babel-plugin-module-resolver: "npm:^4.1.0" - babel-plugin-transform-inline-environment-variables: "npm:^0.4.4" - constructs: "npm:10.2.69" - copy-webpack-plugin: "npm:^11.0.0" - cross-env: "npm:^7.0.3" - dotenv: "npm:^16.3.1" - eslint: "npm:^8.51.0" - fs-extra: "npm:^11.1.0" - jest: "npm:^29.4.3" - metro-react-native-babel-preset: "npm:0.73.9" - mustache: "npm:^4.2.0" - prettier: "npm:^3.0.3" - react-native: "npm:^0.71.14" - react-native-buffer: "npm:^6.0.3" - react-native-gesture-handler: "npm:^2.13.4" - react-native-reanimated: "npm:^3.6.1" - rimraf: "npm:^5.0.0" - shx: "npm:^0.3.4" - sst: "npm:2.41.4" - superstruct: "npm:^1.0.3" - ts-jest: "npm:^29.0.5" - tsconfig-paths-webpack-plugin: "npm:^4.0.1" - turbo: "npm:latest" - typescript: "npm:^5.2.2" - webpack: "npm:^5.89.0" + "@babel/core": ^7.20.0 + "@babel/plugin-proposal-private-methods": ^7.18.6 + "@babel/preset-env": ^7.20.0 + "@babel/runtime": ^7.20.0 + "@graphql-codegen/cli": 5.0.0 + "@graphql-codegen/client-preset": 4.1.0 + "@graphql-codegen/typescript": ^4.0.0 + "@graphql-codegen/typescript-resolvers": ^4.0.0 + "@jest/globals": ^29.4.3 + "@metacraft/crab": ^0.0.4 + "@next/eslint-plugin-next": ^13.5.4 + "@react-native-clipboard/clipboard": ^1.13.0 + "@solana/spl-account-compression": ^0.2.0 + "@tkey/chrome-storage": 9.0.0 + "@types/chrome": ^0.0.231 + "@types/lodash": ^4.14.191 + "@types/node": ^17.0.12 + "@types/numeral": ^2.0.2 + "@types/pouchdb-adapter-http": ^6.1.3 + "@types/pouchdb-adapter-idb": ^6.1.4 + "@types/pouchdb-adapter-websql": ^6.1.4 + "@types/pouchdb-core": ^7.0.11 + "@types/pouchdb-mapreduce": ^6.1.7 + "@types/pouchdb-replication": ^6.4.4 + "@types/react": ^18.0.28 + "@types/react-dom": ^18.0.7 + "@types/react-native": ^0.71.2 + "@typescript-eslint/eslint-plugin": ^6.7.4 + "@typescript-eslint/parser": ^6.7.4 + "@walless/eslint-config": "*" + "@walless/tsconfig": "*" + "@wasm-tool/wasm-pack-plugin": ^1.6.0 + archiver: ^5.3.1 + babel-plugin-module-resolver: ^4.1.0 + babel-plugin-transform-inline-environment-variables: ^0.4.4 + constructs: 10.2.69 + copy-webpack-plugin: ^11.0.0 + cross-env: ^7.0.3 + dotenv: ^16.3.1 + eslint: ^8.51.0 + fs-extra: ^11.1.0 + jest: ^29.4.3 + metro-react-native-babel-preset: 0.73.9 + mustache: ^4.2.0 + prettier: ^3.0.3 + react-native: ^0.71.14 + react-native-buffer: ^6.0.3 + react-native-gesture-handler: ^2.13.4 + react-native-reanimated: ^3.6.1 + rimraf: ^5.0.0 + shx: ^0.3.4 + sst: 2.41.4 + superstruct: ^1.0.3 + ts-jest: ^29.0.5 + tsconfig-paths-webpack-plugin: ^4.0.1 + turbo: latest + typescript: ^5.2.2 + webpack: ^5.89.0 languageName: unknown linkType: soft From f19fb0f9d21ec4d3bd15f3209f952656c353b29d Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Tue, 28 May 2024 12:46:04 +0700 Subject: [PATCH 2/8] update: upgrade @metacraft/crab dep version --- package.json | 2 +- yarn.lock | 124 +++++++++++++++++++++++++-------------------------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index 2c823dc3f..4b59abc8b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "reset": "find . -type dir -name node_modules | xargs rm -rf && rm -rf yarn.lock" }, "dependencies": { - "@metacraft/crab": "^0.0.4", + "@metacraft/crab": "^0.0.5", "@tkey/chrome-storage": "9.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index baaf0b362..f599c02d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6137,10 +6137,10 @@ __metadata: languageName: node linkType: hard -"@metacraft/crab@npm:^0.0.4": - version: 0.0.4 - resolution: "@metacraft/crab@npm:0.0.4" - checksum: 8a230e56162566ad9761634d1c41c600d503c7d1cf9357c2768cd3bd1a0a92346fc93fa57bad0d7e512e8c75feb90d4a2d505ab458a191ea76a600827ba1ca47 +"@metacraft/crab@npm:^0.0.5": + version: 0.0.5 + resolution: "@metacraft/crab@npm:0.0.5" + checksum: 10/0903289b66026ae5bf48b3dbcbbfc1aa1f90cc7c982f3eb8f26f1414754f9e98bee8c98eca4b39c35e3fdfde27bd09b57334a27dc0dace007f771234b2184c17 languageName: node linkType: hard @@ -27555,64 +27555,64 @@ __metadata: version: 0.0.0-use.local resolution: "walless@workspace:." dependencies: - "@babel/core": ^7.20.0 - "@babel/plugin-proposal-private-methods": ^7.18.6 - "@babel/preset-env": ^7.20.0 - "@babel/runtime": ^7.20.0 - "@graphql-codegen/cli": 5.0.0 - "@graphql-codegen/client-preset": 4.1.0 - "@graphql-codegen/typescript": ^4.0.0 - "@graphql-codegen/typescript-resolvers": ^4.0.0 - "@jest/globals": ^29.4.3 - "@metacraft/crab": ^0.0.4 - "@next/eslint-plugin-next": ^13.5.4 - "@react-native-clipboard/clipboard": ^1.13.0 - "@solana/spl-account-compression": ^0.2.0 - "@tkey/chrome-storage": 9.0.0 - "@types/chrome": ^0.0.231 - "@types/lodash": ^4.14.191 - "@types/node": ^17.0.12 - "@types/numeral": ^2.0.2 - "@types/pouchdb-adapter-http": ^6.1.3 - "@types/pouchdb-adapter-idb": ^6.1.4 - "@types/pouchdb-adapter-websql": ^6.1.4 - "@types/pouchdb-core": ^7.0.11 - "@types/pouchdb-mapreduce": ^6.1.7 - "@types/pouchdb-replication": ^6.4.4 - "@types/react": ^18.0.28 - "@types/react-dom": ^18.0.7 - "@types/react-native": ^0.71.2 - "@typescript-eslint/eslint-plugin": ^6.7.4 - "@typescript-eslint/parser": ^6.7.4 - "@walless/eslint-config": "*" - "@walless/tsconfig": "*" - "@wasm-tool/wasm-pack-plugin": ^1.6.0 - archiver: ^5.3.1 - babel-plugin-module-resolver: ^4.1.0 - babel-plugin-transform-inline-environment-variables: ^0.4.4 - constructs: 10.2.69 - copy-webpack-plugin: ^11.0.0 - cross-env: ^7.0.3 - dotenv: ^16.3.1 - eslint: ^8.51.0 - fs-extra: ^11.1.0 - jest: ^29.4.3 - metro-react-native-babel-preset: 0.73.9 - mustache: ^4.2.0 - prettier: ^3.0.3 - react-native: ^0.71.14 - react-native-buffer: ^6.0.3 - react-native-gesture-handler: ^2.13.4 - react-native-reanimated: ^3.6.1 - rimraf: ^5.0.0 - shx: ^0.3.4 - sst: 2.41.4 - superstruct: ^1.0.3 - ts-jest: ^29.0.5 - tsconfig-paths-webpack-plugin: ^4.0.1 - turbo: latest - typescript: ^5.2.2 - webpack: ^5.89.0 + "@babel/core": "npm:^7.20.0" + "@babel/plugin-proposal-private-methods": "npm:^7.18.6" + "@babel/preset-env": "npm:^7.20.0" + "@babel/runtime": "npm:^7.20.0" + "@graphql-codegen/cli": "npm:5.0.0" + "@graphql-codegen/client-preset": "npm:4.1.0" + "@graphql-codegen/typescript": "npm:^4.0.0" + "@graphql-codegen/typescript-resolvers": "npm:^4.0.0" + "@jest/globals": "npm:^29.4.3" + "@metacraft/crab": "npm:^0.0.5" + "@next/eslint-plugin-next": "npm:^13.5.4" + "@react-native-clipboard/clipboard": "npm:^1.13.0" + "@solana/spl-account-compression": "npm:^0.2.0" + "@tkey/chrome-storage": "npm:9.0.0" + "@types/chrome": "npm:^0.0.231" + "@types/lodash": "npm:^4.14.191" + "@types/node": "npm:^17.0.12" + "@types/numeral": "npm:^2.0.2" + "@types/pouchdb-adapter-http": "npm:^6.1.3" + "@types/pouchdb-adapter-idb": "npm:^6.1.4" + "@types/pouchdb-adapter-websql": "npm:^6.1.4" + "@types/pouchdb-core": "npm:^7.0.11" + "@types/pouchdb-mapreduce": "npm:^6.1.7" + "@types/pouchdb-replication": "npm:^6.4.4" + "@types/react": "npm:^18.0.28" + "@types/react-dom": "npm:^18.0.7" + "@types/react-native": "npm:^0.71.2" + "@typescript-eslint/eslint-plugin": "npm:^6.7.4" + "@typescript-eslint/parser": "npm:^6.7.4" + "@walless/eslint-config": "npm:*" + "@walless/tsconfig": "npm:*" + "@wasm-tool/wasm-pack-plugin": "npm:^1.6.0" + archiver: "npm:^5.3.1" + babel-plugin-module-resolver: "npm:^4.1.0" + babel-plugin-transform-inline-environment-variables: "npm:^0.4.4" + constructs: "npm:10.2.69" + copy-webpack-plugin: "npm:^11.0.0" + cross-env: "npm:^7.0.3" + dotenv: "npm:^16.3.1" + eslint: "npm:^8.51.0" + fs-extra: "npm:^11.1.0" + jest: "npm:^29.4.3" + metro-react-native-babel-preset: "npm:0.73.9" + mustache: "npm:^4.2.0" + prettier: "npm:^3.0.3" + react-native: "npm:^0.71.14" + react-native-buffer: "npm:^6.0.3" + react-native-gesture-handler: "npm:^2.13.4" + react-native-reanimated: "npm:^3.6.1" + rimraf: "npm:^5.0.0" + shx: "npm:^0.3.4" + sst: "npm:2.41.4" + superstruct: "npm:^1.0.3" + ts-jest: "npm:^29.0.5" + tsconfig-paths-webpack-plugin: "npm:^4.0.1" + turbo: "npm:latest" + typescript: "npm:^5.2.2" + webpack: "npm:^5.89.0" languageName: unknown linkType: soft From b9a194f4c10b1850179de957e6baebb4046286f0 Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Tue, 28 May 2024 12:49:07 +0700 Subject: [PATCH 3/8] feat: refactor connect and disconnect sdk flow --- apps/wallet/browser/bridge/request.ts | 35 ++-- apps/wallet/browser/content/utils.ts | 23 ++- apps/wallet/browser/kernel/handlers/common.ts | 63 +++++--- .../browser/kernel/messaging/index.ext.ts | 23 ++- .../wallet/browser/kernel/utils/middleware.ts | 151 ++++++++++++------ 5 files changed, 204 insertions(+), 91 deletions(-) diff --git a/apps/wallet/browser/bridge/request.ts b/apps/wallet/browser/bridge/request.ts index 4fd19ed5f..5d1e165fb 100644 --- a/apps/wallet/browser/bridge/request.ts +++ b/apps/wallet/browser/bridge/request.ts @@ -1,4 +1,7 @@ -import { PopupType, RequestType, ResponseCode, Timeout } from '@walless/core'; +import { ChromeChannel } from '@metacraft/crab/chrome'; +import type { RawRequest, Response } from '@metacraft/crab/core'; +import type { UnknownObject } from '@walless/core'; +import { PopupType, RequestType, Timeout } from '@walless/core'; import type { PureMessagePayload, ResponsePayload } from '@walless/messaging'; import { Channels } from '@walless/messaging'; import * as bs58 from 'bs58'; @@ -18,19 +21,25 @@ export const sendRequest = async ( }; export const handleRequestConnect = async ( - requestId: string, + resolveId: string, isApproved: boolean, ) => { - const payload: PopupPayload = { + const payload: RawRequest = { from: PopupType.REQUEST_CONNECT_POPUP, - type: RequestType.REQUEST_CONNECT, - sourceRequestId: requestId, + type: RequestType.REQUEST_CONNECT as never, + resolveId: resolveId, isApproved, }; try { - const res = await encryptedMessenger.request(Channels.kernel, payload); - if (res.responseCode === ResponseCode.SUCCESS) { + const chromeChannel = new ChromeChannel(Channels.popup); + const res = await chromeChannel.request>( + payload, + Timeout.sixtySeconds, + ); + if (res.error) { + throw Error(res.error); + } else { window.close(); } } catch (error) { @@ -74,18 +83,18 @@ export const handleRequestSignature = async ( }; export const getDataFromSourceRequest = async ( - requestId: string, + resolveId: string, from: string, ) => { - const payload: PopupPayload = { + const payload: RawRequest = { from, - type: RequestType.REQUEST_PAYLOAD, - sourceRequestId: requestId, + type: RequestType.REQUEST_PAYLOAD as never, + resolveId: resolveId, }; try { - const res = await encryptedMessenger.request( - Channels.kernel, + const chromeChannel = new ChromeChannel(Channels.popup); + const res = await chromeChannel.request>( payload, Timeout.sixtySeconds, ); diff --git a/apps/wallet/browser/content/utils.ts b/apps/wallet/browser/content/utils.ts index aa65851b5..c469c8e4e 100644 --- a/apps/wallet/browser/content/utils.ts +++ b/apps/wallet/browser/content/utils.ts @@ -1,7 +1,9 @@ -import { createMessenger } from '@walless/messaging'; - -const messenger = createMessenger(); +import { ChromeChannel } from '@metacraft/crab/chrome'; +import type { Response } from '@metacraft/crab/core'; +import { Timeout, type UnknownObject } from '@walless/core'; +import { Channels } from '@walless/messaging'; +const chromeChannel = new ChromeChannel(Channels.kernel); export const initializeMessaging = async () => { window.postMessage({ from: 'walless-content-script-loaded' }); window.addEventListener( @@ -15,9 +17,20 @@ export const initializeMessaging = async () => { await chrome.runtime.sendMessage(data); } else { // TODO: use timeout from sdk that include in data - const response = await messenger.request('kernel', data); + const newResponse = await chromeChannel.request< + Response + >( + { + ...data, + id: data.requestId, + }, + data.timeout || Timeout.sixtySeconds, + ); - window.postMessage(response); + window.postMessage({ + ...newResponse, + from: 'walless@kernel', + }); } } }, diff --git a/apps/wallet/browser/kernel/handlers/common.ts b/apps/wallet/browser/kernel/handlers/common.ts index 4a41fdc0d..9c21aea66 100644 --- a/apps/wallet/browser/kernel/handlers/common.ts +++ b/apps/wallet/browser/kernel/handlers/common.ts @@ -1,8 +1,10 @@ +import type { ChromeKernel } from '@metacraft/crab/chrome'; +import type { Middleware } from '@metacraft/crab/core'; import { PublicKey } from '@solana/web3.js'; -import type { SuiPublicKey } from '@walless/core'; +import type { RequestType, SuiPublicKey } from '@walless/core'; import { Networks } from '@walless/core'; import { ResponseCode } from '@walless/core'; -import type { ConnectOptions } from '@walless/sdk'; +import type { Channels } from '@walless/messaging'; import type { PublicKeyDocument, TrustedDomainDocument } from '@walless/store'; import { selectors } from '@walless/store'; import { encode } from 'bs58'; @@ -13,12 +15,10 @@ import { checkInstalledExtensionById, } from '../utils/helper'; import { openPopup } from '../utils/popup'; -import { getRequestRecord, respond } from '../utils/requestPool'; +import { respond } from '../utils/requestPool'; import type { HandleMethod } from '../utils/types'; -export const connect: HandleMethod<{ options?: ConnectOptions }> = async ({ - payload, -}) => { +export const connect: Middleware = async (payload, respond) => { if (!payload.options) throw new Error('No connection options provided'); const connectOptions = payload.options; @@ -63,12 +63,14 @@ export const connect: HandleMethod<{ options?: ConnectOptions }> = async ({ }) .filter((k) => !!k); - respond(payload.requestId, ResponseCode.SUCCESS, { publicKeys }); + respond({ + publicKeys, + requestId: payload.id, + responseCode: ResponseCode.SUCCESS, + }); }; -export const disconnect: HandleMethod<{ - options?: ConnectOptions; -}> = async ({ payload }) => { +export const disconnect: Middleware = async (payload, respond) => { if (!payload.options) throw Error('No disconnection options provided'); const connectOptions = payload.options; @@ -82,24 +84,33 @@ export const disconnect: HandleMethod<{ ); } - respond(payload.requestId, ResponseCode.SUCCESS); + respond({ + ...payload, + responseCode: ResponseCode.SUCCESS, + }); }; -export const requestPayload: HandleMethod<{ - sourceRequestId?: string; -}> = ({ payload }) => { - if (!payload.sourceRequestId) { - throw Error('Not sourceRequestId provided'); - } - - const { sourceRequestId, requestId } = payload; - const { payload: sourcePayload, channel: sourceChannel } = - getRequestRecord(sourceRequestId); - - respond(requestId, ResponseCode.SUCCESS, { - ...sourceChannel, - ...sourcePayload, - }); +export const requestPayload = ( + kernel: ChromeKernel, +): Middleware => { + return async (payload, respond) => { + if (!payload.resolveId) { + throw Error('Not sourceRequestId provided'); + } + + const { resolveId, id: requestId } = payload; + const response = kernel.getRequestByResolveId(resolveId); + + if ('error' in response) { + respond({ error: response.error }); + } else { + respond({ + requestId, + responseCode: ResponseCode.SUCCESS, + ...response.request, + }); + } + }; }; export type LayoutPayload = { diff --git a/apps/wallet/browser/kernel/messaging/index.ext.ts b/apps/wallet/browser/kernel/messaging/index.ext.ts index 3ff7f3a0c..8936a7f40 100644 --- a/apps/wallet/browser/kernel/messaging/index.ext.ts +++ b/apps/wallet/browser/kernel/messaging/index.ext.ts @@ -1,5 +1,7 @@ +import { ChromeKernel } from '@metacraft/crab/chrome'; import { PopupType, + RequestType, ResponseCode, ResponseMessage, runtime, @@ -9,9 +11,11 @@ import type { MessagePayload, MessengerCallback, } from '@walless/messaging'; -import { decryptMessage } from '@walless/messaging'; +import { Channels, decryptMessage } from '@walless/messaging'; +import * as common from '../handlers/common'; import { onKernelMessage } from '../handlers/kernel'; +import { checkConnection } from '../utils/middleware'; import { respond } from '../utils/requestPool'; import { encryptionKeyVault, initializeVaultKeys } from './shared'; @@ -81,4 +85,21 @@ export const initializeMessaging = async (): Promise => { }); callbackRegistry.kernel = onKernelMessage; + + const kernel = new ChromeKernel(); + kernel + .channel(Channels.kernel) + .handle(RequestType.REQUEST_CONNECT) + .use(checkConnection(kernel)) + .use(common.connect) + .handle(RequestType.REQUEST_DISCONNECT) + .use(common.disconnect) + + .channel(Channels.popup) + .handle(RequestType.REQUEST_PAYLOAD) + .use(common.requestPayload(kernel)) + .handle(RequestType.REQUEST_CONNECT) + .use(kernel.handleCrossResolvingMiddleware) + .run(); + console.log(kernel, '<<< kerenel'); }; diff --git a/apps/wallet/browser/kernel/utils/middleware.ts b/apps/wallet/browser/kernel/utils/middleware.ts index eee54e22b..49b8f248e 100644 --- a/apps/wallet/browser/kernel/utils/middleware.ts +++ b/apps/wallet/browser/kernel/utils/middleware.ts @@ -1,18 +1,21 @@ +import type { ChromeKernel } from '@metacraft/crab/chrome'; +import type { Middleware, RawRequest } from '@metacraft/crab/core'; +import type { RequestType } from '@walless/core'; import type { Networks } from '@walless/core'; import { PopupType, - RequestType, ResponseCode, ResponseMessage, + Timeout, } from '@walless/core'; +import type { Channels } from '@walless/messaging'; import { utils } from '@walless/network'; -import type { ConnectOptions } from '@walless/sdk'; import type { TrustedDomainDocument } from '@walless/store'; import { selectors } from '@walless/store'; import { storage } from 'utils/storage/db'; -import { closePopup, openPopup } from './popup'; -import { getRequestRecord, requestPool, respond } from './requestPool'; +import { openPopup } from './popup'; +import { getRequestRecord, respond } from './requestPool'; import type { HandleMethod } from './types'; export const getPrivateKey = ( @@ -33,57 +36,113 @@ export const getPrivateKey = ( }; }; -export const checkConnection: HandleMethod<{ - options?: ConnectOptions; -}> = async ({ payload, next }) => { - if (!payload.options) throw Error('No connection options provided'); - const { onlyIfTrusted, domain } = payload.options; +export const checkConnection = ( + kernel: ChromeKernel, +): Middleware => { + return async (payload, respond, next) => { + if (!payload.options) throw Error('No connection options provided'); + const { onlyIfTrusted, domain } = payload.options; - if (!onlyIfTrusted) { - next?.(payload); - return; - } + if (!onlyIfTrusted) { + next?.(payload); + return; + } - const domainResponse = await storage.find(selectors.trustedDomains); - const trustedDomains = domainResponse.docs as TrustedDomainDocument[]; - const savedDomain = trustedDomains.find(({ _id }) => _id == domain); - if (!savedDomain || !savedDomain.connect) { - Object.values(requestPool).forEach((ele) => { - if ( - ele.payload.requestId !== payload.requestId && - ele.payload.type === RequestType.REQUEST_CONNECT && - ele.payload.options.domain === domain - ) { - respond(ele.payload.requestId, ResponseCode.ERROR); - closePopup(ele.payload.popupId); + const domainResponse = await storage.find(selectors.trustedDomains); + const trustedDomains = domainResponse.docs as TrustedDomainDocument[]; + const savedDomain = trustedDomains.find(({ _id }) => _id == domain); + if (!savedDomain || !savedDomain.connect) { + const { isApproved } = await handleUserAction<{ isApproved: boolean }>({ + kernel, + payload, + popupType: PopupType.REQUEST_CONNECT_POPUP, + }); + + if (isApproved) { + next?.(payload); + } else { + respond({ + requestId: payload.id, + error: ResponseMessage.REJECT_REQUEST_CONNECT, + responseCode: ResponseCode.ERROR, + }); } - }); + } else if (!savedDomain.trusted) { + respond({ + requestId: payload.id, + error: ResponseMessage.REJECT_REQUEST_CONNECT, + responseCode: ResponseCode.ERROR, + }); + } else { + next?.(payload); + } + }; +}; - await openPopup(PopupType.REQUEST_CONNECT_POPUP, payload.requestId); - } else if (!savedDomain.trusted) { - return respond(payload.requestId, ResponseCode.ERROR, { - error: ResponseMessage.REJECT_REQUEST_CONNECT, - }); - } else { - next?.(payload); - } +const handleUserAction = async ({ + kernel, + payload, + popupType, +}: { + kernel: ChromeKernel; + payload: any; + popupType: PopupType; +}) => { + const { resolveId, resolve } = kernel.createCrossResolvingRequest( + payload.requestId, + payload.timeout || Timeout.sixtySeconds, + ); + + openPopup(popupType, resolveId); + + return await resolve>(); }; -export const checkApproval: HandleMethod<{ - isApproved?: boolean; -}> = async ({ payload, next }) => { - const { requestId, sourceRequestId, isApproved } = payload; +// export const checkApproval = ( +// kernel: ChromeKernel, +// ): Middleware => { +// return async (payload, respond) => { +// const { resolveId, isApproved } = payload; + +// if (!isApproved) { +// kernel.handleCrossResolvingMiddleware(payload); +// respond(sourceRequestId, ResponseCode.ERROR, { +// message: ResponseMessage.REJECT_REQUEST_CONNECT, +// }); +// } else { +// payload = getRequestRecord(sourceRequestId).payload; +// await next?.(payload); +// } + +// respond(requestId, ResponseCode.SUCCESS); +// }; +// }; + +export const requestUserPermission = ( + kernel: ChromeKernel, +): Middleware => { + return async (payload, respond, next) => { + if (!payload.options) throw Error('No connection options provided'); - if (!isApproved) { - respond(sourceRequestId, ResponseCode.ERROR, { - message: ResponseMessage.REJECT_REQUEST_CONNECT, + const { passcode, isApproved } = await handleUserAction<{ + passcode: string; + isApproved: boolean; + }>({ + kernel, + payload, + popupType: PopupType.SIGNATURE_POPUP, }); - } else { - payload = getRequestRecord(sourceRequestId).payload; - await next?.(payload); - } - respond(requestId, ResponseCode.SUCCESS); + if (isApproved) { + next?.({ ...payload, passcode }); + } else { + respond({ + requestId: payload.id, + error: ResponseMessage.REJECT_COMMON_REQUEST, + responseCode: ResponseCode.ERROR, + }); + } + }; }; export const filterSDKSignatureRequest: HandleMethod<{ From 013885a2196e00436b7898218a45c41cb04d4a2a Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Tue, 28 May 2024 12:53:30 +0700 Subject: [PATCH 4/8] update: keep legacy flow which has not refactored --- apps/wallet/browser/kernel/handlers/kernel.ts | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/wallet/browser/kernel/handlers/kernel.ts b/apps/wallet/browser/kernel/handlers/kernel.ts index 20269d518..b04ea5ac5 100644 --- a/apps/wallet/browser/kernel/handlers/kernel.ts +++ b/apps/wallet/browser/kernel/handlers/kernel.ts @@ -1,10 +1,8 @@ -import { Networks, PopupType, RequestType, ResponseCode } from '@walless/core'; +import { Networks, RequestType, ResponseCode } from '@walless/core'; import type { MessengerCallback } from '@walless/messaging'; import { handle } from '../utils/handle'; import { - checkApproval, - checkConnection, filterSDKSignatureRequest, forwardToSourceRequest, getPrivateKey, @@ -30,18 +28,28 @@ export const onKernelMessage: MessengerCallback = async (payload, channel) => { }); } - if (type === RequestType.REQUEST_CONNECT && payload.from === 'walless@sdk') { - handle(payload).execute([checkConnection, common.connect]); - } else if ( - type === RequestType.REQUEST_CONNECT && - payload.from === PopupType.REQUEST_CONNECT_POPUP - ) { - handle(payload).execute([checkApproval, common.connect]); - } else if (type === RequestType.REQUEST_DISCONNECT) { - handle(payload).execute([common.disconnect]); - } else if (type === RequestType.REQUEST_PAYLOAD) { - handle(payload).execute([common.requestPayload]); - } else if (type === RequestType.INSTALL_LAYOUT) { + if (requestId) { + switch (type) { + case RequestType.REQUEST_CONNECT: + case RequestType.REQUEST_DISCONNECT: + case RequestType.REQUEST_PAYLOAD: + return; + } + } + + // if (type === RequestType.REQUEST_CONNECT && payload.from === 'walless@sdk') { + // handle(payload).execute([checkConnection, common.connect]); + // } else if ( + // type === RequestType.REQUEST_CONNECT && + // payload.from === PopupType.REQUEST_CONNECT_POPUP + // ) { + // handle(payload).execute([checkApproval, common.connect]); + // } else if (type === RequestType.REQUEST_DISCONNECT) { + // handle(payload).execute([common.disconnect]); + // } else if (type === RequestType.REQUEST_PAYLOAD) { + // handle(payload).execute([common.requestPayload]); + // } else + if (type === RequestType.INSTALL_LAYOUT) { handle(payload).execute([common.installLayout]); } else if (type === RequestType.CHECK_INSTALLED_LAYOUT) { handle(payload).execute([common.checkInstalledLayout]); From facef772502d397045fa706fbe532314bd15e766 Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Tue, 28 May 2024 12:54:29 +0700 Subject: [PATCH 5/8] chore: rename variable for compatibility with new messaging mechanism --- apps/wallet/browser/kernel/utils/popup.ts | 4 ++-- apps/wallet/src/screens/Requests/Connect.tsx | 10 +++++----- apps/wallet/src/utils/hooks/request.ts | 6 +++--- apps/wallet/src/utils/navigation/index.ts | 6 +++--- apps/wallet/src/utils/navigation/types.ts | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/wallet/browser/kernel/utils/popup.ts b/apps/wallet/browser/kernel/utils/popup.ts index 8c484c591..589db002d 100644 --- a/apps/wallet/browser/kernel/utils/popup.ts +++ b/apps/wallet/browser/kernel/utils/popup.ts @@ -1,4 +1,4 @@ -export const openPopup = async (id: string, requestId: string) => { +export const openPopup = async (id: string, resolveId: string) => { const lastFocusedWindow = await chrome.windows.getLastFocused(); const { top, left = 0, width = 0 } = lastFocusedWindow; const leftPos = left + width - 420; @@ -9,7 +9,7 @@ export const openPopup = async (id: string, requestId: string) => { type: 'popup', width: 420, height: 600, - url: `popup.html#/requests/${id}/${requestId}`, + url: `popup.html#/requests/${id}/${resolveId}`, focused: true, }); }; diff --git a/apps/wallet/src/screens/Requests/Connect.tsx b/apps/wallet/src/screens/Requests/Connect.tsx index e37afe505..04581ce53 100644 --- a/apps/wallet/src/screens/Requests/Connect.tsx +++ b/apps/wallet/src/screens/Requests/Connect.tsx @@ -17,23 +17,23 @@ import { logoSize, unknownLogo } from './shared'; type Props = StackScreenProps; const RequestConnect: FC = ({ route }) => { - const { requestId } = route.params; + const { resolveId } = route.params; const { sender, options } = useRequestData( - requestId as string, + resolveId as string, PopupType.REQUEST_CONNECT_POPUP, ); const publicKey = usePublicKeys(options.network || Networks.solana)[0]; const onApprovePress = () => { - handleRequestConnect(requestId as string, true); + handleRequestConnect(resolveId as string, true); }; const onRejectPress = () => { - handleRequestConnect(requestId as string, false); + handleRequestConnect(resolveId as string, false); }; useEffect(() => { - initializeKernelConnect(PopupType.REQUEST_CONNECT_POPUP + '/' + requestId); + initializeKernelConnect(PopupType.REQUEST_CONNECT_POPUP + '/' + resolveId); }, []); const title = sender.tab?.title || 'Unknown'; diff --git a/apps/wallet/src/utils/hooks/request.ts b/apps/wallet/src/utils/hooks/request.ts index 881f4fa14..64eefd6e3 100644 --- a/apps/wallet/src/utils/hooks/request.ts +++ b/apps/wallet/src/utils/hooks/request.ts @@ -5,7 +5,7 @@ import type { MessagePayload } from '@walless/messaging'; import type { ConnectOptions } from '@walless/sdk'; import { getDataFromSourceRequest } from 'bridge'; -export const useRequestData = (requestId: string, from: string) => { +export const useRequestData = (resolveId: string, from: string) => { const [sender, setSender] = useState({}); const [options, setOptions] = useState({}); const [message, setMessage] = useState(''); @@ -14,7 +14,7 @@ export const useRequestData = (requestId: string, from: string) => { useEffect(() => { const configureSender = async () => { - const result = await getDataFromSourceRequest(requestId, from); + const result = await getDataFromSourceRequest(resolveId, from); const { sender, message, transaction, options } = result ?? {}; setPayload(result as MessagePayload); @@ -39,7 +39,7 @@ export const useRequestData = (requestId: string, from: string) => { if (runtime.isExtension) { configureSender(); } - }, [requestId]); + }, [resolveId]); return { ...payload, diff --git a/apps/wallet/src/utils/navigation/index.ts b/apps/wallet/src/utils/navigation/index.ts index fe3e399c2..adbaa3d37 100644 --- a/apps/wallet/src/utils/navigation/index.ts +++ b/apps/wallet/src/utils/navigation/index.ts @@ -93,13 +93,13 @@ export const linking: LinkingOptions = { path: '/requests', screens: { RequestConnect: { - path: '/connect/:requestId', + path: '/connect/:resolveId', }, RequestSignature: { - path: '/signature/:requestId', + path: '/signature/:resolveId', }, RequestInstallLayout: { - path: '/install-layout/:requestId', + path: '/install-layout/:resolveId', }, }, }, diff --git a/apps/wallet/src/utils/navigation/types.ts b/apps/wallet/src/utils/navigation/types.ts index ea33b2ce7..e309e3d87 100644 --- a/apps/wallet/src/utils/navigation/types.ts +++ b/apps/wallet/src/utils/navigation/types.ts @@ -62,9 +62,9 @@ export type DashboardParamList = { }; export type RequestsParamList = { - RequestConnect: { requestId: string }; - RequestSignature: { requestId: string }; - RequestInstallLayout: { requestId: string }; + RequestConnect: { resolveId: string }; + RequestSignature: { resolveId: string }; + RequestInstallLayout: { resolveId: string }; }; export type RootParamList = { From d1c637b338ac21780bebee9f990638644024b0af Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Wed, 12 Jun 2024 12:59:34 +0700 Subject: [PATCH 6/8] feat: Impl sign message feat using new messaging lib --- apps/wallet/browser/bridge/request.ts | 18 ++- apps/wallet/browser/bridge/utils.ts | 2 + apps/wallet/browser/kernel/handlers/solana.ts | 12 +- .../browser/kernel/messaging/index.ext.ts | 94 ++---------- .../wallet/browser/kernel/utils/middleware.ts | 137 ++++++++++-------- .../src/screens/Requests/Signature/index.tsx | 23 +-- apps/wallet/src/utils/hooks/request.ts | 10 +- packages/sdk/providers/solana.ts | 4 +- 8 files changed, 130 insertions(+), 170 deletions(-) diff --git a/apps/wallet/browser/bridge/request.ts b/apps/wallet/browser/bridge/request.ts index 5d1e165fb..71f1787a8 100644 --- a/apps/wallet/browser/bridge/request.ts +++ b/apps/wallet/browser/bridge/request.ts @@ -65,18 +65,22 @@ export const handleRequestInstallLayout = async ( } }; -export const handleRequestSignature = async ( - options: PayloadOptions, - type: RequestType, -) => { - const payload: PopupPayload = { +export const handleRequestSignature = async (options: PayloadOptions) => { + const { sourceRequestId } = options; + const payload: RawRequest = { from: PopupType.SIGNATURE_POPUP, - type, + type: RequestType.RESOLVE_REQUEST_SIGNATURE as never, + resolveId: sourceRequestId, ...options, }; try { - return await encryptedMessenger.request(Channels.kernel, payload, 10000); + const chromeChannel = new ChromeChannel(Channels.popup); + const res = await chromeChannel.request>( + payload, + Timeout.sixtySeconds, + ); + return res; } catch (error) { throw Error('Unable to handle sign message request'); } diff --git a/apps/wallet/browser/bridge/utils.ts b/apps/wallet/browser/bridge/utils.ts index 661ddc727..a88b5c941 100644 --- a/apps/wallet/browser/bridge/utils.ts +++ b/apps/wallet/browser/bridge/utils.ts @@ -1,3 +1,4 @@ +import type { Networks } from '@walless/core'; import { logger } from '@walless/core'; import type { PureMessagePayload } from '@walless/messaging'; import { createEncryptionKeyVault, createMessenger } from '@walless/messaging'; @@ -7,6 +8,7 @@ export interface PayloadOptions { sourceRequestId: string; isApproved?: boolean; passcode?: string; + network?: Networks; } export type PopupPayload = PureMessagePayload & PayloadOptions; diff --git a/apps/wallet/browser/kernel/handlers/solana.ts b/apps/wallet/browser/kernel/handlers/solana.ts index 90062d58f..20fe874c3 100644 --- a/apps/wallet/browser/kernel/handlers/solana.ts +++ b/apps/wallet/browser/kernel/handlers/solana.ts @@ -1,3 +1,4 @@ +import type { Middleware } from '@metacraft/crab/core'; import { clusterApiUrl, Connection } from '@solana/web3.js'; import { ResponseCode } from '@walless/core'; import { solana } from '@walless/network'; @@ -12,10 +13,7 @@ const connection = new Connection( : clusterApiUrl('devnet'), ); -export const signMessage: HandleMethod<{ - privateKey?: Uint8Array; - message?: string; -}> = async ({ payload }) => { +export const signMessage: Middleware = async (payload, respond) => { if (!payload.privateKey || !payload.message) { throw Error('Missing privateKey or message'); } @@ -25,7 +23,11 @@ export const signMessage: HandleMethod<{ payload.privateKey, ); - respond(payload.requestId, ResponseCode.SUCCESS, { signature }); + respond({ + signature, + message: 'Successfully sign message', + responseCode: ResponseCode.SUCCESS, + }); }; export const signTransaction: HandleMethod<{ diff --git a/apps/wallet/browser/kernel/messaging/index.ext.ts b/apps/wallet/browser/kernel/messaging/index.ext.ts index 8936a7f40..22a4f8d84 100644 --- a/apps/wallet/browser/kernel/messaging/index.ext.ts +++ b/apps/wallet/browser/kernel/messaging/index.ext.ts @@ -1,91 +1,12 @@ import { ChromeKernel } from '@metacraft/crab/chrome'; -import { - PopupType, - RequestType, - ResponseCode, - ResponseMessage, - runtime, -} from '@walless/core'; -import type { - EncryptedMessage, - MessagePayload, - MessengerCallback, -} from '@walless/messaging'; -import { Channels, decryptMessage } from '@walless/messaging'; +import { RequestType } from '@walless/core'; +import { Channels } from '@walless/messaging'; import * as common from '../handlers/common'; -import { onKernelMessage } from '../handlers/kernel'; -import { checkConnection } from '../utils/middleware'; -import { respond } from '../utils/requestPool'; +import * as solana from '../handlers/solana'; +import { checkConnection, requestUserSignature } from '../utils/middleware'; -import { encryptionKeyVault, initializeVaultKeys } from './shared'; - -/* Manually forward/coordinate communication using chrome.runtime.port messaging, - * included encryption support */ export const initializeMessaging = async (): Promise => { - await initializeVaultKeys(); - - const callbackRegistry: Record = {}; - - runtime.onConnect.addListener((port: chrome.runtime.Port) => { - const handleInComingMessage = async ( - message: EncryptedMessage | MessagePayload, - ) => { - const registeredCallback = callbackRegistry[port.name]; - const isEncrypted = !!message?.iv; - - if (registeredCallback) { - if (isEncrypted) { - const key = await encryptionKeyVault.get(port.name); - const decrypted = await decryptMessage( - message as EncryptedMessage, - key, - ); - registeredCallback?.(decrypted, port); - } else { - registeredCallback?.(message as never, port); - } - } - }; - - const handleDisconnect = (port: chrome.runtime.Port) => { - if (port.name.includes('/')) { - const [id, requestId] = port.name.split('/'); - if (id === PopupType.REQUEST_CONNECT_POPUP) { - try { - respond(requestId, ResponseCode.ERROR, { - error: ResponseMessage.REJECT_REQUEST_CONNECT, - }); - } catch (error) { - return; - } - } else if (id === PopupType.SIGNATURE_POPUP) { - try { - respond(requestId, ResponseCode.ERROR, { - error: ResponseMessage.REJECT_COMMON_REQUEST, - }); - } catch (error) { - return; - } - } else if (id === PopupType.REQUEST_INSTALL_LAYOUT_POPUP) { - try { - respond(requestId, ResponseCode.ERROR); - } catch (error) { - return; - } - } - } - - port.onMessage.removeListener(handleInComingMessage); - port.onDisconnect.removeListener(handleDisconnect); - }; - - port.onMessage.addListener(handleInComingMessage); - port.onDisconnect.addListener(handleDisconnect); - }); - - callbackRegistry.kernel = onKernelMessage; - const kernel = new ChromeKernel(); kernel .channel(Channels.kernel) @@ -94,12 +15,17 @@ export const initializeMessaging = async (): Promise => { .use(common.connect) .handle(RequestType.REQUEST_DISCONNECT) .use(common.disconnect) + .handle(RequestType.SIGN_MESSAGE_ON_SOLANA) + .use(requestUserSignature(kernel)) + .use(solana.signMessage) .channel(Channels.popup) .handle(RequestType.REQUEST_PAYLOAD) .use(common.requestPayload(kernel)) .handle(RequestType.REQUEST_CONNECT) .use(kernel.handleCrossResolvingMiddleware) + .handle(RequestType.RESOLVE_REQUEST_SIGNATURE) + .use(kernel.handleCrossResolvingMiddleware) + .run(); - console.log(kernel, '<<< kerenel'); }; diff --git a/apps/wallet/browser/kernel/utils/middleware.ts b/apps/wallet/browser/kernel/utils/middleware.ts index 49b8f248e..d851739fe 100644 --- a/apps/wallet/browser/kernel/utils/middleware.ts +++ b/apps/wallet/browser/kernel/utils/middleware.ts @@ -1,5 +1,5 @@ import type { ChromeKernel } from '@metacraft/crab/chrome'; -import type { Middleware, RawRequest } from '@metacraft/crab/core'; +import type { Middleware, RawRequest, RawResponse } from '@metacraft/crab/core'; import type { RequestType } from '@walless/core'; import type { Networks } from '@walless/core'; import { @@ -18,29 +18,84 @@ import { openPopup } from './popup'; import { getRequestRecord, respond } from './requestPool'; import type { HandleMethod } from './types'; -export const getPrivateKey = ( - network: Networks, -): HandleMethod<{ passcode?: string }> => { - return async ({ payload, next }) => { - const passcode = payload.passcode; - if (!passcode) { - return respond(payload.requestId, ResponseCode.REQUIRE_PASSCODE); - } +const handleUserAction = async ({ + kernel, + payload, + popupType, + callback, +}: { + kernel: ChromeKernel; + payload: any; + popupType: PopupType; + callback?: ( + ...args: unknown[] + ) => Promise<{ response: RawResponse } & Record>; +}) => { + const { resolveId, resolve } = kernel.createCrossResolvingRequest( + payload.requestId, + payload.timeout || Timeout.sixtySeconds, + callback, + ); - try { - const privateKey = await utils.getPrivateKey(storage, network, passcode); - next?.({ ...payload, privateKey }); - } catch { - respond(payload.requestId, ResponseCode.WRONG_PASSCODE); + openPopup(popupType, resolveId); + + return await resolve>(); +}; + +const handlePopupResponse = async ({ + passcode, + isApproved, + network, +}: { + passcode: string; + isApproved: boolean; + network: Networks; +}): Promise<{ response: RawResponse } & Record> => { + if (!network) throw new Error('Required network'); + if (!isApproved) { + return { + response: { + error: 'User rejected', + message: 'User has rejected the signature request', + responseCode: ResponseCode.ERROR, + }, + }; + } else { + const privateKey = await getPrivateKey(network, passcode); + if (privateKey) { + return { + response: { + message: 'ok', + responseCode: ResponseCode.SUCCESS, + }, + isApproved, + privateKey, + }; + } else { + return { + response: { + error: 'Wrong passcode', + message: 'Failed to decode with passcode', + responseCode: ResponseCode.WRONG_PASSCODE, + }, + }; } - }; + } +}; + +export const getPrivateKey = async (network: Networks, passcode: string) => { + try { + return await utils.getPrivateKey(storage, network, passcode); + } catch { + return; + } }; export const checkConnection = ( kernel: ChromeKernel, ): Middleware => { return async (payload, respond, next) => { - if (!payload.options) throw Error('No connection options provided'); + if (!payload.options) throw Error('No connection options provided <<<'); const { onlyIfTrusted, domain } = payload.options; if (!onlyIfTrusted) { @@ -79,62 +134,24 @@ export const checkConnection = ( }; }; -const handleUserAction = async ({ - kernel, - payload, - popupType, -}: { - kernel: ChromeKernel; - payload: any; - popupType: PopupType; -}) => { - const { resolveId, resolve } = kernel.createCrossResolvingRequest( - payload.requestId, - payload.timeout || Timeout.sixtySeconds, - ); - - openPopup(popupType, resolveId); - - return await resolve>(); -}; - -// export const checkApproval = ( -// kernel: ChromeKernel, -// ): Middleware => { -// return async (payload, respond) => { -// const { resolveId, isApproved } = payload; - -// if (!isApproved) { -// kernel.handleCrossResolvingMiddleware(payload); -// respond(sourceRequestId, ResponseCode.ERROR, { -// message: ResponseMessage.REJECT_REQUEST_CONNECT, -// }); -// } else { -// payload = getRequestRecord(sourceRequestId).payload; -// await next?.(payload); -// } - -// respond(requestId, ResponseCode.SUCCESS); -// }; -// }; - -export const requestUserPermission = ( +export const requestUserSignature = ( kernel: ChromeKernel, ): Middleware => { return async (payload, respond, next) => { - if (!payload.options) throw Error('No connection options provided'); - - const { passcode, isApproved } = await handleUserAction<{ + const { isApproved, privateKey } = await handleUserAction<{ passcode: string; isApproved: boolean; + privateKey: Uint8Array; }>({ kernel, payload, popupType: PopupType.SIGNATURE_POPUP, + callback: handlePopupResponse as never, }); + console.log('handle user request sign >>>', isApproved, privateKey); if (isApproved) { - next?.({ ...payload, passcode }); + next?.({ ...payload, privateKey }); } else { respond({ requestId: payload.id, diff --git a/apps/wallet/src/screens/Requests/Signature/index.tsx b/apps/wallet/src/screens/Requests/Signature/index.tsx index 132b4fa55..0e51dbe4c 100644 --- a/apps/wallet/src/screens/Requests/Signature/index.tsx +++ b/apps/wallet/src/screens/Requests/Signature/index.tsx @@ -2,11 +2,9 @@ import type { FC } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react'; import { StyleSheet } from 'react-native'; import type { StackScreenProps } from '@react-navigation/stack'; -import type { RequestType } from '@walless/core'; import { Networks, PopupType } from '@walless/core'; import type { SlideOption } from '@walless/gui'; import { Slider } from '@walless/gui'; -import type { ResponsePayload } from '@walless/messaging'; import { handleRequestSignature } from 'bridge'; import { initializeKernelConnect } from 'bridge/helpers'; import type { PayloadOptions } from 'bridge/utils'; @@ -19,21 +17,22 @@ import RequestSignaturePasscode from './Passcode'; type Props = StackScreenProps; export const RequestSignature: FC = ({ route }) => { - const { requestId } = route.params; - const { sender, message, transaction, type } = useRequestData( - requestId as string, + const { resolveId } = route.params; + const { sender, message, transaction, type, network } = useRequestData( + resolveId as string, PopupType.SIGNATURE_POPUP, ); const [activeIndex, setActiveIndex] = useState(0); const options = useRef({ - sourceRequestId: requestId as string, + sourceRequestId: resolveId as string, isApproved: false, passcode: '', + network, }); const publicKeys = usePublicKeys(Networks.solana)[0]; const handleDenyRequest = useCallback(async () => { - await handleRequestSignature(options.current, type as RequestType); + await handleRequestSignature(options.current); window.close(); }, [type]); @@ -43,9 +42,11 @@ export const RequestSignature: FC = ({ route }) => { }; const handleResolveRequest = useCallback( - async (passcode: string): Promise => { + async (passcode: string) => { options.current.passcode = passcode; - return await handleRequestSignature(options.current, type as RequestType); + options.current.network = network; + const res = await handleRequestSignature(options.current); + return res; }, [type], ); @@ -68,14 +69,14 @@ export const RequestSignature: FC = ({ route }) => { component: () => ( ), }, ]; useEffect(() => { - initializeKernelConnect(`${PopupType.SIGNATURE_POPUP}/${requestId}`); + initializeKernelConnect(PopupType.SIGNATURE_POPUP + '/' + resolveId); }, []); return ( diff --git a/apps/wallet/src/utils/hooks/request.ts b/apps/wallet/src/utils/hooks/request.ts index 64eefd6e3..94865e1c2 100644 --- a/apps/wallet/src/utils/hooks/request.ts +++ b/apps/wallet/src/utils/hooks/request.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import type { UnknownObject } from '@walless/core'; +import type { Networks, UnknownObject } from '@walless/core'; import { runtime } from '@walless/core'; import type { MessagePayload } from '@walless/messaging'; import type { ConnectOptions } from '@walless/sdk'; @@ -11,11 +11,12 @@ export const useRequestData = (resolveId: string, from: string) => { const [message, setMessage] = useState(''); const [transaction, setTransaction] = useState(''); const [payload, setPayload] = useState(); + const [network, setNetwork] = useState(); useEffect(() => { const configureSender = async () => { const result = await getDataFromSourceRequest(resolveId, from); - const { sender, message, transaction, options } = result ?? {}; + const { sender, message, transaction, options, network } = result ?? {}; setPayload(result as MessagePayload); @@ -34,6 +35,10 @@ export const useRequestData = (resolveId: string, from: string) => { if (options) { setOptions(options); } + + if (network) { + setNetwork(network); + } }; if (runtime.isExtension) { @@ -47,5 +52,6 @@ export const useRequestData = (resolveId: string, from: string) => { message, transaction, options, + network, }; }; diff --git a/packages/sdk/providers/solana.ts b/packages/sdk/providers/solana.ts index 56a40555f..9fd96e0cd 100644 --- a/packages/sdk/providers/solana.ts +++ b/packages/sdk/providers/solana.ts @@ -1,4 +1,4 @@ -import { RequestType, Timeout } from '@walless/core'; +import { Networks, RequestType, Timeout } from '@walless/core'; import { sendRequest } from '../utils/messaging'; @@ -23,6 +23,7 @@ export const requestSignMessage = async (message: string) => { from: 'walless@sdk', type: RequestType.SIGN_MESSAGE_ON_SOLANA, message, + network: Networks.solana, }, Timeout.sixtySeconds, ); @@ -34,6 +35,7 @@ export const requestSignTransaction = async (transaction: string) => { from: 'walless@sdk', type: RequestType.SIGN_TRANSACTION_ON_SOLANA, transaction, + network: Networks.solana, }, Timeout.sixtySeconds, ); From 0e81a000375af04e14e9c2706327a9188fa64b93 Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Wed, 12 Jun 2024 13:00:30 +0700 Subject: [PATCH 7/8] chore: temporary comment out for referencing --- apps/wallet/browser/kernel/handlers/kernel.ts | 262 +++++++++--------- 1 file changed, 130 insertions(+), 132 deletions(-) diff --git a/apps/wallet/browser/kernel/handlers/kernel.ts b/apps/wallet/browser/kernel/handlers/kernel.ts index b04ea5ac5..2bffaf5d3 100644 --- a/apps/wallet/browser/kernel/handlers/kernel.ts +++ b/apps/wallet/browser/kernel/handlers/kernel.ts @@ -1,135 +1,133 @@ -import { Networks, RequestType, ResponseCode } from '@walless/core'; -import type { MessengerCallback } from '@walless/messaging'; +// import { Networks, RequestType, ResponseCode } from '@walless/core'; +// import type { MessengerCallback } from '@walless/messaging'; -import { handle } from '../utils/handle'; -import { - filterSDKSignatureRequest, - forwardToSourceRequest, - getPrivateKey, -} from '../utils/middleware'; -import { addRequestRecord } from '../utils/requestPool'; +// import { handle } from '../utils/handle'; +// import { +// filterSDKSignatureRequest, +// forwardToSourceRequest, +// getPrivateKey, +// } from '../utils/middleware'; +// import { addRequestRecord } from '../utils/requestPool'; -import * as aptos from './aptos'; -import * as common from './common'; -import * as solana from './solana'; -import * as sui from './sui'; -import * as tezos from './tezos'; +// import * as aptos from './aptos'; +// import * as common from './common'; +// import * as solana from './solana'; +// import * as sui from './sui'; +// import * as tezos from './tezos'; -export const onKernelMessage: MessengerCallback = async (payload, channel) => { - addRequestRecord(payload.requestId, channel, payload); - const { type, requestId } = payload; - - if (!requestId) { - return channel.postMessage({ - from: 'walless@kernel', - requestId: requestId, - responseCode: ResponseCode.ERROR, - error: 'No request id provided!', - }); - } - - if (requestId) { - switch (type) { - case RequestType.REQUEST_CONNECT: - case RequestType.REQUEST_DISCONNECT: - case RequestType.REQUEST_PAYLOAD: - return; - } - } - - // if (type === RequestType.REQUEST_CONNECT && payload.from === 'walless@sdk') { - // handle(payload).execute([checkConnection, common.connect]); - // } else if ( - // type === RequestType.REQUEST_CONNECT && - // payload.from === PopupType.REQUEST_CONNECT_POPUP - // ) { - // handle(payload).execute([checkApproval, common.connect]); - // } else if (type === RequestType.REQUEST_DISCONNECT) { - // handle(payload).execute([common.disconnect]); - // } else if (type === RequestType.REQUEST_PAYLOAD) { - // handle(payload).execute([common.requestPayload]); - // } else - if (type === RequestType.INSTALL_LAYOUT) { - handle(payload).execute([common.installLayout]); - } else if (type === RequestType.CHECK_INSTALLED_LAYOUT) { - handle(payload).execute([common.checkInstalledLayout]); - } else if (type === RequestType.OPEN_LAYOUT_POPUP) { - handle(payload).execute([common.openLayoutPopup]); - } else if (type === RequestType.SIGN_MESSAGE_ON_SOLANA) { - handle(payload).execute([ - filterSDKSignatureRequest, - getPrivateKey(Networks.solana), - forwardToSourceRequest, - solana.signMessage, - ]); - } else if (type === RequestType.SIGN_TRANSACTION_ON_SOLANA) { - handle(payload).execute([ - filterSDKSignatureRequest, - getPrivateKey(Networks.solana), - forwardToSourceRequest, - solana.signTransaction, - ]); - } else if (type === RequestType.SIGN_SEND_TRANSACTION_ON_SOLANA) { - handle(payload).execute([ - filterSDKSignatureRequest, - getPrivateKey(Networks.solana), - forwardToSourceRequest, - solana.signAndSendTransaction, - ]); - } else if (type === RequestType.SIGN_GASILON_TRANSACTION_ON_SOLANA) { - handle(payload).execute([ - getPrivateKey(Networks.solana), - solana.signAndSendGasilonTransaction, - ]); - } else if (type === RequestType.SIGN_MESSAGE_ON_SUI) { - handle(payload).execute([ - filterSDKSignatureRequest, - getPrivateKey(Networks.sui), - forwardToSourceRequest, - sui.signMessage, - ]); - } else if (type === RequestType.SIGN_TRANSACTION_ON_SUI) { - handle(payload).execute([ - filterSDKSignatureRequest, - getPrivateKey(Networks.sui), - forwardToSourceRequest, - sui.signTransaction, - ]); - } else if (type === RequestType.SIGN_EXECUTE_TRANSACTION_ON_SUI) { - handle(payload).execute([ - filterSDKSignatureRequest, - getPrivateKey(Networks.sui), - forwardToSourceRequest, - sui.signAndExecuteTransaction, - ]); - } else if (payload.type === RequestType.TRANSFER_TEZOS_TOKEN) { - handle(payload).execute([ - getPrivateKey(Networks.tezos), - tezos.transferToken, - ]); - } else if (type === RequestType.TRANSFER_COIN_ON_APTOS) { - handle(payload).execute([ - getPrivateKey(Networks.aptos), - aptos.transferCoin, - ]); - } else if (type === RequestType.TRANSFER_TOKEN_ON_APTOS) { - handle(payload).execute([ - getPrivateKey(Networks.aptos), - aptos.transferToken, - ]); - } else if (type === RequestType.CLAIM_TOKEN_ON_APTOS) { - handle(payload).execute([getPrivateKey(Networks.aptos), aptos.claimToken]); - } else if (type === RequestType.UPDATE_DIRECT_TRANSFER_ON_APTOS) { - handle(payload).execute([ - getPrivateKey(Networks.aptos), - aptos.updateDirectTransfer, - ]); - } else { - return channel.postMessage({ - from: 'walless@kernel', - requestId: payload.requestId, - responseCode: ResponseCode.ERROR, - error: 'Invalid request type!', - }); - } -}; +// export const onKernelMessage: MessengerCallback = async (payload, channel) => { +// addRequestRecord(payload.requestId, channel, payload); +// const { type, requestId } = payload; +// if (!requestId) { +// return channel.postMessage({ +// from: 'walless@kernel', +// requestId: requestId, +// responseCode: ResponseCode.ERROR, +// error: 'No request id provided!', +// }); +// } +// if (requestId) { +// switch (type) { +// case RequestType.REQUEST_CONNECT: +// case RequestType.REQUEST_DISCONNECT: +// case RequestType.REQUEST_PAYLOAD: +// case RequestType.SIGN_MESSAGE_ON_SOLANA: +// return; +// } +// } +// if (type === RequestType.REQUEST_CONNECT && payload.from === 'walless@sdk') { +// handle(payload).execute([checkConnection, common.connect]); +// } else if ( +// type === RequestType.REQUEST_CONNECT && +// payload.from === PopupType.REQUEST_CONNECT_POPUP +// ) { +// handle(payload).execute([checkApproval, common.connect]); +// } else if (type === RequestType.REQUEST_DISCONNECT) { +// handle(payload).execute([common.disconnect]); +// } else if (type === RequestType.REQUEST_PAYLOAD) { +// handle(payload).execute([common.requestPayload]); +// } else if (type === RequestType.INSTALL_LAYOUT) { +// handle(payload).execute([common.installLayout]); +// } else if (type === RequestType.CHECK_INSTALLED_LAYOUT) { +// handle(payload).execute([common.checkInstalledLayout]); +// } else if (type === RequestType.OPEN_LAYOUT_POPUP) { +// handle(payload).execute([common.openLayoutPopup]); +// } +// else if (type === RequestType.SIGN_MESSAGE_ON_SOLANA) { +// handle(payload).execute([ +// filterSDKSignatureRequest, +// getPrivateKey(Networks.solana), +// forwardToSourceRequest, +// solana.signMessage, +// ]); +// } else if (type === RequestType.SIGN_TRANSACTION_ON_SOLANA) { +// handle(payload).execute([ +// filterSDKSignatureRequest, +// getPrivateKey(Networks.solana), +// forwardToSourceRequest, +// solana.signTransaction, +// ]); +// } else if (type === RequestType.SIGN_SEND_TRANSACTION_ON_SOLANA) { +// handle(payload).execute([ +// filterSDKSignatureRequest, +// getPrivateKey(Networks.solana), +// forwardToSourceRequest, +// solana.signAndSendTransaction, +// ]); +// } else if (type === RequestType.SIGN_GASILON_TRANSACTION_ON_SOLANA) { +// handle(payload).execute([ +// getPrivateKey(Networks.solana), +// solana.signAndSendGasilonTransaction, +// ]); +// } else if (type === RequestType.SIGN_MESSAGE_ON_SUI) { +// handle(payload).execute([ +// filterSDKSignatureRequest, +// getPrivateKey(Networks.sui), +// forwardToSourceRequest, +// sui.signMessage, +// ]); +// } else if (type === RequestType.SIGN_TRANSACTION_ON_SUI) { +// handle(payload).execute([ +// filterSDKSignatureRequest, +// getPrivateKey(Networks.sui), +// forwardToSourceRequest, +// sui.signTransaction, +// ]); +// } else if (type === RequestType.SIGN_EXECUTE_TRANSACTION_ON_SUI) { +// handle(payload).execute([ +// filterSDKSignatureRequest, +// getPrivateKey(Networks.sui), +// forwardToSourceRequest, +// sui.signAndExecuteTransaction, +// ]); +// } else if (payload.type === RequestType.TRANSFER_TEZOS_TOKEN) { +// handle(payload).execute([ +// getPrivateKey(Networks.tezos), +// tezos.transferToken, +// ]); +// } else if (type === RequestType.TRANSFER_COIN_ON_APTOS) { +// handle(payload).execute([ +// getPrivateKey(Networks.aptos), +// aptos.transferCoin, +// ]); +// } else if (type === RequestType.TRANSFER_TOKEN_ON_APTOS) { +// handle(payload).execute([ +// getPrivateKey(Networks.aptos), +// aptos.transferToken, +// ]); +// } else if (type === RequestType.CLAIM_TOKEN_ON_APTOS) { +// handle(payload).execute([getPrivateKey(Networks.aptos), aptos.claimToken]); +// } else if (type === RequestType.UPDATE_DIRECT_TRANSFER_ON_APTOS) { +// handle(payload).execute([ +// getPrivateKey(Networks.aptos), +// aptos.updateDirectTransfer, +// ]); +// } else { +// return channel.postMessage({ +// from: 'walless@kernel', +// requestId: payload.requestId, +// responseCode: ResponseCode.ERROR, +// error: 'Invalid request type!', +// }); +// } +// }; From 31af25ce359c7883f7cbc08be72b709a0b2b3b60 Mon Sep 17 00:00:00 2001 From: FME849 <1414439@gmail.com> Date: Thu, 13 Jun 2024 12:38:48 +0700 Subject: [PATCH 8/8] update: refactor resolveCallback function, ready for messaging lib upgrade --- .../wallet/browser/kernel/utils/middleware.ts | 59 +++++++------------ 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/apps/wallet/browser/kernel/utils/middleware.ts b/apps/wallet/browser/kernel/utils/middleware.ts index d851739fe..5ec470356 100644 --- a/apps/wallet/browser/kernel/utils/middleware.ts +++ b/apps/wallet/browser/kernel/utils/middleware.ts @@ -1,5 +1,5 @@ import type { ChromeKernel } from '@metacraft/crab/chrome'; -import type { Middleware, RawRequest, RawResponse } from '@metacraft/crab/core'; +import type { Middleware, RawRequest } from '@metacraft/crab/core'; import type { RequestType } from '@walless/core'; import type { Networks } from '@walless/core'; import { @@ -22,19 +22,18 @@ const handleUserAction = async ({ kernel, payload, popupType, - callback, + resolveCallback, }: { kernel: ChromeKernel; + // eslint-disable-next-line payload: any; popupType: PopupType; - callback?: ( - ...args: unknown[] - ) => Promise<{ response: RawResponse } & Record>; + resolveCallback?: Middleware; }) => { const { resolveId, resolve } = kernel.createCrossResolvingRequest( payload.requestId, payload.timeout || Timeout.sixtySeconds, - callback, + resolveCallback, ); openPopup(popupType, resolveId); @@ -42,43 +41,27 @@ const handleUserAction = async ({ return await resolve>(); }; -const handlePopupResponse = async ({ - passcode, - isApproved, - network, -}: { - passcode: string; - isApproved: boolean; - network: Networks; -}): Promise<{ response: RawResponse } & Record> => { +const handlePopupResponse: Middleware = async (request, respond, resolve) => { + const { isApproved, passcode, network } = request; if (!network) throw new Error('Required network'); if (!isApproved) { - return { - response: { - error: 'User rejected', - message: 'User has rejected the signature request', - responseCode: ResponseCode.ERROR, - }, - }; + respond({ + message: 'User has rejected the signature request', + responseCode: ResponseCode.ERROR, + }); } else { const privateKey = await getPrivateKey(network, passcode); if (privateKey) { - return { - response: { - message: 'ok', - responseCode: ResponseCode.SUCCESS, - }, - isApproved, - privateKey, - }; + respond({ + message: 'Successfully sign request', + responseCode: ResponseCode.SUCCESS, + }); + resolve?.({ isApproved, privateKey }); } else { - return { - response: { - error: 'Wrong passcode', - message: 'Failed to decode with passcode', - responseCode: ResponseCode.WRONG_PASSCODE, - }, - }; + respond({ + message: 'Failed to decode with passcode', + responseCode: ResponseCode.WRONG_PASSCODE, + }); } } }; @@ -146,7 +129,7 @@ export const requestUserSignature = ( kernel, payload, popupType: PopupType.SIGNATURE_POPUP, - callback: handlePopupResponse as never, + resolveCallback: handlePopupResponse, }); console.log('handle user request sign >>>', isApproved, privateKey);