Skip to content

poc: non-evm native confirmations#43531

Draft
matthewwalsh0 wants to merge 28 commits into
mainfrom
poc/non-evm-native-confirmations
Draft

poc: non-evm native confirmations#43531
matthewwalsh0 wants to merge 28 commits into
mainfrom
poc/non-evm-native-confirmations

Conversation

@matthewwalsh0

@matthewwalsh0 matthewwalsh0 commented Jun 14, 2026

Copy link
Copy Markdown
Member

Description

POC to demonstrate the use of fully native confirmations for non-EVM transactions.

The API is protocol-agnostic. The Solana wallet Snap is the first consumer used to exercise the flow end to end.

The Snap still owns the transaction lifecycle: protocol-specific transaction construction, signing, submission, and fee calculation. The client owns the confirmation lifecycle using a base pending transaction definition that carries the protocol-agnostic display and validation data needed by the native confirmation.

Architecture Summary

  1. A Snap requires a confirmation, for example when handling signAndSendTransaction as part of the client Send flow.
  2. Snap calls snap_confirmTransaction.
  3. Snaps Platform validates the request and invokes the confirmation hook.
  4. Extension receives the Snap API request through that hook.
  5. Extension stores the lean transaction payload in MultichainTransactionsController.pendingTransactions.
  6. Extension creates an ApprovalController request with type universalTransaction.
  7. Extension Confirm UI renders the approval using the pending transaction data.
  8. User approves or rejects.
  9. Extension resolves the Snap API request.
  10. Snap signs/submits only if approved.

Benefits

  • Clear ownership: Snaps handle protocol execution, clients handle confirmation UX.
  • One universal confirmation flow that can support any network.
  • Consistent UX and easier updates across all transaction confirmations.
  • Native client controls for approvals, alerts, loading states, and validation.
  • Network-specific details can be added without making Snaps own any UX.
  • Faster network rollout because new networks do not need bespoke confirmation UX.
  • Backwards compatible with Snap confirmations, so migration can be staggered.

MetaMask/snaps

@metamask/snaps-rpc-methods defines the restricted confirmation methods.

snap_confirmTransaction accepts:

type ConfirmTransactionParams = {
  id?: string;
  chainId: CaipChainId;
  accountId: string;
  to: string;
  amount: string;
  assetId?: CaipAssetType;
  fee?: {
    amount: string;
    assetId?: CaipAssetType;
  };
  custom?: Record<string, Json>;
};

snap_updateConfirmTransaction accepts:

type UpdateConfirmTransactionParams = {
  id: string;
  fee?: {
    amount: string;
    assetId?: CaipAssetType;
  };
  custom?: Record<string, Json>;
};

The Snaps changes:

  • Add snap_confirmTransaction.
  • Add snap_updateConfirmTransaction.
  • Validate the protocol-agnostic payload, including CAIP chain and asset IDs.
  • Define the internal method hook that clients wire to snap_confirmTransaction to show the native confirmation and return the approval result.
  • Return a boolean approval result to the Snap.
  • Add SDK method typings for both methods so Snap consumers can call them without temporary casts.
  • Allow the new permissions through manifest validation and Snap permission handling.

MetaMask/core

@metamask/multichain-transactions-controller provides the temporary Snap-to-UI state bridge.

Pending universal transaction state is keyed by approvalId:

type PendingMultichainTransaction<
  CustomData extends Record<string, Json> = Record<string, Json>,
> = {
  approvalId: string;
  chainId: CaipChainId;
  accountId: string;
  to: string;
  amount: string;
  assetId?: CaipAssetType;
  fee?: {
    amount: string;
    assetId?: CaipAssetType;
  };
  custom?: CustomData;
  origin: string;
  createdAt: number;
};

The Core changes:

  • Add non-persisted pendingTransactions keyed by approvalId.
  • Add PendingMultichainTransaction.
  • Add messenger actions: addPendingTransaction, updatePendingTransaction, and removePendingTransaction.
  • Let extension UI read Snap-provided display payload while the approval is active.
  • Use standard controller state reads for pending confirmation data.

MetaMask/metamask-extension

Snaps Platform Integration

Extension connects the Snaps Platform hook to approval infrastructure:

  • Implements showUniversalTransactionConfirmation.
  • Creates an approvalId.
  • Writes the lean Snap payload into MultichainTransactionsController.pendingTransactions.
  • Creates an ApprovalController request with type universalTransaction.
  • Resolves the Snap API request based on approval or rejection.
  • Cleans up pending transaction state after resolution.
  • Supports Snap-owned update IDs by mapping active (snapId, id) pairs to internal approval IDs.
  • Wires the updateUniversalTransactionConfirmation hook so snap_updateConfirmTransaction can patch pending Core state while the confirmation is active.

Confirmation UI

Extension renders the native confirmation:

  • Routes universalTransaction approvals to universal confirmation UI.
  • Reads pending transaction data by approvalId.
  • Renders heading, From, To, Network, and Network Fee rows.
  • Reuses wallet-initiated header behavior so Advanced Details works.
  • Uses generic universal rows for protocol-agnostic display data.
  • Uses protocol-specific rows for protocol-specific custom data.
  • Adds a Solana-specific test count row from custom.count to demonstrate Snap-specific custom data updates.
  • Blocks confirmation with standard danger alerts when client-side multichain balances cannot cover the raw transfer amount plus any Snap-provided aggregate fee requirement.
  • Uses an alert row for Network Fee so universal affordability alerts render inline instead of only blocking the footer.
  • Converts client display-unit multichain balances into raw units before comparing against the raw Snap confirmation payload.

Send Flow Integration

Extension aligns non-EVM Send loading and validation with the native confirmation flow:

  • Navigates to Confirm with loader=Send while Snap approval is being prepared.
  • Replaces the old Send loader screen with the EVM-style Confirm skeleton.
  • Removes Send-time Snap onAmountInput validation for non-EVM sends.
  • Validates Send amount against client balance state before the Snap builds the transaction.
  • Displays non-EVM native Send balances at full token precision because the Max button is hidden and truncated balance text can leave hidden dust that covers fees during QA.

Balance Handling

This POC keeps native confirmation UX ownership separate from balance retrieval ownership.

The client no longer calls the Snap onAmountInput path to validate non-EVM amounts before creating the send request. Send validates entered amounts against client-owned balance state, then the native universal confirmation performs the final affordability check against the Snap-provided raw amount and aggregate fee requirement.

For supported Solana balances, Send and universal confirmation wait for Accounts API v5 balance data instead of falling back to Snap-backed balance state.

MetaMask/snap-solana-wallet

The Solana wallet Snap is the first consumer:

  • SendService builds the Solana transaction.
  • Calls snap_confirmTransaction before signing or submitting.
  • Passes account, recipient, chain ID, raw amount, optional asset ID, optional raw fee amount, optional confirmation ID, and optional custom data.
  • Treats the fee amount as the aggregate execution fee / required amount for the fee asset, not a protocol-specific rent or account-creation field.
  • Calls snap_updateConfirmTransaction while the confirmation is active to update custom.count.
  • Continues with SolMethod.SignAndSendTransaction only if approved.

Centralized Balances

Centralized non-EVM balance retrieval can be implemented separately from native confirmations.

For this POC, the client performs Send amount validation and final confirmation affordability checks using client-owned balance data. The Snap still calculates the protocol transaction and aggregate fee requirement, then passes the raw amount and fee data into the pending transaction definition.

A production balance-centralization path can reuse the same ownership split without requiring native confirmations: clients can refresh and cache non-EVM balances through controllers, pass the relevant balances to Snap-rendered flows when needed, and keep Snaps focused on protocol transaction construction and submission.

References

Changelog

CHANGELOG entry: null

Related issues

Fixes:

Manual testing steps

Screenshots/Recordings

Before

After

EVM Solana Network Fee Alert

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

…onfirm page

Routes universalTransaction approvals through the existing <Confirm> page +
<Info> dispatcher (parallel to EVM transactions) instead of a separate
/confirm-multichain-transaction route. Adds UniversalTransactionInfo with
heading, network, origin, from, to and fee rows backed by a new
useUniversalTransactionData hook.
… loader

- Route universalTransaction approvals through WalletInitiatedHeader so
  AdvancedDetailsButton renders; back button calls onCancel
- Fee row: render `<amount> [icon] SOL` right-aligned. Collapsed shows
  fiat inline; advanced toggle shows native main line with fiat sub-line
- Move Network fee into its own ConfirmInfoSection to match EVM grouping
- Show ConfirmLoader inside UniversalTransactionInfo while pending tx
  data is propagating
- Non-EVM Send submit now navigates to /confirm-transaction?loader=Send
  so the EVM-style InfoSkeleton renders while the snap creates the
  approval (instead of the send-page LOADER screen)
@matthewwalsh0 matthewwalsh0 changed the title feat(confirmations): add universal multichain Snap confirmations poc: non-evm native confirmations Jun 14, 2026
@matthewwalsh0 matthewwalsh0 added the team-confirmations Push issues to confirmations team label Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team-confirmations Push issues to confirmations team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant