Skip to content

Conversation

@ghgoodreau
Copy link
Contributor

@ghgoodreau ghgoodreau commented Sep 3, 2025

Explanation

This PR extends the bridge controller to support Bitcoin transactions, building on the existing Solana support to create a more generic non-EVM chain handling system.

Current state: The bridge controller currently only supports EVM chains and Solana for cross-chain transactions.

Solution: This PR:

  • Renames Solana-specific functions and types to be generic for all non-EVM chains (NonEvmFees
    instead of SolanaFees, handleNonEvmTx instead of handleSolanaTx)
  • Adds support for Bitcoin transactions using PSBT (Partially Signed Bitcoin Transaction) format
  • Updates the Snap interface to use the new unified computeFee and signAndSendTransaction methods
    that work across all non-EVM chains
  • Maintains backward compatibility while deprecating Solana-specific naming

Key changes:

  • The nonEvmFeesInNative field now stores fees in the smallest units for each chain
  • Transaction handling now detects Bitcoin PSBT format alongside string trade data

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

@ghgoodreau ghgoodreau requested a review from a team as a code owner September 3, 2025 20:31
@ghgoodreau ghgoodreau marked this pull request as draft September 3, 2025 20:31
cursor[bot]

This comment was marked as outdated.

@ghgoodreau ghgoodreau changed the title DRAFT: SWAPS-2839 updates snap methods to new interface in bridge controller SWAPS-2839 updates snap methods to new interface in bridge controller Sep 8, 2025
@ghgoodreau ghgoodreau marked this pull request as ready for review September 8, 2025 18:42
@ghgoodreau ghgoodreau requested a review from a team as a code owner September 8, 2025 18:42
@ghgoodreau ghgoodreau marked this pull request as draft September 9, 2025 15:27
Copy link
Contributor

@aganglada aganglada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion on the spec. The ClientRequest: bit was only to outline it it's an onClientRequest

Comment on lines 13 to 14
import { toHex } from '@metamask/controller-utils';
import { SolScope } from '@metamask/keyring-api';
import type {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! I believe you can remove keyring-api from the package.json as well

* @param chainId - The chain ID to check
* @returns True if the chain is a supported non-EVM chain, false otherwise
*/
export const isNonEvmChainId = (
Copy link
Contributor

@infiniteflower infiniteflower Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's an easier way, otherwise we need to keep updating this for every single new non EVM network

How about:

  1. Convert chainId to CAIP
  2. Check CAIP namespace for eip155
  3. If eip155, then it's EVM

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea! It does add some testing scope whereas the current functionality is already tested and working. Is this blocking? If not, I'd prefer to get these changes released to unblock the client PR, and then can update the function next week. Let me know what you think and I can add a // TODO comment @infiniteflower

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea this is non-blocking, fine with adding a TODO and addressing it in a follow up PR

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

if (isSolanaChainId(chainId)) {
// Solana fees are stored in lamports (smallest units), need to convert to SOL
const decimals = 9;
feeInNative = calcTokenAmount(nonEvmFeesInNative ?? '0', decimals);
Copy link
Member

@micaelae micaelae Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we convert the SOL amount to lamports in bridge-controller.ts if we need to convert it back to SOL here?

@micaelae micaelae merged commit 1aae93d into main Sep 22, 2025
238 of 239 checks passed
@micaelae micaelae deleted the SWAPS-2839-update-snap-methods-in-bridge-controller branch September 22, 2025 17:18

### Removed

- Remove direct dependency on `@metamask/keyring-api` - no longer needed with unified Snap interface ([#6454](https://github.com/MetaMask/core/pull/6454))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency was not removed here, this is unaccurate

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welp, I see it was actually listed twice. It's also in the changed section.

- Update response handling to support new `transactionId` format from unified interface
- Support multiple response formats: string, `{ transactionId }`, `{ result: { signature } }`, and `{ signature }`
- Maintain backward compatibility with legacy response formats
- Rename transaction handling functions for clarity ([#6454](https://github.com/MetaMask/core/pull/6454))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changelogs are meant for users of the package, not internal contributors. Please don't document internal-only changes such as internal function renames. It makes it much harder to tell what has actually changed for anyone using this package.

### Changed

- Update transaction submission to use new unified Snap interface for all non-EVM chains ([#6454](https://github.com/MetaMask/core/pull/6454))
- Replace `signAndSendTransactionWithoutConfirmation` with `ClientRequest:signAndSendTransaction` method
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change.

I'm told in practice it probably doesn't require client changes because our snaps support ClientRequest:signAndSendTransaction already. But we should still be documenting it properly here so that it's clearly understood that there is a compatibility change here.

- Update chain ID handling for non-EVM chains ([#6454](https://github.com/MetaMask/core/pull/6454))
- Add fallback chain ID (`0x0`) when CAIP format can't be converted to hex for source chains
- Add fallback chain ID (`0x1`) for non-EVM destination chains
- Update `getClientRequest` to create proper requests for all non-EVM chains ([#6454](https://github.com/MetaMask/core/pull/6454))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an internal change as well (a subset of "Add support for Bitcoin bridge transactions"). No need to document internal changes like this.


- **BREAKING:** Rename fee handling for non-EVM chains ([#6454](https://github.com/MetaMask/core/pull/6454))
- Replace `SolanaFees` type with `NonEvmFees` type
- Replace `solanaFeesInLamports` field with `nonEvmFeesInNative` field
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unclear what is being renamed here, is this a property on a return value? The context made it seem like it was a package export or a method or something. More context would be appreciated here

- **BREAKING:** Rename fee handling for non-EVM chains ([#6454](https://github.com/MetaMask/core/pull/6454))
- Replace `SolanaFees` type with `NonEvmFees` type
- Replace `solanaFeesInLamports` field with `nonEvmFeesInNative` field
- Update `#appendSolanaFees` to `#appendNonEvmFees` to support all non-EVM chains
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line seems like an internal-only change

- Replace `getFeeForTransaction` with `computeFee` method that returns fees in native token units
- Update fee calculation to handle different unit conversions per chain
- Support fee computation for Bitcoin and Solana chains
- Update quote validation to support Bitcoin-specific trade data format ([#6454](https://github.com/MetaMask/core/pull/6454))
Copy link
Member

@Gudahtt Gudahtt Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is fine, but maybe better to move these two entries ("Update quote validation" and "Update selectors and utilities") as sub-bullets of "Add support for Bitcoin bridge transactions". It seems like a detail of that addition, I don't think it's as helpful to frame it as a standalone change.

- Update `#appendSolanaFees` to `#appendNonEvmFees` to support all non-EVM chains
- The `nonEvmFeesInNative` field stores fees in the smallest units for each chain (lamports for Solana, satoshis for Bitcoin)
- Update Snap methods to use new unified interface for non-EVM chains ([#6454](https://github.com/MetaMask/core/pull/6454))
- Replace `getFeeForTransaction` with `computeFee` method that returns fees in native token units
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a breaking change as well. This is a change in the expectations we have of snaps this controller inter-operates with.

snapId: snapId as never,
origin: 'metamask',
handler: 'onRpcRequest' as never,
handler: 'onClientRequest' as never,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why cast to never 🤔 As the name might imply, never should never be resolved on any of our types. It's meant to alert when a conditional type is broken.

Gudahtt added a commit that referenced this pull request Sep 23, 2025
)

## Explanation

This reverts commit 1aae93d, which was
accidentally released as part of v44 with a bunch of undocumented
breaking changes.

## References

See #6454

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs),
highlighting breaking changes as necessary
- [x] I've prepared draft pull requests for clients and consumer
packages to resolve any breaking changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants