From 209bffefde15a08a6d5541f9589b0e134714a85f Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Tue, 17 Sep 2024 09:43:07 +0200 Subject: [PATCH] feat: detect metamask version to force upgrade --- packages/get-starknet/.eslintrc.js | 7 ++ packages/get-starknet/package.json | 2 +- packages/get-starknet/src/snap.ts | 33 +++++++ packages/get-starknet/src/wallet.ts | 132 ++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) diff --git a/packages/get-starknet/.eslintrc.js b/packages/get-starknet/.eslintrc.js index 13477795..0c280f2a 100644 --- a/packages/get-starknet/.eslintrc.js +++ b/packages/get-starknet/.eslintrc.js @@ -36,6 +36,13 @@ module.exports = { 'import/no-nodejs-modules': 'off', }, }, + + { + files: ['src/wallet.ts'], + rules: { + 'no-restricted-globals': ['off', 'document', 'window'] // Disable rule for document and window + }, + }, ], ignorePatterns: ['!.eslintrc.js', 'dist/', '**/test', '.nyc_output/', 'coverage/'], diff --git a/packages/get-starknet/package.json b/packages/get-starknet/package.json index c36f5b4e..a81842f0 100644 --- a/packages/get-starknet/package.json +++ b/packages/get-starknet/package.json @@ -17,7 +17,7 @@ "build": "webpack --config webpack.config.build.js", "prettier": "prettier --write \"src/**/*.ts\"", "lint": "eslint 'src/*.{js,ts,tsx}' --max-warnings 0 -f json -o eslint-report.json", - "lint:fix": "eslint '**/*.{js,ts,tsx}' --fix", + "lint:fix": "eslint 'src/*.{js,ts,tsx}' --fix", "test:unit": "" }, "keywords": [], diff --git a/packages/get-starknet/src/snap.ts b/packages/get-starknet/src/snap.ts index bac8e669..300937b0 100644 --- a/packages/get-starknet/src/snap.ts +++ b/packages/get-starknet/src/snap.ts @@ -28,6 +28,39 @@ export class MetaMaskSnap { this.#version = version; } + async isUpgradeRequired(minRequiredVersion = '13.0.0'): Promise { + const version = await this.#getVersionNumber(); + const parts = version.split('.').map(Number); + const requiredParts = minRequiredVersion.split('.').map(Number); + + for (let i = 0; i < 3; i++) { + if (parts[i] < requiredParts[i]) { + return true; // current version is smaller, upgrade required + } else if (parts[i] > requiredParts[i]) { + return false; // current version is greater or equal, no upgrade required + } + } + + return false; // If both versions are equal + } + + async #getVersionNumber(): Promise { + // Requesting the client version from the provider + const clientVersion: string = (await this.#provider.request({ + method: 'web3_clientVersion', + params: [], + })) as string; + + // Extracting the version number (xx.yy.zz) using a regular expression + const versionMatch = clientVersion.match(/(\d+\.\d+\.\d+)/u); + + // If a match is found, return it; otherwise, return a default value or throw an error + if (versionMatch) { + return versionMatch[0]; // The version number (xx.yy.zz) + } + throw new Error('Version number not found in client version'); + } + async getPubKey(userAddress: string): Promise { return (await this.#provider.request({ method: 'wallet_invokeSnap', diff --git a/packages/get-starknet/src/wallet.ts b/packages/get-starknet/src/wallet.ts index a24c81ca..7fc926d0 100644 --- a/packages/get-starknet/src/wallet.ts +++ b/packages/get-starknet/src/wallet.ts @@ -37,6 +37,8 @@ export class MetaMaskSnapWallet implements IStarknetWindowObject { metamaskProvider: MetaMaskProvider; + isPopupVisible: boolean; + static readonly #cairoVersion = '0'; // eslint-disable-next-line @typescript-eslint/naming-convention, no-restricted-globals @@ -53,6 +55,7 @@ export class MetaMaskSnapWallet implements IStarknetWindowObject { this.account = undefined; this.selectedAddress = undefined; this.isConnected = false; + this.isPopupVisible = false; this.snap = new MetaMaskSnap(MetaMaskSnapWallet.#SNAPI_ID, snapVersion, this.metamaskProvider); } @@ -128,6 +131,16 @@ export class MetaMaskSnapWallet implements IStarknetWindowObject { } async enable() { + const minRequiredVersion = '12.0.0'; + if (await this.snap.isUpgradeRequired(minRequiredVersion)) { + if (typeof document !== 'undefined') { + // Safe to use document + this.#showPopup(); + } + + throw new Error('MetaMask upgrade required'); + } + await this.snap.installIfNot(); this.isConnected = true; const network = await this.#getNetwork(); @@ -158,4 +171,123 @@ export class MetaMaskSnapWallet implements IStarknetWindowObject { off() { throw new Error('Method not supported'); } + + // Function to dynamically create and show the pop-up + // Function to dynamically create and show the pop-up + #showPopup() { + if (this.isPopupVisible) { + return; + } // Prevent multiple pop-ups + this.isPopupVisible = true; + + const backdrop = document.createElement('div'); + Object.assign(backdrop.style, { + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + backgroundColor: 'rgba(0, 0, 0, 0.25)', + backdropFilter: 'blur(5px)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + zIndex: '1000', + }); + backdrop.id = 'popupBackdrop'; + + const popup = document.createElement('div'); + Object.assign(popup.style, { + position: 'relative', + backgroundColor: '#ffffff', + borderRadius: '8px', + padding: '20px', + maxWidth: '400px', + width: '100%', + boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', + display: 'flex', // Flexbox to handle vertical centering + flexDirection: 'column', + alignItems: 'center', // Center content horizontally + justifyContent: 'center', // Center content vertically + height: '300px', // Ensure enough height to center the button in the middle of the pop-up + textAlign: 'center', + }); + + const closeButton = document.createElement('button'); + Object.assign(closeButton.style, { + position: 'absolute', + top: '10px', + right: '10px', + fontSize: '1.5rem', + border: 'none', + background: 'none', + cursor: 'pointer', + }); + closeButton.innerHTML = '×'; + closeButton.onclick = () => { + document.body.removeChild(backdrop); + this.isPopupVisible = false; + }; + + const title = document.createElement('h2'); + title.innerText = 'Update MetaMask'; + Object.assign(title.style, { + fontSize: '1.5rem', + marginBottom: '16px', + }); + + const message = document.createElement('p'); + message.innerText = 'Please update your MetaMask to the latest version for the best experience.'; + Object.assign(message.style, { + fontSize: '1rem', + marginBottom: '16px', + }); + + // Center the button using margin and block-level display + const updateButton = document.createElement('button'); + Object.assign(updateButton.style, { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: '10px', + padding: '10px 20px', + backgroundColor: '#f0f0f0', + border: 'none', + borderRadius: '5px', + color: '#333333', + cursor: 'pointer', + fontSize: '1rem', + marginTop: '20px', + }); + + const logoImg = document.createElement('img'); + Object.assign(logoImg.style, { + width: '30px', + height: '30px', + }); + logoImg.src = 'https://metamask.io/assets/icon.svg'; + logoImg.alt = 'MetaMask Logo'; + + const buttonText = document.createElement('span'); + buttonText.innerText = 'Update MetaMask'; + + updateButton.onclick = () => { + window.open('https://metamask.io/', '_blank'); + }; + + updateButton.appendChild(logoImg); + updateButton.appendChild(buttonText); + + // Append elements to the pop-up + popup.appendChild(closeButton); + popup.appendChild(title); + popup.appendChild(message); + popup.appendChild(updateButton); + + // Append the pop-up to the backdrop + backdrop.appendChild(popup); + + // Append the backdrop (with the pop-up inside) to the body + document.body.appendChild(backdrop); + } }