Skip to content

Commit a5403b3

Browse files
authored
feat: implement solana snap onClientRequest (MetaMask#5961)
1 parent 1a19d0e commit a5403b3

11 files changed

Lines changed: 317 additions & 34 deletions

File tree

packages/bridge-controller/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Export feature flag util for bridge status controller ([#5961](https://github.com/MetaMask/core/pull/5961))
13+
1014
## [32.1.2]
1115

1216
### Changed

packages/bridge-controller/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,5 @@ export {
129129
} from './selectors';
130130

131131
export { DEFAULT_FEATURE_FLAG_CONFIG } from './constants/bridge';
132+
133+
export { getBridgeFeatureFlags } from './utils/feature-flags';

packages/bridge-controller/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export type ChainConfiguration = {
6565
refreshRate?: number;
6666
topAssets?: string[];
6767
isUnifiedUIEnabled?: boolean;
68+
isSnapConfirmationEnabled?: boolean;
6869
};
6970

7071
export type L1GasFees = {

packages/bridge-controller/src/utils/feature-flags.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1+
import type { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';
2+
13
import { formatChainIdToCaip } from './caip-formatters';
24
import { validateFeatureFlagsResponse } from './validators';
35
import { DEFAULT_FEATURE_FLAG_CONFIG } from '../constants/bridge';
4-
import type {
5-
FeatureFlagsPlatformConfig,
6-
ChainConfiguration,
7-
BridgeControllerMessenger,
8-
} from '../types';
6+
import type { FeatureFlagsPlatformConfig, ChainConfiguration } from '../types';
97

108
export const formatFeatureFlags = (
119
bridgeFeatureFlags: FeatureFlagsPlatformConfig,
@@ -37,12 +35,16 @@ export const processFeatureFlags = (
3735
/**
3836
* Gets the bridge feature flags from the remote feature flag controller
3937
*
40-
* @param messenger - The messenger instance
38+
* @param messenger - Any messenger with access to RemoteFeatureFlagController:getState
4139
* @returns The bridge feature flags
4240
*/
43-
export function getBridgeFeatureFlags(
44-
messenger: BridgeControllerMessenger,
45-
): FeatureFlagsPlatformConfig {
41+
export function getBridgeFeatureFlags<
42+
T extends {
43+
call(
44+
action: 'RemoteFeatureFlagController:getState',
45+
): RemoteFeatureFlagControllerState;
46+
},
47+
>(messenger: T): FeatureFlagsPlatformConfig {
4648
// This will return the bridgeConfig for the current platform even without specifying the platform
4749
const remoteFeatureFlagControllerState = messenger.call(
4850
'RemoteFeatureFlagController:getState',

packages/bridge-status-controller/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- **BREAKING:** Implement onClientRequest for Solana snap transactions, now requires action permission for RemoteFeatureFlagController:getState ([#5961](https://github.com/MetaMask/core/pull/5961))
13+
1014
## [29.1.1]
1115

1216
### Changed

packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2854,29 +2854,24 @@ Array [
28542854
Array [
28552855
"AccountsController:getSelectedMultichainAccount",
28562856
],
2857+
Array [
2858+
"RemoteFeatureFlagController:getState",
2859+
],
28572860
Array [
28582861
"SnapController:handleRequest",
28592862
Object {
2860-
"handler": "onKeyringRequest",
2863+
"handler": "onClientRequest",
28612864
"origin": "metamask",
28622865
"request": Object {
28632866
"id": "test-uuid-1234",
28642867
"jsonrpc": "2.0",
2865-
"method": "keyring_submitRequest",
2868+
"method": "signAndSendTransactionWithoutConfirmation",
28662869
"params": Object {
2867-
"account": undefined,
2868-
"id": "test-uuid-1234",
2869-
"request": Object {
2870-
"method": "signAndSendTransaction",
2871-
"params": Object {
2872-
"account": Object {
2873-
"address": "0x123...",
2874-
},
2875-
"scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
2876-
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHDXLY8oVRIwA8ZdRSGjM5RIZJW8Wv+Twyw3NqU4Hov+OHoHp/dmeDvstKbICW3ezeGR69t3/PTAvdXgZVdJFJXaxkoKXUTWfEAyQyCCG9nwVoDsd10OFdnM9ldSi+9SLqHpqWVDV+zzkmftkF//DpbXxqeH8obNXHFR7pUlxG9uNVOn64oNsFdeUvD139j1M51iRmUY839Y25ET4jDRscT081oGb+rLnywLjLSrIQx6MkqNBhCFbxqY1YmoGZVORW/QMGRm/lIRcy/+ytunLDm+e8jOW7xfcSayxDmzpAAAAAjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+FkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpBHnVW/IxwG7udMVuzmgVB/2xst6j9I5RArHNola8E4+0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6JmXkZ+niuxMhAGrmKBaBo94uMv2Sl+Xh3i+VOO0m5BdNZ1ElenbwQylHQY+VW1ydG1MaUEeNpG+EVgswzPMwPoLBgAFAsBcFQAGAAkDQA0DAAAAAAAHBgABAhMICQAHBgADABYICQEBCAIAAwwCAAAAUEYVOwAAAAAJAQMBEQoUCQADBAETCgsKFw0ODxARAwQACRQj5RfLl3rjrSoBAAAAQ2QAAVBGFTsAAAAAyYZnBwAAAABkAAAJAwMAAAEJDAkAAAIBBBMVCQjGASBMKQwnooTbKNxdBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUHTKomh4KXvNgA0ovYKS5F8GIOBgAAAAAAAAAAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAEIF7RFOAwAAAAAAAAAAAAAAaAIAAAAAAAC4CwAAAAAAAOAA2mcAAAAAAAAAAAAAAAAAAAAApapuIXG0FuHSfsU8qME9s/kaic0AAwGCsZdSuxV5eCm+Ria4LEQPgTg4bg65gNrTAefEzpAfPQgCABIMAgAAAAAAAAAAAAAACAIABQwCAAAAsIOFAAAAAAADWk6DVOZO8lMFQg2r0dgfltD6tRL/B1hH3u00UzZdgqkAAxEqIPdq2eRt/F6mHNmFe7iwZpdrtGmHNJMFlK7c6Bc6k6kjBezr6u/tAgvu3OGsJSwSElmcOHZ21imqH/rhJ2KgqDJdBPFH4SYIM1kBAAA=",
2877-
},
2870+
"account": Object {
2871+
"address": "0x123...",
28782872
},
28792873
"scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
2874+
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHDXLY8oVRIwA8ZdRSGjM5RIZJW8Wv+Twyw3NqU4Hov+OHoHp/dmeDvstKbICW3ezeGR69t3/PTAvdXgZVdJFJXaxkoKXUTWfEAyQyCCG9nwVoDsd10OFdnM9ldSi+9SLqHpqWVDV+zzkmftkF//DpbXxqeH8obNXHFR7pUlxG9uNVOn64oNsFdeUvD139j1M51iRmUY839Y25ET4jDRscT081oGb+rLnywLjLSrIQx6MkqNBhCFbxqY1YmoGZVORW/QMGRm/lIRcy/+ytunLDm+e8jOW7xfcSayxDmzpAAAAAjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+FkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpBHnVW/IxwG7udMVuzmgVB/2xst6j9I5RArHNola8E4+0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6JmXkZ+niuxMhAGrmKBaBo94uMv2Sl+Xh3i+VOO0m5BdNZ1ElenbwQylHQY+VW1ydG1MaUEeNpG+EVgswzPMwPoLBgAFAsBcFQAGAAkDQA0DAAAAAAAHBgABAhMICQAHBgADABYICQEBCAIAAwwCAAAAUEYVOwAAAAAJAQMBEQoUCQADBAETCgsKFw0ODxARAwQACRQj5RfLl3rjrSoBAAAAQ2QAAVBGFTsAAAAAyYZnBwAAAABkAAAJAwMAAAEJDAkAAAIBBBMVCQjGASBMKQwnooTbKNxdBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUHTKomh4KXvNgA0ovYKS5F8GIOBgAAAAAAAAAAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAEIF7RFOAwAAAAAAAAAAAAAAaAIAAAAAAAC4CwAAAAAAAOAA2mcAAAAAAAAAAAAAAAAAAAAApapuIXG0FuHSfsU8qME9s/kaic0AAwGCsZdSuxV5eCm+Ria4LEQPgTg4bg65gNrTAefEzpAfPQgCABIMAgAAAAAAAAAAAAAACAIABQwCAAAAsIOFAAAAAAADWk6DVOZO8lMFQg2r0dgfltD6tRL/B1hH3u00UzZdgqkAAxEqIPdq2eRt/F6mHNmFe7iwZpdrtGmHNJMFlK7c6Bc6k6kjBezr6u/tAgvu3OGsJSwSElmcOHZ21imqH/rhJ2KgqDJdBPFH4SYIM1kBAAA=",
28802875
},
28812876
},
28822877
"snapId": "test-snap",
@@ -2892,7 +2887,7 @@ Array [
28922887
],
28932888
Array [
28942889
"AccountsController:getAccountByAddress",
2895-
"0x123...",
2890+
"",
28962891
],
28972892
Array [
28982893
"BridgeController:trackUnifiedSwapBridgeEvent",
@@ -2916,7 +2911,7 @@ Array [
29162911
"quoted_vs_used_gas_ratio": 1,
29172912
"security_warnings": Array [],
29182913
"slippage_limit": 0,
2919-
"source_transaction": "COMPLETE",
2914+
"source_transaction": "PENDING",
29202915
"stx_enabled": false,
29212916
"swap_type": "crosschain",
29222917
"token_address_destination": "eip155:1/slip44:60",
@@ -2942,7 +2937,7 @@ Object {
29422937
"destinationTokenAmount": "0.5",
29432938
"destinationTokenDecimals": 18,
29442939
"destinationTokenSymbol": "ETH",
2945-
"hash": "signature",
2940+
"hash": undefined,
29462941
"id": "test-uuid-1234",
29472942
"isBridgeTx": true,
29482943
"isSolana": true,
@@ -2974,7 +2969,7 @@ Object {
29742969
"destinationTokenAmount": "0.5",
29752970
"destinationTokenDecimals": 18,
29762971
"destinationTokenSymbol": "ETH",
2977-
"hash": "signature",
2972+
"hash": undefined,
29782973
"id": "test-uuid-1234",
29792974
"isBridgeTx": true,
29802975
"isSolana": true,
@@ -3180,7 +3175,7 @@ Object {
31803175
},
31813176
"refuel": false,
31823177
"srcChainId": 1151111081099710,
3183-
"srcTxHash": "signature",
3178+
"srcTxHash": undefined,
31843179
},
31853180
}
31863181
`;

packages/bridge-status-controller/src/bridge-status-controller.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,19 @@ const getMessengerMock = ({
520520
},
521521
],
522522
};
523+
} else if (method === 'RemoteFeatureFlagController:getState') {
524+
return {
525+
remoteFeatureFlags: {
526+
bridgeConfig: {
527+
support: true,
528+
chains: {
529+
[ChainId.SOLANA]: {
530+
isSnapConfirmationEnabled: false,
531+
},
532+
},
533+
},
534+
},
535+
};
523536
}
524537
return null;
525538
}),
@@ -566,7 +579,23 @@ const executePollingWithPendingStatus = async () => {
566579

567580
// Define mocks at the top level
568581
const mockFetchFn = jest.fn();
569-
const mockMessengerCall = jest.fn();
582+
const mockMessengerCall = jest.fn().mockImplementation((method: string) => {
583+
if (method === 'RemoteFeatureFlagController:getState') {
584+
return {
585+
remoteFeatureFlags: {
586+
bridgeConfig: {
587+
support: true,
588+
chains: {
589+
[ChainId.SOLANA]: {
590+
isSnapConfirmationEnabled: false,
591+
},
592+
},
593+
},
594+
},
595+
};
596+
}
597+
return null;
598+
});
570599
const mockSelectedAccount = {
571600
id: 'test-account-id',
572601
address: '0xaccount1',
@@ -1486,6 +1515,19 @@ describe('BridgeStatusController', () => {
14861515

14871516
it('should handle snap controller errors', async () => {
14881517
mockMessengerCall.mockReturnValueOnce(mockSolanaAccount);
1518+
// Mock the RemoteFeatureFlagController:getState call that happens in getBridgeFeatureFlags
1519+
mockMessengerCall.mockReturnValueOnce({
1520+
remoteFeatureFlags: {
1521+
bridgeConfig: {
1522+
support: true,
1523+
chains: {
1524+
[ChainId.SOLANA]: {
1525+
isSnapConfirmationEnabled: false,
1526+
},
1527+
},
1528+
},
1529+
},
1530+
});
14891531
mockMessengerCall.mockRejectedValueOnce(new Error('Snap error'));
14901532

14911533
const { controller, startPollingForBridgeTxStatusSpy } =

packages/bridge-status-controller/src/bridge-status-controller.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import {
1515
getActionType,
1616
formatChainIdToCaip,
1717
isCrossChain,
18+
getBridgeFeatureFlags,
1819
isHardwareWallet,
1920
} from '@metamask/bridge-controller';
2021
import type { TraceCallback } from '@metamask/controller-utils';
2122
import { toHex } from '@metamask/controller-utils';
22-
import { EthAccountType } from '@metamask/keyring-api';
23+
import { EthAccountType, SolScope } from '@metamask/keyring-api';
2324
import { StaticIntervalPollingController } from '@metamask/polling-controller';
2425
import type {
2526
TransactionController,
@@ -63,6 +64,7 @@ import {
6364
getTxStatusesFromHistory,
6465
} from './utils/metrics';
6566
import {
67+
getClientRequest,
6668
getKeyringRequest,
6769
getStatusRequestParams,
6870
getTxMetaFields,
@@ -560,15 +562,20 @@ export class BridgeStatusController extends StaticIntervalPollingController<Brid
560562
'Failed to submit cross-chain swap transaction: undefined snap id',
561563
);
562564
}
563-
const keyringRequest = getKeyringRequest(quoteResponse, selectedAccount);
564-
const keyringResponse = (await this.messagingSystem.call(
565+
566+
const bridgeFeatureFlags = getBridgeFeatureFlags(this.messagingSystem);
567+
const request = bridgeFeatureFlags?.chains?.[SolScope.Mainnet]
568+
?.isSnapConfirmationEnabled
569+
? getKeyringRequest(quoteResponse, selectedAccount)
570+
: getClientRequest(quoteResponse, selectedAccount);
571+
const requestResponse = (await this.messagingSystem.call(
565572
'SnapController:handleRequest',
566-
keyringRequest,
573+
request,
567574
)) as string | { result: Record<string, string> };
568575

569576
// The extension client actually redirects before it can do anytyhing with this meta
570577
const txMeta = handleSolanaTxResponse(
571-
keyringResponse,
578+
requestResponse,
572579
quoteResponse,
573580
selectedAccount,
574581
);

packages/bridge-status-controller/src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import type {
2424
NetworkControllerGetNetworkClientByIdAction,
2525
NetworkControllerGetStateAction,
2626
} from '@metamask/network-controller';
27+
import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller';
2728
import type { HandleSnapRequest } from '@metamask/snaps-controllers';
2829
import type {
2930
TransactionControllerGetStateAction,
@@ -335,7 +336,8 @@ type AllowedActions =
335336
| BridgeControllerAction<BridgeBackgroundAction.GET_BRIDGE_ERC20_ALLOWANCE>
336337
| BridgeControllerAction<BridgeBackgroundAction.TRACK_METAMETRICS_EVENT>
337338
| GetGasFeeState
338-
| AccountsControllerGetAccountByAddressAction;
339+
| AccountsControllerGetAccountByAddressAction
340+
| RemoteFeatureFlagControllerGetStateAction;
339341

340342
/**
341343
* The external events available to the BridgeStatusController.

0 commit comments

Comments
 (0)