From efc8b272c94ef99060b8e609b893b9d54dda845e Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 6 Dec 2024 10:56:15 +0400 Subject: [PATCH] feat(evm): propogate chain change event to dapps --- src/background/index.ts | 14 +++++++- src/content-scripts/inject.ts | 24 ++++++++++++- src/offscreen/offscreen.ts | 34 +++++++++++++++++-- src/protocols/ethereum/config.ts | 4 +++ .../libs/EthereumRpcMethodsHandler.ts | 12 +++++-- src/protocols/ethereum/types.ts | 6 ++-- 6 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/background/index.ts b/src/background/index.ts index 62dc37540..1b2ede2a7 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,4 +1,4 @@ -import { IBackgroundMessageData } from '@/types'; +import type { IBackgroundMessageData } from '@/types'; import { openPopup, removePopup, @@ -47,6 +47,18 @@ const handleMessage: browser.runtime.onMessageBool = ( popupType, } = msg.params ?? {}; switch (msg.method) { + case 'chainChanged': + browser.tabs.query({ active: true, lastFocusedWindow: true }).then(([tab]) => { + if (tab.id) { + browser.tabs.sendMessage(tab.id, { + superheroWalletApproved: true, + method: msg.method, + result: msg.params?.rpcMethodParams?.result, + type: 'result', + }); + } + }); + break; case 'openPopup': openPopup(popupType!, aepp!, popupProps).then((popupConfig) => { sendResponse(popupConfig); diff --git a/src/content-scripts/inject.ts b/src/content-scripts/inject.ts index 78c4bcf86..e23dd0b13 100644 --- a/src/content-scripts/inject.ts +++ b/src/content-scripts/inject.ts @@ -7,7 +7,13 @@ import { import type { IEthRpcMethodParameters } from '@/protocols/ethereum/types'; import type { BackgroundMethod } from '@/types'; -import { ETH_RPC_ETHERSCAN_PROXY_METHODS, ETH_RPC_METHODS } from '@/protocols/ethereum/config'; +import { + ETH_RPC_ETHERSCAN_PROXY_METHODS, + ETH_RPC_METHODS, + ETH_RPC_WALLET_EVENTS, +} from '@/protocols/ethereum/config'; + +const connectedDapps: Record = {}; window.browser = require('webextension-polyfill'); @@ -33,6 +39,9 @@ const runContentScript = () => { rpcMethodParams: params, aepp: event.origin, }); + if (!connectedDapps[event.origin]) { + connectedDapps[event.origin] = event.source; + } event.source.postMessage({ jsonrpc: '2.0', result, @@ -70,6 +79,19 @@ const runContentScript = () => { }, false, ); + browser.runtime.onMessage.addListener(({ method, result }: any) => { + if (method === ETH_RPC_WALLET_EVENTS.chainChanged) { + Object.entries(connectedDapps).forEach(([origin, source]) => { + source.postMessage({ + jsonrpc: '2.0', + result, + method, + superheroWalletApproved: true, + type: 'result', + }, origin as any); + }); + } + }); /** * Aex-2 Aepp communication diff --git a/src/offscreen/offscreen.ts b/src/offscreen/offscreen.ts index 7ce8c3c3e..befab0efc 100644 --- a/src/offscreen/offscreen.ts +++ b/src/offscreen/offscreen.ts @@ -1,14 +1,44 @@ import '@/lib/initPolyfills'; import '@/protocols/registerAdapters'; +import { watch } from 'vue'; import type { IBackgroundMessageData } from '@/types'; import type { EthRpcSupportedMethods } from '@/protocols/ethereum/types'; -import { IS_FIREFOX, POPUP_METHODS } from '@/constants'; -import { useWalletConnect } from '@/composables'; +import { IS_FIREFOX, POPUP_METHODS, PROTOCOLS } from '@/constants'; +import { useWalletConnect, useNetworks } from '@/composables'; import { handleEthereumRpcMethod } from '@/protocols/ethereum/libs/EthereumRpcMethodsHandler'; +import { ETH_RPC_WALLET_EVENTS } from '@/protocols/ethereum/config'; import * as wallet from './wallet'; import { registerInPageContentScript, updateDynamicRules } from '../background/utils'; +const { activeNetworkName, networks } = useNetworks(); + +watch(activeNetworkName, async (newNetworkName, oldNetworkName) => { + if (oldNetworkName && newNetworkName !== oldNetworkName) { + if (IS_FIREFOX) { + const [tab] = await browser.tabs.query({ active: true, lastFocusedWindow: true }); + if (tab.id) { + browser.tabs.sendMessage(tab.id, { + superheroWalletApproved: true, + method: ETH_RPC_WALLET_EVENTS.chainChanged, + result: `0x${BigInt(networks.value[newNetworkName].protocols[PROTOCOLS.ethereum].chainId).toString(16)}`, + type: 'result', + }); + } + } else { + browser.runtime.sendMessage({ + target: 'background', + method: ETH_RPC_WALLET_EVENTS.chainChanged, + params: { + rpcMethodParams: { + result: `0x${BigInt(networks.value[newNetworkName].protocols[PROTOCOLS.ethereum].chainId).toString(16)}`, + }, + }, + }); + } + } +}); + // If browser is FF, load the redirectRule script because background is not loaded from the manifest // in FF we have to use the offscreen.html as background page if (IS_FIREFOX) { diff --git a/src/protocols/ethereum/config.ts b/src/protocols/ethereum/config.ts index d9cceb7bb..7f944744e 100644 --- a/src/protocols/ethereum/config.ts +++ b/src/protocols/ethereum/config.ts @@ -121,3 +121,7 @@ export const ETH_RPC_METHODS = { switchNetwork: 'wallet_switchEthereumChain', revokePermissions: 'wallet_revokePermissions', } as const; + +export const ETH_RPC_WALLET_EVENTS = { + chainChanged: 'chainChanged', +} as const; diff --git a/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts b/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts index fa66d866b..9bafdf76d 100644 --- a/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts +++ b/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts @@ -12,7 +12,12 @@ import { ProtocolAdapterFactory } from '@/lib/ProtocolAdapterFactory'; import { EtherscanService } from '@/protocols/ethereum/libs/EtherscanService'; import { useEthNetworkSettings } from '@/protocols/ethereum/composables/ethNetworkSettings'; import { useEthFeeCalculation } from '@/protocols/ethereum/composables/ethFeeCalculation'; -import { ETH_CONTRACT_ID, ETH_RPC_ETHERSCAN_PROXY_METHODS, ETH_RPC_METHODS } from '@/protocols/ethereum/config'; +import { + ETH_CONTRACT_ID, + ETH_RPC_ETHERSCAN_PROXY_METHODS, + ETH_RPC_METHODS, + ETH_RPC_WALLET_EVENTS, +} from '@/protocols/ethereum/config'; import { CONNECT_PERMISSIONS, @@ -176,7 +181,10 @@ export async function handleEthereumRpcMethod( } return false; } - if (Object.values(ETH_RPC_ETHERSCAN_PROXY_METHODS).includes(method)) { + if ( + method !== ETH_RPC_WALLET_EVENTS.chainChanged + && Object.values(ETH_RPC_ETHERSCAN_PROXY_METHODS).includes(method) + ) { const apiUrl = ethActiveNetworkPredefinedSettings.value.middlewareUrl; const result = await new EtherscanService(apiUrl) .fetchFromApi({ diff --git a/src/protocols/ethereum/types.ts b/src/protocols/ethereum/types.ts index e06134b54..984a0c287 100644 --- a/src/protocols/ethereum/types.ts +++ b/src/protocols/ethereum/types.ts @@ -1,5 +1,5 @@ import type { Dictionary, INetworkProtocolSettings, ObjectValues } from '@/types'; -import { ETH_RPC_ETHERSCAN_PROXY_METHODS, ETH_RPC_METHODS } from '@/protocols/ethereum/config'; +import { ETH_RPC_ETHERSCAN_PROXY_METHODS, ETH_RPC_METHODS, ETH_RPC_WALLET_EVENTS } from '@/protocols/ethereum/config'; /** * Settings specific to this protocol. @@ -34,8 +34,10 @@ export interface IEthRpcMethodParameters { tag?: string; value?: string; chainId?: string; + result?: string; } export type EthRpcMethod = ObjectValues; export type EthRpcEtherscanProxyMethod = ObjectValues; -export type EthRpcSupportedMethods = EthRpcMethod | EthRpcEtherscanProxyMethod; +export type ETHRpcWalletEvents = ObjectValues; +export type EthRpcSupportedMethods = EthRpcMethod | EthRpcEtherscanProxyMethod | ETHRpcWalletEvents;