Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add logger to apps/connector #643

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
add apps/connector logger
felicio committed Jan 13, 2025
commit d8d683ae37a32fa39382f612da932d2f0bd2cac0
5 changes: 5 additions & 0 deletions .changeset/sharp-cheetahs-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'connector': patch
---

add logger
8 changes: 8 additions & 0 deletions apps/connector/README.md
Original file line number Diff line number Diff line change
@@ -102,3 +102,11 @@ FLAG_CONNECTOR_ENABLED=1 '/Volumes/Status Desktop/Status.app/Contents/MacOS/nim_
## Testing

Download latest build from last merged PR or build from source. To use the extension see the load steps from [Development](#development) section.

### Logging

Browser > Console

```
await connector.enableLogging(true)
```
5 changes: 5 additions & 0 deletions apps/connector/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -4,6 +4,11 @@ import configs, { tailwindcssConfigs } from '@status-im/eslint-config'
export default [
...configs,
...tailwindcssConfigs,
{
rules: {
'no-console': 'error',
},
},
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
1 change: 1 addition & 0 deletions apps/connector/global.d.ts
Original file line number Diff line number Diff line change
@@ -3,5 +3,6 @@ import type { Provider } from './src/contents/provider'
declare global {
interface Window {
ethereum: Provider
registration?: ServiceWorkerRegistration
}
}
8 changes: 8 additions & 0 deletions apps/connector/src/background.ts
Original file line number Diff line number Diff line change
@@ -46,3 +46,11 @@ chrome.action.onClicked.addListener(async tab => {
type: 'status:icon:clicked',
} satisfies ServiceWorkerMessage)
})

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.method === 'toggleLogging') {
storage.set('status:logging', message.params[0])

sendResponse(message.params[0])
}
})
73 changes: 73 additions & 0 deletions apps/connector/src/contents/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { ProxyMessage } from '~messages/proxy-message'

import type { RequestArguments } from '~lib/request-arguments'
import type { PlasmoCSConfig } from 'plasmo'

export const config: PlasmoCSConfig = {
matches: ['https://*/*'],
world: 'MAIN',
run_at: 'document_start',
all_frames: false,
}

Object.defineProperties(window, {
connector: {
value: {
enableLogging: (value = true) => {
// shared web storage with host page and content scripts (Web Storage API)
window.localStorage.setItem('status:logging', JSON.stringify(value))

// service worker extension storage (extension storage API)
request({
method: 'toggleLogging',
params: [value],
})

return value
},
},
configurable: false,
writable: false,
},
})

async function request(args: RequestArguments) {
const { method, params } = args

const messageChannel = new MessageChannel()

return new Promise((resolve, reject) => {
messageChannel.port1.onmessage = ({ data }) => {
try {
const message = ProxyMessage.parse(data)

messageChannel.port1.close()

switch (message.type) {
case 'status:proxy:success': {
resolve(message.data)

return
}
case 'status:proxy:error': {
reject(new Error(message.error.message))

return
}
}
} catch {
return
}
}

const mainMessage = {
type: 'status:main',
data: {
method,
params,
},
}

window.postMessage(mainMessage, window.origin, [messageChannel.port2])
})
}
21 changes: 19 additions & 2 deletions apps/connector/src/contents/provider.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { logger } from '~lib/logger'
import { ProviderRpcError } from '~lib/provider-rpc-error'
import { RequestArguments } from '~lib/request-arguments'
import { ProxyMessage } from '~messages/proxy-message'
@@ -96,18 +97,24 @@ export class Provider {
this.#listeners.get('connect')?.({ chainId: '0x1' })
this.#listeners.get('connected')?.({ chainId: '0x1' })
this.connected = true

logger.info('connected::')
}

if (method === 'wallet_switchEthereumChain') {
this.#listeners.get('chainChanged')?.(message.data)
this.#listeners.get('networkChanged')?.(message.data)

logger.info('chainChanged::')
}

resolve(message.data)

return
}
case 'status:proxy:error': {
logger.error(message.error)

// note: for those dApps that make call after having permissions revoked
if (
message.error.message === 'dApp is not permitted by user' &&
@@ -148,15 +155,21 @@ export class Provider {
}

public on(event: Event, handler: (args: unknown) => void): void {
logger.info('on::', event, handler)

this.#listeners.set(event, handler)
}

/** @deprecated */
public async close(): Promise<void> {
public async close(...args: unknown[]): Promise<void> {
logger.info('close::', args)

this.disconnect()
}

public removeListener(event: Event): void {
public removeListener(event: Event, handler: (args: unknown) => void): void {
logger.info('removeListener::', event, handler)

// note: not all dapps remove these on disconnect
if (event === 'close' || event === 'disconnect') {
this.disconnect()
@@ -166,6 +179,8 @@ export class Provider {
}

public async enable() {
logger.info('enable::')

return true
}

@@ -176,6 +191,8 @@ export class Provider {

this.connected = false

logger.info('disconnect::')

await this.request({
method: 'wallet_revokePermissions',
params: [
55 changes: 49 additions & 6 deletions apps/connector/src/contents/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { getFaviconUrl } from '~lib/get-favicon-url'
import { logger } from '~lib/logger'
import { MainMessage } from '~messages/main-message'
import { ProviderMessage } from '~messages/provider-message'

import { DesktopClient } from '../lib/desktop-client'
@@ -13,7 +15,7 @@ export const config: PlasmoCSConfig = {

const desktopClient = new DesktopClient()

const handleMessage = async (event: MessageEvent) => {
const handleProviderMessage = async (event: MessageEvent) => {
if (event.origin !== window.origin) {
return
}
@@ -39,13 +41,17 @@ const handleMessage = async (event: MessageEvent) => {
}

try {
logger.info('request::', message.data)

const response = await desktopClient.send({
...message.data,
name: window.location.hostname,
url: window.origin,
iconUrl: getFaviconUrl() ?? '',
})

logger.info('response::', response)

event.ports[0].postMessage({
type: 'status:proxy:success',
data: response,
@@ -71,12 +77,10 @@ const handleMessage = async (event: MessageEvent) => {
}
}

const proxyMessage: ProxyMessage = {
event.ports[0].postMessage({
type: 'status:proxy:error',
error: proxyError,
}

event.ports[0].postMessage(proxyMessage)
} satisfies ProxyMessage)
}
}

@@ -108,4 +112,43 @@ function isRpcError(
)
}

window.addEventListener('message', handleMessage)
window.addEventListener('message', handleProviderMessage)

const handleMainMessage = async (event: MessageEvent) => {
if (event.origin !== window.origin) {
return
}

let message: MainMessage
try {
message = MainMessage.parse(event.data)
} catch {
return
}

if (message.type !== 'status:main') {
return
}

try {
const response = await chrome.runtime.sendMessage(
chrome.runtime.id,
message.data,
)

event.ports[0].postMessage({
type: 'status:proxy:success',
data: response,
} satisfies ProxyMessage)
} catch (error) {
event.ports[0].postMessage({
type: 'status:proxy:error',
error: {
code: -32603,
message: isError(error) ? error.message : 'Internal error',
},
} satisfies ProxyMessage)
}
}

window.addEventListener('message', handleMainMessage)
9 changes: 7 additions & 2 deletions apps/connector/src/hooks/use-local-storage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useEffect, useState } from 'react'

import { logger } from '~lib/logger'

export function useLocalStorage<T>(
key: string,
initialValue: T,
@@ -10,7 +12,8 @@ export function useLocalStorage<T>(
try {
const item = window.localStorage.getItem(key)
setStoredValue(item ? JSON.parse(item) : initialValue)
} catch {
} catch (error) {
logger.error(error)
setStoredValue(initialValue)
}
}, [])
@@ -39,7 +42,9 @@ export function useLocalStorage<T>(
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
window.dispatchEvent(new Event('storage'))
} catch {}
} catch (error) {
logger.error(error)
}
}

return [storedValue, setValue]
13 changes: 13 additions & 0 deletions apps/connector/src/lib/desktop-client.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@ import { WebSocketProvider } from 'ethers'

import { config } from '~config'

import { logger } from './logger'

import type { RequestArguments } from '~lib/request-arguments'
import type { WebSocketLike } from 'ethers'

@@ -19,6 +21,7 @@ export class DesktopClient {
return
}

logger.info('stop::')
this.#rpcClient?.destroy()
this.#rpcClient = null

@@ -27,6 +30,7 @@ export class DesktopClient {

public async send(args: DesktopRequestArguments) {
if (!this.#rpcClient) {
logger.info('start::')
this.#rpcClient = new WebSocketProvider(
config.desktop.rpc.url,
'mainnet',
@@ -41,6 +45,11 @@ export class DesktopClient {

await waitUntilOpen(this.#rpcClient.websocket)

logger.info('client::', {
method: config.desktop.rpc.method,
params: [JSON.stringify(args)],
})

return await this.#rpcClient.send(config.desktop.rpc.method, [
JSON.stringify(args),
])
@@ -71,6 +80,10 @@ async function waitUntilOpen(websocket: WebSocketLike) {
reject(new Error('Timed out to connect to the RPC server'))
}, 30 * 1000)

if (websocket.readyState === WebSocket.CONNECTING) {
logger.warn('Waiting for the RPC server to connect')
}

const onopen = websocket.onopen?.bind(websocket)
websocket.onopen = event => {
onopen?.(event)
47 changes: 47 additions & 0 deletions apps/connector/src/lib/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable no-console */

export const logger = {
info(message: unknown, ...args: unknown[]) {
log('info', message, ...args)
},

warn(message: unknown, ...args: unknown[]) {
log('warn', message, ...args)
},

error(message: unknown, ...args: unknown[]) {
log('error', message, ...args)
},
}

function log(
level: 'info' | 'warn' | 'error',
message: unknown,
...args: unknown[]
) {
// service worker context
if (
typeof window === 'undefined' && // !window throws
typeof self !== 'undefined' &&
self.registration
) {
chrome.storage.sync.get('status:logging').then(result => {
if (!(result['status:logging'] === 'true')) {
return
}

// async logging
console[level]('status:connector:', message, ...args)
})

return
}

// other extension contexts
if (!(window?.localStorage.getItem('status:logging') === 'true')) {
return
}

// synchronous logging
console[level]('status:connector:', message, ...args)
}
1 change: 1 addition & 0 deletions apps/connector/src/lib/request-arguments.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { z } from 'zod'

/**
* @see https://eips.ethereum.org/EIPS/eip-1193#request-1
* @see https://www.jsonrpc.org/specification#request_object
*/
export const RequestArguments = z.object({
method: z.string(),
12 changes: 12 additions & 0 deletions apps/connector/src/messages/main-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { z } from 'zod'

import { RequestArguments } from '~lib/request-arguments'

export const MainMessage = z.discriminatedUnion('type', [
z.object({
type: z.literal('status:main'),
data: RequestArguments,
}),
])

export type MainMessage = z.infer<typeof MainMessage>