From d2531d8278b4929cfe3e309a91b76f71cd224170 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Thu, 21 May 2026 09:53:07 +0100 Subject: [PATCH 1/9] feat: integrate snap_confirmTransaction in SendService for universal confirmation POC --- packages/snap/locales/en.json | 2 +- packages/snap/messages.json | 2 +- packages/snap/snap.manifest.json | 13 +++++++---- .../src/core/services/send/SendService.ts | 23 +++++++++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/snap/locales/en.json b/packages/snap/locales/en.json index 81819f21..7a18defd 100644 --- a/packages/snap/locales/en.json +++ b/packages/snap/locales/en.json @@ -167,7 +167,7 @@ "message": "Confirm" }, "confirmation.signAndSendTransaction.title": { - "message": "Transaction request" + "message": "CHEESE" }, "confirmation.signTransaction.title": { "message": "Signing request" diff --git a/packages/snap/messages.json b/packages/snap/messages.json index 5f8558d0..5ef459d3 100644 --- a/packages/snap/messages.json +++ b/packages/snap/messages.json @@ -54,7 +54,7 @@ "confirmation.advanced.hide": "Hide advanced", "confirmation.cancelButton": "Cancel", "confirmation.confirmButton": "Confirm", - "confirmation.signAndSendTransaction.title": "Transaction request", + "confirmation.signAndSendTransaction.title": "CHEESE", "confirmation.signTransaction.title": "Signing request", "confirmation.sendAndConfirmTransaction.title": "Sending and confirming transaction", "confirmation.advanced.unknownInstruction": "Unknown", diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index ddb80479..442a23f3 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snap-solana-wallet.git" }, "source": { - "shasum": "hFc9PK6QCElAWB+pnkHVzW3JQVj8UNub9NozdKIxAOQ=", + "shasum": "8PZ9SicJM3P8Tj64Z4egs4G6fy37nyieYlbRS14JywU=", "location": { "npm": { "filePath": "dist/bundle.js", @@ -19,7 +19,8 @@ "locales": ["locales/en.json"] }, "initialConnections": { - "https://portfolio.metamask.io": {} + "https://portfolio.metamask.io": {}, + "http://localhost:3000": {} }, "initialPermissions": { "endowment:rpc": { @@ -27,7 +28,10 @@ "snaps": false }, "endowment:keyring": { - "allowedOrigins": ["https://portfolio.metamask.io"] + "allowedOrigins": [ + "https://portfolio.metamask.io", + "http://localhost:3000" + ] }, "snap_getBip32Entropy": [ { @@ -73,7 +77,8 @@ "snap_manageAccounts": {}, "snap_manageState": {}, "snap_dialog": {}, - "snap_getPreferences": {} + "snap_getPreferences": {}, + "snap_confirmTransaction": {} }, "platformVersion": "10.3.0", "manifestVersion": "0.1" diff --git a/packages/snap/src/core/services/send/SendService.ts b/packages/snap/src/core/services/send/SendService.ts index 00537d20..e6e3b323 100644 --- a/packages/snap/src/core/services/send/SendService.ts +++ b/packages/snap/src/core/services/send/SendService.ts @@ -131,6 +131,29 @@ export class SendService { const base64EncodedTransaction = fromTransactionToBase64String(transaction); + const approved = (await snap.request({ + method: 'snap_confirmTransaction', + params: { + chainNamespace: 'solana', + chain: scope, + accountId: fromAccountId, + from: account.address, + to: toAddress, + value: isNativeSend + ? solToLamports(amount).toString() + : amount, + assetType: assetId, + assetSymbol: isNativeSend ? 'SOL' : 'TOKEN', + assetDecimals: isNativeSend ? 9 : 0, + origin: METAMASK_ORIGIN, + }, + })) as boolean; + + if (!approved) { + this.#logger.log('User rejected transaction via universal confirmation'); + return { rejected: true }; + } + const keyringRequest: KeyringRequest = { id: globalThis.crypto.randomUUID(), scope, From f4e147438f10adead294585fa79e2426c94f6bc7 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Thu, 21 May 2026 12:08:53 +0100 Subject: [PATCH 2/9] feat: include feeRaw, feeAssetType and real SPL asset metadata in universal confirmation payload --- .../src/core/services/send/SendService.ts | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/snap/src/core/services/send/SendService.ts b/packages/snap/src/core/services/send/SendService.ts index e6e3b323..0aa5bf71 100644 --- a/packages/snap/src/core/services/send/SendService.ts +++ b/packages/snap/src/core/services/send/SendService.ts @@ -14,6 +14,7 @@ import { fromTransactionToBase64String } from '../../sdk-extensions/codecs'; import type { Serializable } from '../../serialization/types'; import { solToLamports } from '../../utils/conversion'; import { createPrefixedLogger, type ILogger } from '../../utils/logger'; +import { toTokenUnits } from '../../utils/toTokenUnit'; import type { AssetsService } from '../assets'; import type { SolanaConnection } from '../connection'; import type { RecipientClassifier } from './RecipientClassifier'; @@ -131,6 +132,20 @@ export class SendService { const base64EncodedTransaction = fromTransactionToBase64String(transaction); + const accountBalances = await this.#assetsService.findByAccount(account); + const assetEntry = accountBalances.find((a) => a.assetType === assetId); + + const assetSymbol = assetEntry?.symbol ?? (isNativeSend ? 'SOL' : 'TOKEN'); + const assetDecimals = assetEntry?.decimals ?? (isNativeSend ? 9 : 0); + + const rawValue = isNativeSend + ? solToLamports(amount).toString() + : toTokenUnits(amount, assetDecimals).toString(); + + const sendFeeCalculator = new SendFeeCalculator(builder); + const feeRaw = sendFeeCalculator.getFee().toString(); + const feeAssetType = Networks[scope].nativeToken.caip19Id; + const approved = (await snap.request({ method: 'snap_confirmTransaction', params: { @@ -139,12 +154,12 @@ export class SendService { accountId: fromAccountId, from: account.address, to: toAddress, - value: isNativeSend - ? solToLamports(amount).toString() - : amount, + value: rawValue, assetType: assetId, - assetSymbol: isNativeSend ? 'SOL' : 'TOKEN', - assetDecimals: isNativeSend ? 9 : 0, + assetSymbol, + assetDecimals, + feeRaw, + feeAssetType, origin: METAMASK_ORIGIN, }, })) as boolean; From e7bf6b49cdece5abecce7d4a180dcc5b97edfe27 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 10:23:07 +0100 Subject: [PATCH 3/9] chore: update snap manifest shasum for confirmation POC --- packages/snap/snap.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 442a23f3..e50ee569 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snap-solana-wallet.git" }, "source": { - "shasum": "8PZ9SicJM3P8Tj64Z4egs4G6fy37nyieYlbRS14JywU=", + "shasum": "c3/qF9Sx+nfnvCErlJXB3qCTMoGUWFDxo1N3ySkwi4o=", "location": { "npm": { "filePath": "dist/bundle.js", From 4b88099a577864f8f7279240c9e9fe4e3deddea2 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 10:32:23 +0100 Subject: [PATCH 4/9] chore: format snap manifest for confirmation POC --- packages/snap/snap.manifest.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index e50ee569..09cf1f4c 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -16,7 +16,9 @@ "registry": "https://registry.npmjs.org/" } }, - "locales": ["locales/en.json"] + "locales": [ + "locales/en.json" + ] }, "initialConnections": { "https://portfolio.metamask.io": {}, @@ -35,7 +37,11 @@ }, "snap_getBip32Entropy": [ { - "path": ["m", "44'", "501'"], + "path": [ + "m", + "44'", + "501'" + ], "curve": "ed25519" } ], From b86f49b28aeb0c5d97d88c73f4cec2ef05b09c89 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 11:17:23 +0100 Subject: [PATCH 5/9] feat: send lean confirm transaction payload --- packages/snap/snap.manifest.json | 2 +- .../src/core/services/send/SendService.ts | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 09cf1f4c..7d320f57 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snap-solana-wallet.git" }, "source": { - "shasum": "c3/qF9Sx+nfnvCErlJXB3qCTMoGUWFDxo1N3ySkwi4o=", + "shasum": "G2XiqOnbk6Mc1ax0w2K+uC+uECwPWxKqexVuuT4L9bA=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/core/services/send/SendService.ts b/packages/snap/src/core/services/send/SendService.ts index 0aa5bf71..c631ba2d 100644 --- a/packages/snap/src/core/services/send/SendService.ts +++ b/packages/snap/src/core/services/send/SendService.ts @@ -132,11 +132,16 @@ export class SendService { const base64EncodedTransaction = fromTransactionToBase64String(transaction); - const accountBalances = await this.#assetsService.findByAccount(account); + const accountBalances = + (await this.#assetsService.findByAccount(account)) ?? []; const assetEntry = accountBalances.find((a) => a.assetType === assetId); - const assetSymbol = assetEntry?.symbol ?? (isNativeSend ? 'SOL' : 'TOKEN'); - const assetDecimals = assetEntry?.decimals ?? (isNativeSend ? 9 : 0); + const assetDecimals = + assetEntry && 'decimals' in assetEntry + ? assetEntry.decimals + : isNativeSend + ? 9 + : 0; const rawValue = isNativeSend ? solToLamports(amount).toString() @@ -144,25 +149,20 @@ export class SendService { const sendFeeCalculator = new SendFeeCalculator(builder); const feeRaw = sendFeeCalculator.getFee().toString(); - const feeAssetType = Networks[scope].nativeToken.caip19Id; const approved = (await snap.request({ method: 'snap_confirmTransaction', params: { - chainNamespace: 'solana', - chain: scope, + chainId: scope, accountId: fromAccountId, - from: account.address, to: toAddress, - value: rawValue, - assetType: assetId, - assetSymbol, - assetDecimals, - feeRaw, - feeAssetType, - origin: METAMASK_ORIGIN, + amount: rawValue, + ...(isNativeSend ? {} : { assetId }), + fee: { + amount: feeRaw, + }, }, - })) as boolean; + } as never)) as boolean; if (!approved) { this.#logger.log('User rejected transaction via universal confirmation'); From 775d887b379b21f9cfd8122248e101332dd6f030 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 14:02:23 +0100 Subject: [PATCH 6/9] feat: update universal confirmation custom data --- packages/snap/snap.manifest.json | 5 +-- .../src/core/services/send/SendService.ts | 35 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 7d320f57..0c194cb2 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snap-solana-wallet.git" }, "source": { - "shasum": "G2XiqOnbk6Mc1ax0w2K+uC+uECwPWxKqexVuuT4L9bA=", + "shasum": "gUXZtZB/An7g6m9eSQ1k5z813pRPFK8pGOXmYUkHKq0=", "location": { "npm": { "filePath": "dist/bundle.js", @@ -84,7 +84,8 @@ "snap_manageState": {}, "snap_dialog": {}, "snap_getPreferences": {}, - "snap_confirmTransaction": {} + "snap_confirmTransaction": {}, + "snap_updateConfirmTransaction": {} }, "platformVersion": "10.3.0", "manifestVersion": "0.1" diff --git a/packages/snap/src/core/services/send/SendService.ts b/packages/snap/src/core/services/send/SendService.ts index c631ba2d..b211fa0a 100644 --- a/packages/snap/src/core/services/send/SendService.ts +++ b/packages/snap/src/core/services/send/SendService.ts @@ -149,10 +149,13 @@ export class SendService { const sendFeeCalculator = new SendFeeCalculator(builder); const feeRaw = sendFeeCalculator.getFee().toString(); + const confirmationId = globalThis.crypto.randomUUID(); + let customCount = 0; - const approved = (await snap.request({ + const confirmationPromise = snap.request({ method: 'snap_confirmTransaction', params: { + id: confirmationId, chainId: scope, accountId: fromAccountId, to: toAddress, @@ -161,8 +164,36 @@ export class SendService { fee: { amount: feeRaw, }, + custom: { + count: customCount, + }, }, - } as never)) as boolean; + } as never) as Promise; + + const updateInterval = setInterval(() => { + customCount += 1; + + void snap + .request({ + method: 'snap_updateConfirmTransaction', + params: { + id: confirmationId, + custom: { + count: customCount, + }, + }, + } as never) + .catch((error) => { + this.#logger.warn( + 'Failed to update universal confirmation custom data', + error, + ); + }); + }, 1000); + + const approved = await confirmationPromise.finally(() => { + clearInterval(updateInterval); + }); if (!approved) { this.#logger.log('User rejected transaction via universal confirmation'); From c5fe0970c02deb784f7559d2bf4659c706e7c87a Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 14:28:28 +0100 Subject: [PATCH 7/9] refactor: use typed confirm transaction requests --- packages/snap/snap.manifest.json | 14 ++++---------- .../snap/src/core/services/send/SendService.ts | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 0c194cb2..bc640058 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snap-solana-wallet.git" }, "source": { - "shasum": "gUXZtZB/An7g6m9eSQ1k5z813pRPFK8pGOXmYUkHKq0=", + "shasum": "nB+rWwqDOLneOumFj5Npqh2itldzWWFByUhA9pwWP9w=", "location": { "npm": { "filePath": "dist/bundle.js", @@ -16,9 +16,7 @@ "registry": "https://registry.npmjs.org/" } }, - "locales": [ - "locales/en.json" - ] + "locales": ["locales/en.json"] }, "initialConnections": { "https://portfolio.metamask.io": {}, @@ -37,11 +35,7 @@ }, "snap_getBip32Entropy": [ { - "path": [ - "m", - "44'", - "501'" - ], + "path": ["m", "44'", "501'"], "curve": "ed25519" } ], @@ -87,6 +81,6 @@ "snap_confirmTransaction": {}, "snap_updateConfirmTransaction": {} }, - "platformVersion": "10.3.0", + "platformVersion": "11.1.1", "manifestVersion": "0.1" } diff --git a/packages/snap/src/core/services/send/SendService.ts b/packages/snap/src/core/services/send/SendService.ts index b211fa0a..831ff8a4 100644 --- a/packages/snap/src/core/services/send/SendService.ts +++ b/packages/snap/src/core/services/send/SendService.ts @@ -168,7 +168,7 @@ export class SendService { count: customCount, }, }, - } as never) as Promise; + }); const updateInterval = setInterval(() => { customCount += 1; @@ -182,7 +182,7 @@ export class SendService { count: customCount, }, }, - } as never) + }) .catch((error) => { this.#logger.warn( 'Failed to update universal confirmation custom data', From f0ffc25b8f3fb71a6944d52f85c36296882ef88f Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 18:29:41 +0100 Subject: [PATCH 8/9] test: mock confirm transaction request in send service --- packages/snap/src/core/services/send/SendService.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/snap/src/core/services/send/SendService.test.ts b/packages/snap/src/core/services/send/SendService.test.ts index 23457f7c..b12d6e2d 100644 --- a/packages/snap/src/core/services/send/SendService.test.ts +++ b/packages/snap/src/core/services/send/SendService.test.ts @@ -42,6 +42,12 @@ Object.defineProperty(globalThis, 'crypto', { }, }); +Object.defineProperty(globalThis, 'snap', { + value: { + request: jest.fn(), + }, +}); + jest.mock('@solana/kit', () => ({ ...jest.requireActual('@solana/kit'), address: jest.requireActual('@solana/kit').address, @@ -75,6 +81,7 @@ describe('SendService', () => { beforeEach(() => { jest.clearAllMocks(); + jest.mocked(snap.request).mockResolvedValue(true); mockConnection = createMockConnection(); mockCache = new InMemoryCache(mockLogger); From b34f42f7fb499e4ed4d3934333cf46c63b858eca Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Sun, 14 Jun 2026 19:43:13 +0100 Subject: [PATCH 9/9] chore: remove test title copy --- packages/snap/locales/en.json | 2 +- packages/snap/messages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/snap/locales/en.json b/packages/snap/locales/en.json index 7a18defd..81819f21 100644 --- a/packages/snap/locales/en.json +++ b/packages/snap/locales/en.json @@ -167,7 +167,7 @@ "message": "Confirm" }, "confirmation.signAndSendTransaction.title": { - "message": "CHEESE" + "message": "Transaction request" }, "confirmation.signTransaction.title": { "message": "Signing request" diff --git a/packages/snap/messages.json b/packages/snap/messages.json index 5ef459d3..5f8558d0 100644 --- a/packages/snap/messages.json +++ b/packages/snap/messages.json @@ -54,7 +54,7 @@ "confirmation.advanced.hide": "Hide advanced", "confirmation.cancelButton": "Cancel", "confirmation.confirmButton": "Confirm", - "confirmation.signAndSendTransaction.title": "CHEESE", + "confirmation.signAndSendTransaction.title": "Transaction request", "confirmation.signTransaction.title": "Signing request", "confirmation.sendAndConfirmTransaction.title": "Sending and confirming transaction", "confirmation.advanced.unknownInstruction": "Unknown",