From 8adf9a33830eee1dd08da8a698bf906d009d716f Mon Sep 17 00:00:00 2001
From: BenRey
Date: Mon, 18 Nov 2024 10:31:05 +0100
Subject: [PATCH] feat: enhance MetaMask integration with wallet unlock prompts
and connection checks
---
package-lock.json | 4 +-
package.json | 3 +-
src/massaStation/MassaStationWallet.ts | 2 +-
src/metamaskSnap/MetamaskWallet.ts | 14 ++++-
src/metamaskSnap/metamask.ts | 71 ++++++++++++++++++++++++++
src/metamaskSnap/snap.ts | 46 ++++++++++++++---
src/walletsManager/walletList.ts | 5 +-
7 files changed, 129 insertions(+), 16 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 90f278b..4dcfc14 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,8 +16,7 @@
"bs58check": "^4.0.0",
"buffer": "^6.0.3",
"eventemitter3": "^5.0.1",
- "lodash.isequal": "^4.5.0",
- "loglevel": "^1.9.2"
+ "lodash.isequal": "^4.5.0"
},
"devDependencies": {
"@babel/preset-env": "^7.22.14",
@@ -7571,6 +7570,7 @@
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
diff --git a/package.json b/package.json
index c90b02e..c57da9a 100644
--- a/package.json
+++ b/package.json
@@ -60,8 +60,7 @@
"bs58check": "^4.0.0",
"buffer": "^6.0.3",
"eventemitter3": "^5.0.1",
- "lodash.isequal": "^4.5.0",
- "loglevel": "^1.9.2"
+ "lodash.isequal": "^4.5.0"
},
"devDependencies": {
"@babel/preset-env": "^7.22.14",
diff --git a/src/massaStation/MassaStationWallet.ts b/src/massaStation/MassaStationWallet.ts
index f5e2dac..31de085 100644
--- a/src/massaStation/MassaStationWallet.ts
+++ b/src/massaStation/MassaStationWallet.ts
@@ -44,7 +44,7 @@ export class MassaStationWallet implements Wallet {
}
static async createIfInstalled(): Promise {
- if (isMassaWalletEnabled()) {
+ if (await isMassaWalletEnabled()) {
return new MassaStationWallet();
}
return null;
diff --git a/src/metamaskSnap/MetamaskWallet.ts b/src/metamaskSnap/MetamaskWallet.ts
index 7bfdbe9..4b5b9d9 100644
--- a/src/metamaskSnap/MetamaskWallet.ts
+++ b/src/metamaskSnap/MetamaskWallet.ts
@@ -6,7 +6,11 @@ import {
Provider,
} from '@massalabs/massa-web3';
import { WalletName } from '../wallet';
-import { getMetamaskProvider } from './metamask';
+import {
+ getMetamaskProvider,
+ isMetaMaskUnlocked,
+ promptAndWaitForWalletUnlock,
+} from './metamask';
import { connectSnap, getMassaSnapInfo } from './snap';
import { MetamaskAccount } from './MetamaskAccount';
import { MetaMaskInpageProvider } from '@metamask/providers';
@@ -32,6 +36,7 @@ export class MetamaskWallet implements Wallet {
static async createIfInstalled(): Promise {
try {
const metamask = await getMetamaskProvider();
+ console.log('metamask', metamask);
if (!metamask) return null;
return new MetamaskWallet(metamask);
@@ -132,7 +137,14 @@ export class MetamaskWallet implements Wallet {
public async connect() {
try {
+ const isUnlocked = await isMetaMaskUnlocked();
+
+ if (!isUnlocked) {
+ await promptAndWaitForWalletUnlock();
+ }
+
const snap = await getMassaSnapInfo(this.metamaskProvider);
+
if (!snap) {
await connectSnap(this.metamaskProvider);
}
diff --git a/src/metamaskSnap/metamask.ts b/src/metamaskSnap/metamask.ts
index 83e81a1..8935e9d 100644
--- a/src/metamaskSnap/metamask.ts
+++ b/src/metamaskSnap/metamask.ts
@@ -124,3 +124,74 @@ export async function getMetamaskProvider(): Promise 0;
+}
+
+export function isMetamaskInstalled() {
+ return Boolean(window.ethereum);
+}
+
+export async function promptAndWaitForWalletUnlock(): Promise {
+ if (typeof window.ethereum === 'undefined') {
+ throw new Error(
+ 'MetaMask is not installed. Please install it and try again.',
+ );
+ }
+
+ const ethereum = window.ethereum;
+
+ try {
+ // Prompt the user to unlock the wallet
+ await ethereum.request({ method: 'eth_requestAccounts' });
+
+ // Wait for accounts to become available
+ return new Promise((resolve, reject) => {
+ const checkAccounts = async () => {
+ try {
+ const accounts: string[] = await ethereum.request({
+ method: 'eth_accounts',
+ });
+ if (accounts && accounts.length > 0) {
+ resolve(accounts); // Wallet is unlocked
+ }
+ } catch (error) {
+ reject(
+ new Error('Error checking accounts: ' + (error as Error).message),
+ );
+ }
+ };
+
+ // Initial check for accounts
+ checkAccounts();
+
+ ethereum.on('accountsChanged', (accounts: string[]) => {
+ if (accounts.length > 0) {
+ console.log('Wallet unlocked:', accounts);
+ resolve(accounts);
+ } else {
+ console.warn('Accounts changed, but no accounts are available.');
+ }
+ });
+ ethereum.on('disconnect', () => {
+ reject(new Error('MetaMask disconnected.'));
+ });
+ });
+ } catch (error: any) {
+ if (error.code === 4001) {
+ throw new Error('User rejected the request.');
+ } else if (error.message.includes('User closed popup')) {
+ throw new Error('MetaMask popup was closed without connecting.');
+ } else {
+ throw new Error('Wallet unlocking failed.');
+ }
+ }
+}
diff --git a/src/metamaskSnap/snap.ts b/src/metamaskSnap/snap.ts
index 0bebfd4..21457dc 100644
--- a/src/metamaskSnap/snap.ts
+++ b/src/metamaskSnap/snap.ts
@@ -2,14 +2,11 @@ import type { MetaMaskInpageProvider } from '@metamask/providers';
import { MASSA_SNAP_ID } from './config';
import type { GetSnapsResponse, Snap } from './types';
-import log from 'loglevel';
-
-log.setLevel('error');
const getInstalledSnaps = async (
provider: MetaMaskInpageProvider,
): Promise =>
- await provider.request({
+ provider.request({
method: 'wallet_getSnaps',
});
@@ -25,7 +22,7 @@ export const connectSnap = async (
snapId: string = MASSA_SNAP_ID,
params: Record<'version' | string, unknown> = {},
) => {
- await provider.request({
+ provider.request({
method: 'wallet_requestSnaps',
params: {
[snapId]: params,
@@ -45,7 +42,7 @@ export const getMassaSnapInfo = async (
snap.id === MASSA_SNAP_ID && (!version || snap.version === version),
);
} catch (error) {
- log.error('Failed to obtain installed snap', error);
+ console.error('Failed to obtain installed snap', error);
return undefined;
}
};
@@ -58,3 +55,40 @@ export const showPrivateKey = async (provider: MetaMaskInpageProvider) => {
};
export const isLocalSnap = (snapId: string) => snapId.startsWith('local:');
+
+export async function isDappConnectedToSnap(snapId: string): Promise {
+ if (typeof window.ethereum === 'undefined') {
+ console.error('MetaMask is not installed.');
+ return false;
+ }
+
+ try {
+ // Request all installed snaps
+ const installedSnaps = await window.ethereum.request({
+ method: 'wallet_getSnaps',
+ });
+
+ // Check if the specific Snap is installed
+ const snap = installedSnaps[snapId];
+ if (!snap) {
+ console.log(`Snap with ID ${snapId} is not installed.`);
+ return false;
+ }
+
+ // Check if the current dApp is allowed
+ const currentOrigin = window.location.origin;
+ const allowedOrigins =
+ snap.permissions?.snap_allowedOrigins?.caveats?.[0]?.value || [];
+
+ if (allowedOrigins.includes(currentOrigin)) {
+ console.log(`DApp ${currentOrigin} is already connected to the Snap.`);
+ return true;
+ } else {
+ console.log(`DApp ${currentOrigin} is NOT connected to the Snap.`);
+ return false;
+ }
+ } catch (error) {
+ console.error('Error checking Snap connection:', error);
+ return false;
+ }
+}
diff --git a/src/walletsManager/walletList.ts b/src/walletsManager/walletList.ts
index 9f002a6..67353b2 100644
--- a/src/walletsManager/walletList.ts
+++ b/src/walletsManager/walletList.ts
@@ -5,9 +5,6 @@ import { wait } from '../utils/time';
import { MassaStationWallet } from '../massaStation/MassaStationWallet';
import { WalletName } from '../wallet/types';
import { MetamaskWallet } from '../metamaskSnap/MetamaskWallet';
-import log from 'loglevel';
-
-log.setLevel('error');
export const supportedWallets: WalletInterfaces = [
BearbyWallet,
@@ -22,7 +19,7 @@ export async function getWallets(delay = 200): Promise {
try {
return await WalletClass.createIfInstalled();
} catch (error) {
- log.error(`Error initializing wallet ${WalletClass.name}:`, error);
+ console.error(`Error initializing wallet ${WalletClass.name}:`, error);
}
return null;
});