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

fix: change pali communication [use messages instead port connections] and fix dapp interaction #676

Merged
merged 8 commits into from
Dec 18, 2024
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "paliwallet",
"version": "3.2.2",
"version": "3.2.3",
"description": "A Non-Custodial Crypto Wallet",
"private": true,
"repository": {
Expand Down Expand Up @@ -52,7 +52,7 @@
"@headlessui/react": "^1.6.0",
"@heroicons/react": "^1.0.5",
"@pollum-io/sysweb3-core": "^1.0.27",
"@pollum-io/sysweb3-keyring": "^1.0.486",
"@pollum-io/sysweb3-keyring": "^1.0.487",
"@pollum-io/sysweb3-network": "^1.0.95",
"@pollum-io/sysweb3-utils": "^1.1.235",
"@reduxjs/toolkit": "^1.4.0",
Expand Down
2 changes: 1 addition & 1 deletion source/config/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const MV2_OPTIONS = {
const MV3_OPTIONS = {
manifest_version: 3,
name: 'Pali Wallet',
version: '3.2.2',
version: '3.2.3',
icons: {
16: 'assets/icons/favicon-16.png',
32: 'assets/icons/favicon-32.png',
Expand Down
46 changes: 38 additions & 8 deletions source/scripts/Background/controllers/DAppController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ interface IDappsSession {
[host: string]: {
activeAddress: string | null;
hasWindow: boolean;
port: chrome.runtime.Port;
};
}

Expand All @@ -31,29 +30,46 @@ const DAppController = (): IDAppController => {
return !!dapps?.[host];
};

const setup = (port: chrome.runtime.Port) => {
const setup = (sender: chrome.runtime.MessageSender) => {
const { isBitcoinBased } = store.getState().vault;
const { host } = new URL(port.sender.url);
const { host } = new URL(sender.url);
const activeAccount = isBitcoinBased
? getAccount(host)?.xpub
: getAccount(host)?.address;
_dapps[host] = {
activeAddress: activeAccount ? activeAccount : null,
hasWindow: false,
port,
};

port.onMessage.addListener(onMessage);
chrome.runtime.onMessage.addListener(onMessage);
// port.onDisconnect.addListener(onDisconnect); //TODO: make contentScript unavailable to Dapp on disconnection of port
};

const connect = (dapp: IDApp, isDappConnected = false) => {
!isDappConnected && store.dispatch(addDApp(dapp));
const { accounts, isBitcoinBased } = store.getState().vault;
_dapps[dapp.host] = { activeAddress: '', hasWindow: false, port: null };
_dapps[dapp.host] = { activeAddress: '', hasWindow: false };
_dapps[dapp.host].activeAddress = isBitcoinBased
? accounts[dapp.accountType][dapp.accountId].xpub
: accounts[dapp.accountType][dapp.accountId].address;

isBitcoinBased
? _dispatchPaliEvent(
dapp.host,
{
method: PaliSyscoinEvents.xpubChanged,
params: accounts[dapp.accountType][dapp.accountId].xpub,
},
PaliSyscoinEvents.xpubChanged
)
: _dispatchPaliEvent(
dapp.host,
{
method: PaliEvents.accountsChanged,
params: [_dapps[dapp.host].activeAddress],
},
PaliEvents.accountsChanged
);
};

const requestPermissions = (
Expand Down Expand Up @@ -221,8 +237,22 @@ const DAppController = (): IDAppController => {
data?: { method: string; params: any },
id = 'notification'
) => {
if (_dapps[host] && _dapps[host].port) {
_dapps[host].port.postMessage({ id, data });
const tabs = await chrome.tabs.query({ url: `*://${host}/*` });

if (tabs.length) {
tabs.forEach((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
world: 'MAIN',
func: (eventData) => {
const event = new CustomEvent('paliNotification', {
detail: JSON.stringify(eventData),
});
window.dispatchEvent(event);
},
args: [{ id, data }],
});
});
}
};

Expand Down
20 changes: 13 additions & 7 deletions source/scripts/Background/controllers/message-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Message } from './types';
* - Enable/disable requests
* - Requests for Sys and Eth providers methods
*/
const _messageHandler = async (host: string, message: Message) => {
const _messageHandler = (host: string, message: Message) => {
if (chrome.runtime.lastError) {
throw new Error('Runtime last error');
}
Expand Down Expand Up @@ -41,16 +41,22 @@ const _messageHandler = async (host: string, message: Message) => {
* Receives and reply messages
*/
export const onMessage = async (
message: Message,
port: chrome.runtime.Port
message: any,
sender: chrome.runtime.MessageSender
) => {
const { host } = new URL(port.sender.url);
const { host } = new URL(sender.url);

try {
const response = await _messageHandler(host, message);
if (response === undefined) return;
port.postMessage({ id: message.id, data: response });
await chrome.tabs.sendMessage(sender.tab.id, {
id: message.id,
data: response,
});
} catch (error: any) {
console.error(error);
port.postMessage({ id: message.id, data: { error: error } }); //This was altered for better ethereum compatibility TODO: check on syscoin contentScript side
await chrome.tabs.sendMessage(sender.tab.id, {
id: message.id,
data: { error: error },
}); //This was altered for better ethereum compatibility TODO: check on syscoin contentScript side
}
};
157 changes: 105 additions & 52 deletions source/scripts/Background/controllers/message-handler/popup-promise.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,102 @@
import { ethErrors } from 'helpers/errors';

import {
ICustomEvent,
IDAppController,
PaliRoutes,
} from '../../../../types/index'; // need to use this relative import [avoid terminal error]
import { getController } from 'scripts/Background';
import cleanErrorStack from 'utils/cleanErrorStack';

const TX_ROUTES = [
PaliRoutes.SendEthTX,
PaliRoutes.SendApprove,
PaliRoutes.SendNTokenTX,
] as const;

const CHAIN_ROUTES = [
PaliRoutes.SwitchEthChain,
PaliRoutes.AddEthChain,
PaliRoutes.SwitchUtxo,
] as const;

const REJECTION_ROUTES = new Set([
PaliRoutes.SendEthTX,
PaliRoutes.SendApprove,
PaliRoutes.EthSign,
PaliRoutes.EncryptKey,
PaliRoutes.SwitchEthChain,
PaliRoutes.AddEthChain,
PaliRoutes.ChangeAccount,
PaliRoutes.SwitchUtxo,
PaliRoutes.WatchAsset,
PaliRoutes.SwitchNetwork,
]);

const handleResponseEvent = async (
event: ICustomEvent,
eventName: string,
host: string,
route: PaliRoutes,
dapp: Readonly<IDAppController>,
resolve: (value: unknown) => void
): Promise<void> => {
const expectedEventName = `${eventName}.${host}`;

if (event.data.eventName !== expectedEventName) {
return;
}

if (CHAIN_ROUTES.includes(route as (typeof CHAIN_ROUTES)[number])) {
dapp.setHasWindow(host, false);
resolve(null);
return;
}

if (!event.data.detail) {
return;
}

try {
const parsedDetail = JSON.parse(event.data.detail);

if (TX_ROUTES.includes(route as (typeof TX_ROUTES)[number])) {
resolve(parsedDetail.hash);
return;
}

resolve(parsedDetail);
} catch (error) {
console.error('Error parsing event detail:', error);
throw new Error('Failed to parse event detail');
}
};

const handleCloseWindow = (
popup: any,
route: PaliRoutes,
host: string,
dapp: Readonly<IDAppController>,
resolve: (value: unknown) => void
): void => {
const handleWindowRemoval = (windowId: number): void => {
if (windowId !== popup.id) {
return;
}

dapp.setHasWindow(host, false);

if (REJECTION_ROUTES.has(route)) {
resolve(cleanErrorStack(ethErrors.provider.userRejectedRequest()));
return;
}

resolve({ success: false });
};

chrome.windows.onRemoved.addListener(handleWindowRemoval);
};

/**
* Opens a popup and adds events listener to resolve a promise.
*
Expand All @@ -26,15 +120,7 @@ export const popupPromise = async ({
route: string;
}) => {
const { dapp, createPopup } = getController();
// if (
// eventName !== 'connect' &&
// eventName !== 'wallet_switchEthereumChain' &&
// eventName !== 'wallet_addEthereumChain' &&
// eventName !== 'change_UTXOEVM' &&
// eventName !== 'switchNetwork' &&
// !dapp.isConnected(host)
// )
// return;

if (dapp.hasWindow(host))
throw cleanErrorStack(
ethErrors.provider.unauthorized('Dapp already has a open window')
Expand All @@ -49,49 +135,16 @@ export const popupPromise = async ({
throw error;
}
return new Promise((resolve) => {
self.addEventListener('message', (event) => {
if (event.data.eventName === `${eventName}.${host}`) {
if (event.data.detail !== undefined && event.data.detail !== null) {
if (
route === 'tx/send/ethTx' ||
route === 'tx/send/approve' ||
route === 'tx/send/nTokenTx'
) {
resolve(JSON.parse(event.data.detail).hash);
}
resolve(JSON.parse(event.data.detail));
}
if (
route === 'switch-EthChain' ||
route === 'add-EthChain' ||
route === 'switch-UtxoEvm'
) {
resolve(null);
dapp.setHasWindow(host, false);
return null;
}
}
});

chrome.windows.onRemoved.addListener((id) => {
if (id === popup.id) {
if (
route === 'tx/send/ethTx' ||
route === 'tx/send/approve' ||
route === 'tx/ethSign' ||
route === 'tx/encryptKey' ||
route === 'switch-EthChain' ||
route === 'add-EthChain' ||
route === 'change-account' ||
route === 'switch-UtxoEvm' ||
route === 'watch-asset' ||
route === 'switch-network'
) {
resolve(cleanErrorStack(ethErrors.provider.userRejectedRequest()));
}
dapp.setHasWindow(host, false);
resolve({ success: false });
}
self.addEventListener('message', (swEvent) => {
handleResponseEvent(
swEvent,
eventName,
host,
route as PaliRoutes,
dapp,
resolve
);
handleCloseWindow(popup, route as PaliRoutes, host, dapp, resolve);
});
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { getNetworkChain, networkChain } from 'utils/network';

import { popupPromise } from './popup-promise';

/**
* Handles methods request.
*
Expand Down Expand Up @@ -142,7 +143,7 @@
//* Wallet methods
if (prefix === 'wallet') {
let tryingToAdd = false;
const { activeNetwork, networks: chains } = store.getState().vault;

Check warning on line 146 in source/scripts/Background/controllers/message-handler/requests.ts

View workflow job for this annotation

GitHub Actions / yarn test-all

'activeNetwork' is already declared in the upper scope on line 40 column 5
switch (methodName) {
case 'isLocked':
return !wallet.isUnlocked();
Expand Down Expand Up @@ -256,7 +257,7 @@
tryingToAdd = true;
case 'switchEthereumChain':
if (isBitcoinBased) throw cleanErrorStack(ethErrors.rpc.internal());
const chainId = tryingToAdd

Check warning on line 260 in source/scripts/Background/controllers/message-handler/requests.ts

View workflow job for this annotation

GitHub Actions / yarn test-all

'chainId' is already declared in the upper scope on line 43 column 11
? customRPCData.chainId
: Number(data.params[0].chainId);

Expand Down
Loading
Loading