diff --git a/.gitignore b/.gitignore index e58a38fd2..f7bb37c9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +# editor .idea +.zed # automations .sst @@ -72,4 +74,4 @@ cli/tokens/**/cache.json cli/tokens/**/config.json cli/tokens/**/sugar.log -playground \ No newline at end of file +playground diff --git a/apps/wallet/browser/bridge/index.ts b/apps/wallet/browser/bridge/index.ts index 83d836edc..21cfe24c0 100644 --- a/apps/wallet/browser/bridge/index.ts +++ b/apps/wallet/browser/bridge/index.ts @@ -3,9 +3,8 @@ import { runtime } from '@walless/core'; import type { SettingDocument } from '@walless/store'; -import { storage } from 'utils/storage'; -import { injectServiceWorker, launchSignInTab } from './utils'; +import { injectServiceWorker, launchSignInTab, storage } from './utils'; export const runBridge = async (): Promise => { if (runtime.isExtension) { diff --git a/apps/wallet/browser/bridge/utils.ts b/apps/wallet/browser/bridge/utils.ts index 661ddc727..95e141f4c 100644 --- a/apps/wallet/browser/bridge/utils.ts +++ b/apps/wallet/browser/bridge/utils.ts @@ -1,7 +1,10 @@ import { logger } from '@walless/core'; import type { PureMessagePayload } from '@walless/messaging'; import { createEncryptionKeyVault, createMessenger } from '@walless/messaging'; -import { storage } from 'utils/storage/db'; +import { create } from '@walless/store'; +import IDBPouch from 'pouchdb-adapter-idb'; + +export const storage = create('engine', IDBPouch); export interface PayloadOptions { sourceRequestId: string; diff --git a/apps/wallet/browser/bundler/polyfill.js b/apps/wallet/browser/bundler/polyfill.js new file mode 100644 index 000000000..d00bdcc77 --- /dev/null +++ b/apps/wallet/browser/bundler/polyfill.js @@ -0,0 +1,8 @@ +global.chrome.runtime = Object.assign(global.chrome.runtime || {}, { + id: 'internal-walless-app', +}); + +export * from 'browser/content/utils'; +export const injectScript = () => { + require('browser/content/injection'); +}; diff --git a/apps/wallet/browser/bundler/utils.js b/apps/wallet/browser/bundler/utils.js new file mode 100644 index 000000000..9a1e307e0 --- /dev/null +++ b/apps/wallet/browser/bundler/utils.js @@ -0,0 +1,42 @@ +const path = require('path'); + +/** + * polyfill `chrome.runtime` usage of contentScript in web runtime + */ +module.exports.contentScriptPolyfill = (configs, { modules }) => { + const { webpack } = modules; + + configs.plugins.push( + new webpack.NormalModuleReplacementPlugin(/.\/utils/, (resource) => { + const contentScriptScope = 'browser/content'; + const isInContentScriptScope = + resource.contextInfo.issuer.includes(contentScriptScope); + + if (isInContentScriptScope && resource.request === './utils') { + resource.request = path.resolve(__dirname, './polyfill.js'); + } + }), + ); + + return configs; +}; + +/** + * experimental, dApp side need to support proxy request + */ +module.exports.widgetsProxy = (configs) => { + configs = { + ...configs, + proxy: { + '/widgets-proxy': { + target: 'http://127.0.0.1:3011', // tezos dApp host + pathRewrite: function () { + return '/'; + }, + changeOrigin: true, + }, + }, + }; + + return configs; +}; diff --git a/apps/wallet/browser/content/index.ts b/apps/wallet/browser/content/index.ts index 2efa10067..c0eb65021 100644 --- a/apps/wallet/browser/content/index.ts +++ b/apps/wallet/browser/content/index.ts @@ -1,10 +1,4 @@ -import { logger } from '@walless/core'; +import { initializeContentScript } from './init'; -import { initializeMessaging, injectScript } from './utils'; - -(async () => { - await initializeMessaging(); - logger.info('Messaging module initialized..'); - - injectScript('injection.js'); -})(); +console.log('init content script'); +initializeContentScript(); diff --git a/apps/wallet/browser/content/init.ts b/apps/wallet/browser/content/init.ts new file mode 100644 index 000000000..b2d258b99 --- /dev/null +++ b/apps/wallet/browser/content/init.ts @@ -0,0 +1,12 @@ +import { logger } from '@walless/core'; + +import { initializeTezosWallet } from './tezos'; +import { initializeMessaging, injectScript } from './utils'; + +export const initializeContentScript = async () => { + initializeTezosWallet(); + await initializeMessaging(); + logger.info('Messaging module initialized..'); + + injectScript('injection.js'); +}; diff --git a/apps/wallet/browser/content/tezos.ts b/apps/wallet/browser/content/tezos.ts new file mode 100644 index 000000000..ac3838a00 --- /dev/null +++ b/apps/wallet/browser/content/tezos.ts @@ -0,0 +1,278 @@ +import type { + OperationRequest, + PermissionRequest, + PostMessagePairingRequest, + PostMessagePairingResponse, + SignPayloadRequest, + SignPayloadResponse, +} from '@airgap/beacon-sdk'; +import { + BeaconMessageType, + decryptCryptoboxPayload, + encryptCryptoboxPayload, + ExtensionMessageTarget, + NetworkType, + PermissionScope, + sealCryptobox, +} from '@airgap/beacon-sdk'; +import type { UnknownObject } from '@walless/core'; +import { logger, Networks, RequestType } from '@walless/core'; +import { + createCryptoBoxClient, + createCryptoBoxServer, +} from '@walless/crypto/utils/p2p'; +import type { ConnectOptions } from '@walless/sdk'; + +import { + deserialize, + getDAppPublicKey, + getOrCreateKeypair, + messenger, + serialize, + storeDAppPublicKey, +} from './utils'; + +const TEZOS_PAIRING_REQUEST = 'postmessage-pairing-request'; +const TEZOS_PAIRING_RESPONSE = 'postmessage-pairing-response'; + +const ONE_MINUTE_TO_MS = 60000; + +export const WALLESS_TEZOS = { + id: chrome.runtime.id, + name: 'Walless', + iconUrl: 'https://walless.io/img/walless-icon.svg', + appUrl: 'https://walless.io', + version: '3', +}; + +let targetWindow = window; +export const resolveTargetWindow = (w: Window) => { + if (w) { + console.log('use external window'); + targetWindow = w as never; + } + targetWindow.postMessage('hello from Walless - Tezos wallet 2', '*'); +}; + +export let init = false; +export const initializeTezosWallet = () => { + console.log('init tezos wallet'); + if (init) { + logger.error('already initialized tezos wallet'); + return; + } + + init = true; + + window.addEventListener('message', async (e) => { + if (e.data?.type && e.data.type.includes('webpack')) { + return; + } + + console.log('tezos wallet - on message:', e.data, e.origin); + if (!e.data) return; + const { target, targetId } = e.data; + const isNotTezosRequest = target !== ExtensionMessageTarget.EXTENSION; + const wrongTarget = targetId && targetId !== WALLESS_TEZOS.id; + if (isNotTezosRequest || wrongTarget) return; + + if (e.data.payload === 'ping') return handlePingPong(); + + origin = e.origin; + if (e.data.payload) { + let payload = e.data.payload; + if (typeof payload === 'string') payload = deserialize(payload); + + if (payload.type === TEZOS_PAIRING_REQUEST) { + return handlePairingRequest(payload); + } else if (payload.type === BeaconMessageType.PermissionRequest) { + const response = await handlePermissionRequest(payload as never); + targetWindow.postMessage({ + message: { + target: ExtensionMessageTarget.PAGE, + payload: response, + }, + sender: { id: WALLESS_TEZOS.id }, + }); + } + } else if (e.data.encryptedPayload) { + handleEncryptedRequest(e.data.encryptedPayload); + } + }); +}; + +const handlePingPong = () => { + console.log('handle ping pong'); + targetWindow.postMessage({ + target: ExtensionMessageTarget.PAGE, + payload: 'pong', + sender: WALLESS_TEZOS, + }); +}; + +const handlePairingRequest = async (payload: PostMessagePairingRequest) => { + const keypair = await getOrCreateKeypair(origin, true); + const recipientPublicKey = await storeDAppPublicKey( + origin, + payload.publicKey, + ); + + const resPayload: PostMessagePairingResponse = { + type: TEZOS_PAIRING_RESPONSE, + publicKey: Buffer.from(keypair.publicKey).toString('hex'), + ...WALLESS_TEZOS, + }; + + const recipientPublicKeyBytes = Uint8Array.from( + Buffer.from(recipientPublicKey, 'hex'), + ); + + const encryptedPayload = await sealCryptobox( + JSON.stringify(resPayload), + recipientPublicKeyBytes, + ); + + targetWindow.postMessage({ + message: { + target: ExtensionMessageTarget.PAGE, + payload: encryptedPayload, + }, + sender: { id: WALLESS_TEZOS.id }, + }); +}; + +const handleEncryptedRequest = async (encryptedPayload: string) => { + if (typeof encryptedPayload !== 'string') return; + const payload = await decryptPayload(encryptedPayload); + if (!payload || !payload.type) return; + sendAckMessage(payload.id); + + let response; + if (payload.type === BeaconMessageType.PermissionRequest) { + response = await handlePermissionRequest(payload as never); + } else if (payload.type === BeaconMessageType.Disconnect) { + handleDisconnect(); + } else if (payload.type === BeaconMessageType.SignPayloadRequest) { + response = await handleSignPayloadRequest(payload as never); + } else if (payload.type === BeaconMessageType.OperationRequest) { + response = await handleOperationRequest(payload as never); + } else { + console.log('not support this type of request'); + } + + if (response) { + respondWithSharedKeyEncrypt(response); + } +}; + +const handlePermissionRequest = async (payload: PermissionRequest) => { + // TODO: check network is valid or not, throw error if not + const res = await messenger.request<{ options: ConnectOptions }>( + 'kernel', + { + from: 'walless@sdk', + type: RequestType.REQUEST_CONNECT, + options: { + network: Networks.tezos, + domain: origin, + onlyIfTrusted: true, + }, + }, + ONE_MINUTE_TO_MS, + ); + + const response = { + id: payload.id, + type: BeaconMessageType.PermissionResponse, + network: { type: NetworkType.MAINNET }, // TODO: handle custom networks + publicKey: res.publicKeys[0].publicKey, + scopes: [PermissionScope.OPERATION_REQUEST, PermissionScope.SIGN], + }; + + return response; +}; + +const handleSignPayloadRequest = async (payload: SignPayloadRequest) => { + const res = await messenger.request( + 'kernel', + { + from: 'walless@sdk', + type: RequestType.SIGN_PAYLOAD_ON_TEZOS, + payload: payload.payload, + signingType: payload.signingType, + }, + ONE_MINUTE_TO_MS, + ); + + const response: SignPayloadResponse = { + id: payload.id, + signature: res.signature, + signingType: payload.signingType, + type: BeaconMessageType.SignPayloadResponse, + senderId: payload.senderId, + version: '2', + }; + + return response; +}; + +const handleOperationRequest = async (payload: OperationRequest) => { + // TODO: need to implement + console.log(payload); +}; + +const sendAckMessage = async (requestId: string) => { + const response = { + type: BeaconMessageType.Acknowledge, + id: requestId, + }; + + return response; +}; + +const decryptPayload = async (encryptedPayload: string) => { + const keypair = await getOrCreateKeypair(origin); + const recipientPublicKey = await getDAppPublicKey(origin); + const sharedKey = await createCryptoBoxServer(recipientPublicKey, keypair); + try { + const payload = await decryptCryptoboxPayload( + Buffer.from(encryptedPayload, 'hex'), + sharedKey.receive, + ); + + const payloadJson = deserialize(payload); + return payloadJson; + } catch (e) { + // TODO: need to respond error to client side + console.log('error decrypting payload', e); + handleDisconnect(); + } +}; + +const respondWithSharedKeyEncrypt = async (payload: UnknownObject) => { + const keypair = await getOrCreateKeypair(origin); + const recipientPublicKey = await getDAppPublicKey(origin); + const sharedKey = await createCryptoBoxClient(recipientPublicKey, keypair); + const encryptedPayload = await encryptCryptoboxPayload( + serialize(payload), + sharedKey.send, + ); + + targetWindow.postMessage({ + message: { + target: ExtensionMessageTarget.PAGE, + encryptedPayload, + }, + sender: { id: WALLESS_TEZOS.id }, + }); +}; + +const handleDisconnect = () => { + messenger.request<{ options: ConnectOptions }>('kernel', { + from: 'walless@sdk', + type: RequestType.REQUEST_DISCONNECT, + options: { + domain: origin, + }, + }); +}; diff --git a/apps/wallet/browser/content/utils.ts b/apps/wallet/browser/content/utils.ts index aa65851b5..c90249c00 100644 --- a/apps/wallet/browser/content/utils.ts +++ b/apps/wallet/browser/content/utils.ts @@ -1,6 +1,16 @@ +import type { UnknownObject } from '@walless/core'; +import { + extractPublicKeyFromSecretKey, + generateKeyPair, +} from '@walless/crypto/utils/p2p'; import { createMessenger } from '@walless/messaging'; +import { create } from '@walless/store'; +import { decode, encode } from 'bs58check'; +import IDBPouch from 'pouchdb-adapter-idb'; -const messenger = createMessenger(); +export const storage = create('engine', IDBPouch); + +export const messenger = createMessenger(); export const initializeMessaging = async () => { window.postMessage({ from: 'walless-content-script-loaded' }); @@ -37,3 +47,65 @@ export const injectScript = (scriptUri: string) => { console.error('script injection failed.', error); } }; + +export const serialize = (data: UnknownObject): string => { + return encode(Buffer.from(JSON.stringify(data))); +}; + +export const deserialize = (encoded: string): UnknownObject => { + return JSON.parse(decode(encoded).toString()); +}; + +export const getOrCreateKeypair = async (origin: string, create = false) => { + if (!origin) throw Error('require origin'); + + const key = 'transport_secret_key:' + origin; + if (create) { + const keypair = generateKeyPair(); + chrome.storage.local.set({ + [key]: Buffer.from(keypair.secretKey).toString('hex'), + }); + + return keypair; + } else { + const result = await chrome.storage.local.get([key]); + const secretKeyString = result[key] as string; + + if (secretKeyString) { + const secretKey = new Uint8Array(Buffer.from(secretKeyString, 'hex')); + const keypair = { + publicKey: extractPublicKeyFromSecretKey(secretKey), + secretKey, + }; + + return keypair; + } else { + const keypair = generateKeyPair(); + chrome.storage.local.set({ + transport_secret_key: Buffer.from(keypair.secretKey).toString('hex'), + }); + + return keypair; + } + } +}; + +export const storeDAppPublicKey = async (origin: string, publicKey: string) => { + if (!origin) throw Error('require origin'); + const key = 'dapp_public_key:' + origin; + await chrome.storage.local.set({ [key]: publicKey }); + + return publicKey; +}; + +export const getDAppPublicKey = async (origin: string) => { + if (!origin) throw Error('require origin'); + + const key = 'dapp_public_key:' + origin; + const result = await chrome.storage.local.get([key]); + const publicKey = result[key]; + + if (!publicKey) throw Error('Not found dapp public key'); + + return publicKey; +}; diff --git a/apps/wallet/browser/kernel/handlers/aptos.ts b/apps/wallet/browser/kernel/handlers/aptos.ts index 244c03a39..0a9091024 100644 --- a/apps/wallet/browser/kernel/handlers/aptos.ts +++ b/apps/wallet/browser/kernel/handlers/aptos.ts @@ -1,8 +1,8 @@ import { ResponseCode } from '@walless/core'; import { aptos } from '@walless/network'; import { Network, Provider } from 'aptos'; -import { environment } from 'utils/config'; +import { environment } from '../utils/config'; import { respond } from '../utils/requestPool'; import type { HandleMethod } from '../utils/types'; diff --git a/apps/wallet/browser/kernel/handlers/common.ts b/apps/wallet/browser/kernel/handlers/common.ts index 4a41fdc0d..f02cbfb86 100644 --- a/apps/wallet/browser/kernel/handlers/common.ts +++ b/apps/wallet/browser/kernel/handlers/common.ts @@ -1,12 +1,11 @@ import { PublicKey } from '@solana/web3.js'; -import type { SuiPublicKey } from '@walless/core'; +import type { SuiPublicKey, TezosPublicKey } from '@walless/core'; import { Networks } from '@walless/core'; import { ResponseCode } from '@walless/core'; import type { ConnectOptions } from '@walless/sdk'; import type { PublicKeyDocument, TrustedDomainDocument } from '@walless/store'; import { selectors } from '@walless/store'; import { encode } from 'bs58'; -import { storage } from 'utils/storage/db'; import { addExtensionsById, @@ -14,6 +13,7 @@ import { } from '../utils/helper'; import { openPopup } from '../utils/popup'; import { getRequestRecord, respond } from '../utils/requestPool'; +import { storage } from '../utils/storage'; import type { HandleMethod } from '../utils/types'; export const connect: HandleMethod<{ options?: ConnectOptions }> = async ({ @@ -41,6 +41,7 @@ export const connect: HandleMethod<{ options?: ConnectOptions }> = async ({ const pkDocs = await storage.find(selectors.allKeys); const publicKeys = pkDocs.docs + .filter((k) => k.network === connectOptions.network) .map((publickey) => { switch (publickey.network) { case Networks.solana: { @@ -50,7 +51,6 @@ export const connect: HandleMethod<{ options?: ConnectOptions }> = async ({ network, }; } - case Networks.sui: { const { network, encodedPublicKey } = publickey as never as SuiPublicKey; @@ -59,6 +59,16 @@ export const connect: HandleMethod<{ options?: ConnectOptions }> = async ({ network, }; } + case Networks.tezos: { + const { + network, + meta: { publicKey }, + } = publickey as PublicKeyDocument; + return { + publicKey, + network, + }; + } } }) .filter((k) => !!k); diff --git a/apps/wallet/browser/kernel/handlers/kernel.ts b/apps/wallet/browser/kernel/handlers/kernel.ts index 20269d518..517a72adc 100644 --- a/apps/wallet/browser/kernel/handlers/kernel.ts +++ b/apps/wallet/browser/kernel/handlers/kernel.ts @@ -5,6 +5,7 @@ import { handle } from '../utils/handle'; import { checkApproval, checkConnection, + deserializePayloadToMessageOnTezos, filterSDKSignatureRequest, forwardToSourceRequest, getPrivateKey, @@ -94,6 +95,14 @@ export const onKernelMessage: MessengerCallback = async (payload, channel) => { forwardToSourceRequest, sui.signAndExecuteTransaction, ]); + } else if (payload.type === RequestType.SIGN_PAYLOAD_ON_TEZOS) { + handle(payload).execute([ + deserializePayloadToMessageOnTezos, + filterSDKSignatureRequest, + getPrivateKey(Networks.tezos), + forwardToSourceRequest, + tezos.signPayload, + ]); } else if (payload.type === RequestType.TRANSFER_TEZOS_TOKEN) { handle(payload).execute([ getPrivateKey(Networks.tezos), diff --git a/apps/wallet/browser/kernel/handlers/solana.ts b/apps/wallet/browser/kernel/handlers/solana.ts index 90062d58f..ed3b5381a 100644 --- a/apps/wallet/browser/kernel/handlers/solana.ts +++ b/apps/wallet/browser/kernel/handlers/solana.ts @@ -1,8 +1,8 @@ import { clusterApiUrl, Connection } from '@solana/web3.js'; import { ResponseCode } from '@walless/core'; import { solana } from '@walless/network'; -import { environment } from 'utils/config'; +import { environment } from '../utils/config'; import { respond } from '../utils/requestPool'; import type { HandleMethod } from '../utils/types'; diff --git a/apps/wallet/browser/kernel/handlers/sui.ts b/apps/wallet/browser/kernel/handlers/sui.ts index b36143a1a..2927fe3f2 100644 --- a/apps/wallet/browser/kernel/handlers/sui.ts +++ b/apps/wallet/browser/kernel/handlers/sui.ts @@ -1,8 +1,8 @@ import { getFullnodeUrl, SuiClient } from '@mysten/sui.js/client'; import { ResponseCode } from '@walless/core'; import { sui } from '@walless/network'; -import { environment } from 'utils/config'; +import { environment } from '../utils/config'; import { respond } from '../utils/requestPool'; import type { HandleMethod } from '../utils/types'; diff --git a/apps/wallet/browser/kernel/handlers/tezos.ts b/apps/wallet/browser/kernel/handlers/tezos.ts index 4b642ad51..49d38a135 100644 --- a/apps/wallet/browser/kernel/handlers/tezos.ts +++ b/apps/wallet/browser/kernel/handlers/tezos.ts @@ -1,8 +1,10 @@ +import { InMemorySigner } from '@taquito/signer'; import { TezosToolkit } from '@taquito/taquito'; import { ResponseCode } from '@walless/core'; import { tezos } from '@walless/network'; -import { environment } from 'utils/config'; +import { encode } from 'bs58'; +import { environment } from '../utils/config'; import { respond } from '../utils/requestPool'; import type { HandleMethod } from '../utils/types'; @@ -20,6 +22,21 @@ const tezosToolkit = new TezosToolkit( : tezosEndpoints.ghostnetTestnet, ); +export const signPayload: HandleMethod<{ + privateKey?: Uint8Array; + payload?: string; +}> = async ({ payload }) => { + if (!payload.privateKey || !payload.payload) { + throw Error('Missing privateKey or message'); + } + + const privateKey = encode(payload.privateKey); + const signer = await InMemorySigner.fromSecretKey(privateKey); + const { prefixSig } = await signer.sign(payload.payload as string); + + respond(payload.requestId, ResponseCode.SUCCESS, { signature: prefixSig }); +}; + export const transferToken: HandleMethod<{ privateKey?: Uint8Array; transaction?: string; diff --git a/apps/wallet/browser/kernel/index.ts b/apps/wallet/browser/kernel/index.ts index a92c1850d..7c788a8f0 100644 --- a/apps/wallet/browser/kernel/index.ts +++ b/apps/wallet/browser/kernel/index.ts @@ -1,9 +1,8 @@ import { logger, runtime } from '@walless/core'; import { configure, migrateDatabase } from '@walless/store'; -// TODO: need to initialize storage separately for kernel -import { storage } from 'utils/storage/db'; import { keepBackgroundAlive } from './utils/extension'; +import { storage } from './utils/storage'; import { initializeMessaging } from './messaging'; import { configurePWA } from './pwa'; diff --git a/apps/wallet/browser/kernel/messaging/shared.ts b/apps/wallet/browser/kernel/messaging/shared.ts index 98b4ac9f5..49e68ca18 100644 --- a/apps/wallet/browser/kernel/messaging/shared.ts +++ b/apps/wallet/browser/kernel/messaging/shared.ts @@ -1,5 +1,6 @@ import { Channels, createEncryptionKeyVault } from '@walless/messaging'; -import { storage } from 'utils/storage/db'; + +import { storage } from '../utils/storage'; export const channels = [ Channels.ui, diff --git a/apps/wallet/browser/kernel/utils/config.ts b/apps/wallet/browser/kernel/utils/config.ts new file mode 100644 index 000000000..4c9813dff --- /dev/null +++ b/apps/wallet/browser/kernel/utils/config.ts @@ -0,0 +1,28 @@ +export const w3aBaseUrl = + NETWORK_CLUSTER === 'mainnet' ? 'https:app.walless.io' : location.origin; + +export const environment = { + NETWORK_CLUSTER, + FIREBASE_API_KEY, + BUILD_TARGET, + BROWSER_CLIENT_ID, + EXTENSION_CLIENT_ID, + FIREBASE_AUTH_DOMAIN, + FIREBASE_PROJECT_ID, + FIREBASE_STORAGE_BUCKET, + FIREBASE_MESSAGING_SENDER_ID, + FIREBASE_APP_ID, + FIREBASE_MEASUREMENT_ID, + FIREBASE_VAPID_KEY, + GATEFI_ENDPOINT, + GATEFI_MERCHANT_ID, + WEB3AUTH_ID, + GRAPHQL_ENDPOINT, + PIXEVERSE_ENDPOINT, + PIXEVERSE_ORIGIN, + PIXEVERSE_URL, + SOLANA_CLUSTER_URL, + GASILON_ENDPOINT, + JUPITER_TOKENS_ENDPOINT, + JUPITER_API_ENDPOINT, +}; diff --git a/apps/wallet/browser/kernel/utils/helper.ts b/apps/wallet/browser/kernel/utils/helper.ts index 644181f20..466c68e13 100644 --- a/apps/wallet/browser/kernel/utils/helper.ts +++ b/apps/wallet/browser/kernel/utils/helper.ts @@ -5,9 +5,9 @@ import type { PrivateKeyDocument, PublicKeyDocument, } from '@walless/store'; -import { storage } from 'utils/storage/db'; import { mockLayoutCards } from './mockExtension'; +import { storage } from './storage'; export const getExtensionById = async (id: string) => { return mockLayoutCards.find((card) => card._id === id); diff --git a/apps/wallet/browser/kernel/utils/middleware.ts b/apps/wallet/browser/kernel/utils/middleware.ts index eee54e22b..8db3ee91a 100644 --- a/apps/wallet/browser/kernel/utils/middleware.ts +++ b/apps/wallet/browser/kernel/utils/middleware.ts @@ -9,10 +9,10 @@ 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 { storage } from './storage'; import type { HandleMethod } from './types'; export const getPrivateKey = ( @@ -47,13 +47,14 @@ export const checkConnection: HandleMethod<{ 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 ( + const isDuplicatedRequest = ele.payload.requestId !== payload.requestId && ele.payload.type === RequestType.REQUEST_CONNECT && - ele.payload.options.domain === domain - ) { + ele.payload.options.domain === domain; + if (isDuplicatedRequest) { respond(ele.payload.requestId, ResponseCode.ERROR); closePopup(ele.payload.popupId); } @@ -121,3 +122,33 @@ export const forwardToSourceRequest: HandleMethod<{ next?.(payload); } }; + +export const deserializePayloadToMessageOnTezos: HandleMethod<{ + payload?: string; + signingType?: string; +}> = ({ payload, next }) => { + if (payload.from === 'walless@sdk') { + if (!payload.payload || !payload.signingType) + throw Error('Missing payload or signingType'); + + let message: string; + try { + if (payload.signingType === 'raw') { + message = Buffer.from(payload.payload, 'hex').toString(); + payload.message = message; + } else if ( + payload.signingType === 'operation' || + payload.signingType === 'micheline' + ) { + message = Buffer.from(payload.payload, 'hex').toString(); + payload.message = message; + } else { + throw Error(`can not handle this signing type ${payload.signingType}`); + } + } catch (e) { + console.log('deserialize failed', e); + } + } + + next?.(payload); +}; diff --git a/apps/wallet/browser/kernel/utils/storage.ts b/apps/wallet/browser/kernel/utils/storage.ts new file mode 100644 index 000000000..34e817cd3 --- /dev/null +++ b/apps/wallet/browser/kernel/utils/storage.ts @@ -0,0 +1,4 @@ +import { create } from '@walless/store'; +import IDBPouch from 'pouchdb-adapter-idb'; + +export const storage = create('engine', IDBPouch); diff --git a/apps/wallet/metacraft.config.js b/apps/wallet/metacraft.config.js index ba367fe91..81d946df2 100644 --- a/apps/wallet/metacraft.config.js +++ b/apps/wallet/metacraft.config.js @@ -8,6 +8,10 @@ const { injectEnvironments, replaceExtensionArgonLinks, } = require('./browser/bundler/webpack'); +const { + contentScriptPolyfill, + widgetsProxy, +} = require('./browser/bundler/utils'); module.exports = { publicPath: () => process.env.PUBLIC_URL || '/', @@ -21,8 +25,9 @@ module.exports = { injectEntries, injectEnvironments, replaceExtensionArgonLinks, + contentScriptPolyfill, ], - devMiddlewares: [w3aDevRoute], + devMiddlewares: [w3aDevRoute, widgetsProxy], htmlPluginOptions: { chunks: ['app'], }, diff --git a/apps/wallet/package.json b/apps/wallet/package.json index 8688b0c38..c2bc66d76 100644 --- a/apps/wallet/package.json +++ b/apps/wallet/package.json @@ -1,6 +1,6 @@ { "name": "@walless/wallet", - "version": "1.1.5", + "version": "1.1.6", "private": true, "scripts": { "dev": "metacraft", @@ -18,6 +18,8 @@ "test": "jest --passWithNoTests" }, "dependencies": { + "@airgap/beacon-sdk": "^4.2.1", + "@airgap/beacon-utils": "^4.2.1", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@coral-xyz/anchor": "^0.29.0", "@craftzdog/pouchdb-collate-react-native": "^7.3.0", diff --git a/apps/wallet/src/features/Widget/TezosDApp/index.tsx b/apps/wallet/src/features/Widget/TezosDApp/index.tsx new file mode 100644 index 000000000..25f842192 --- /dev/null +++ b/apps/wallet/src/features/Widget/TezosDApp/index.tsx @@ -0,0 +1,28 @@ +import { useRef } from 'react'; +import { StyleSheet } from 'react-native'; + +export const TezosDApp = () => { + const iframeRef = useRef(null); + + const handleOnLoad = () => { + iframeRef.current?.contentWindow?.focus(); + }; + + return ( + + ); +}; + +export default TezosDApp; + +const styles = StyleSheet.create({ + container: { + flex: 1, + borderColor: 'transparent', + }, +}); diff --git a/apps/wallet/src/features/Widget/TezosDApp/index.web.tsx b/apps/wallet/src/features/Widget/TezosDApp/index.web.tsx new file mode 100644 index 000000000..b3a8ef497 --- /dev/null +++ b/apps/wallet/src/features/Widget/TezosDApp/index.web.tsx @@ -0,0 +1,43 @@ +import { useEffect, useRef } from 'react'; +import { StyleSheet } from 'react-native'; +import { + initializeTezosWallet, + resolveTargetWindow, +} from 'browser/content/tezos'; + +export const TezosDApp = () => { + const iframeRef = useRef(null); + + const handleOnLoad = () => { + iframeRef.current?.contentWindow?.focus(); + }; + + useEffect(() => { + if (!iframeRef.current) { + console.log('not found ref'); + return; + } + console.log('resolve target window', iframeRef.current.contentWindow); + resolveTargetWindow(iframeRef.current.contentWindow as never); + initializeTezosWallet(); + }, [iframeRef]); + + return ( +