From 44b7ded210c3e04f4454190b76de105f574604a4 Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Fri, 9 Oct 2026 16:11:52 +0200 Subject: [PATCH 1/7] feat(bridge-status-controller): pass swap asset ids to Tron snap requests Include the sourceAssetId and destAssetId in the Tron signAndSendTransaction options so the Tron snap can classify the transaction as a same-chain swap or a cross-chain bridge, mirroring the Stellar snap. --- oxlint-suppressions.json | 2 +- .../bridge-status-controller/CHANGELOG.md | 1 + .../bridge-status-controller.test.ts.snap | 10 ++ .../src/utils/snaps.ts | 18 ++- .../src/utils/transaction.test.ts | 103 +++++++++++++----- 5 files changed, 103 insertions(+), 31 deletions(-) diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index efbd8787270..0d6e76ae243 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -2238,7 +2238,7 @@ "count": 2 }, "typescript/no-unsafe-assignment": { - "count": 5 + "count": 7 }, "typescript/no-unsafe-return": { "count": 2 diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index 9856208cb79..3fd33894213 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Pass the swap/bridge `sourceAssetId` and `destAssetId` to Tron Snap `signAndSendTransaction` requests. ([#10775](https://github.com/MetaMask/core/pull/10775)) - Bump `@metamask/profile-sync-controller` from `^34.0.0` to `^34.0.3` ([#10752](https://github.com/MetaMask/core/pull/10752), [#10756](https://github.com/MetaMask/core/pull/10756), [#10761](https://github.com/MetaMask/core/pull/10761)) - Bump `@metamask/snaps-controllers` from `^19.0.0` to `^19.0.1` ([#10719](https://github.com/MetaMask/core/pull/10719)) - Bump `@metamask/transaction-controller` from `^72.1.0` to `^72.2.0` ([#10772](https://github.com/MetaMask/core/pull/10772)) diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 2352bef56b2..98eb0e9fb47 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -5826,6 +5826,8 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should handle "params": { "accountId": "tron-account-1", "options": { + "destAssetId": "tron:728126428/slip44:195", + "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, @@ -5929,6 +5931,8 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should success "params": { "accountId": "tron-account-1", "options": { + "destAssetId": "tron:728126428/slip44:195", + "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, @@ -5951,6 +5955,8 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should success "params": { "accountId": "tron-account-1", "options": { + "destAssetId": "tron:728126428/slip44:195", + "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, @@ -6152,6 +6158,8 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should success "params": { "accountId": "tron-account-1", "options": { + "destAssetId": "tron:728126428/slip44:195", + "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, @@ -6174,6 +6182,8 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should success "params": { "accountId": "tron-account-1", "options": { + "destAssetId": "tron:728126428/slip44:195", + "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index 1117a074cd5..5f5e3f98673 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -91,9 +91,10 @@ export const getClientRequest = ( let options: Record | undefined; - // Only Stellar trades expect asset IDs in the request options. Passing them - // for other non-EVM chains (e.g. Bitcoin) breaks strict snap request - // validation and prevents the transaction from being broadcast. + // Stellar and Tron trades expect asset IDs in the request options; the Tron + // fields are added in the branch below. Passing them for any other non-EVM + // chain (e.g. Bitcoin) breaks strict snap request validation and prevents the + // transaction from being broadcast. if (isStellarTrade(trade)) { if (sourceAssetId !== undefined || destAssetId !== undefined) { options = { @@ -108,10 +109,19 @@ export const getClientRequest = ( } if (isTronTrade(trade)) { - // Tron trades need the visible flag and contract type to be included in the request options + // Tron trades need the visible flag and contract type to be included in the + // request options. The swap/bridge asset ids are also passed so the Tron + // snap can classify the transaction as a same-chain swap or a cross-chain + // bridge, mirroring the Stellar snap. options = { visible: trade.visible, type: trade.raw_data?.contract?.[0]?.type, + ...(sourceAssetId !== undefined && { + sourceAssetId, + }), + ...(destAssetId !== undefined && { + destAssetId, + }), }; } diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 44d99b443d8..010e10adeec 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1619,11 +1619,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }); }); - it('should include Tron options when trade is Tron', () => { - const createClientRequestSpy = jest - .spyOn(snaps, 'getClientRequest') - .mockReturnValue({ mocked: true } as never); - + it('should include Tron options, including swap asset IDs, when trade is Tron', () => { const tronTrade = { raw_data_hex: 'abcdef', raw_data: { @@ -1639,36 +1635,91 @@ describe('Bridge Status Controller Transaction Utils', () => { }, }; + const sourceAssetId = + 'tron:728126428/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; + const destAssetId = + 'eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; + const result = snaps.getClientRequest( tronTrade, ChainId.TRON, mockAccount.id, mockAccount.metadata.snap.id, + sourceAssetId, + destAssetId, ); - expect(result).toStrictEqual({ mocked: true }); - expect(createClientRequestSpy.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "raw_data": { - "contract": [ - { - "type": "TransferContract", - }, - ], - }, - "raw_data_hex": "abcdef", - "visible": true, + expect(result).toMatchObject({ + origin: 'metamask', + snapId: 'test-snap-id', + handler: 'onClientRequest', + request: { + id: expect.any(String), + jsonrpc: '2.0', + method: 'signAndSendTransaction', + params: { + transaction: 'q83v', + scope: formatChainIdToCaip(ChainId.TRON), + accountId: 'test-account-id', + options: { + visible: true, + type: 'TransferContract', + sourceAssetId, + destAssetId, }, - 728126428, - "test-account-id", - "test-snap-id", - ], - ] - `); + }, + }, + }); + }); + + it('should omit Tron asset ID options when source and destination asset IDs are not provided', () => { + const tronTrade = { + raw_data_hex: 'abcdef', + raw_data: { + contract: [{ type: 'TransferContract' }], + }, + visible: true, + } as never; + + const mockAccount = { + id: 'test-account-id', + metadata: { + snap: { id: 'test-snap-id' }, + }, + }; - createClientRequestSpy.mockRestore(); + const result = snaps.getClientRequest( + tronTrade, + ChainId.TRON, + mockAccount.id, + mockAccount.metadata.snap.id, + ); + + expect(result).toMatchObject({ + origin: 'metamask', + snapId: 'test-snap-id', + handler: 'onClientRequest', + request: { + id: expect.any(String), + jsonrpc: '2.0', + method: 'signAndSendTransaction', + params: { + transaction: 'q83v', + scope: formatChainIdToCaip(ChainId.TRON), + accountId: 'test-account-id', + options: { + visible: true, + type: 'TransferContract', + }, + }, + }, + }); + expect( + (result.request.params as { options: Record }).options, + ).not.toHaveProperty('sourceAssetId'); + expect( + (result.request.params as { options: Record }).options, + ).not.toHaveProperty('destAssetId'); }); it('should include Stellar source and destination asset IDs as options when trade is Stellar', () => { From 1c15fb1fb4db505e573f2dffa7b39ca7896d0e32 Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Fri, 9 Oct 2026 19:46:58 +0200 Subject: [PATCH 2/7] feat(bridge-status-controller): pass swap asset ids to Solana snap requests Include the sourceAssetId and destAssetId in the Solana signAndSendTransaction options so the Solana snap can classify the transaction as a same-chain swap or a cross-chain bridge, mirroring the Stellar snap. The Solana and Stellar branches share the same asset-id-only options shape, so they are combined into a single condition. --- oxlint-suppressions.json | 2 +- .../bridge-status-controller/CHANGELOG.md | 1 + .../bridge-status-controller.test.ts.snap | 16 ++++++++ .../src/utils/snaps.ts | 11 +++-- .../src/utils/transaction.test.ts | 40 ++++++++++++++++++- 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index efbd8787270..3a6d8078b8a 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -2238,7 +2238,7 @@ "count": 2 }, "typescript/no-unsafe-assignment": { - "count": 5 + "count": 6 }, "typescript/no-unsafe-return": { "count": 2 diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index 9856208cb79..c2788cc61a0 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Pass the swap/bridge `sourceAssetId` and `destAssetId` to Solana Snap `signAndSendTransaction` requests, so the Solana Snap can classify the transaction as a same-chain swap or a cross-chain bridge. - Bump `@metamask/profile-sync-controller` from `^34.0.0` to `^34.0.3` ([#10752](https://github.com/MetaMask/core/pull/10752), [#10756](https://github.com/MetaMask/core/pull/10756), [#10761](https://github.com/MetaMask/core/pull/10761)) - Bump `@metamask/snaps-controllers` from `^19.0.0` to `^19.0.1` ([#10719](https://github.com/MetaMask/core/pull/10719)) - Bump `@metamask/transaction-controller` from `^72.1.0` to `^72.2.0` ([#10772](https://github.com/MetaMask/core/pull/10772)) diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 2352bef56b2..b623b2b0fbb 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -5012,6 +5012,10 @@ exports[`BridgeStatusController submitTx: Solana bridge should handle snap contr "method": "signAndSendTransaction", "params": { "accountId": "solana-account-1", + "options": { + "destAssetId": "eip155:1/slip44:60", + "sourceAssetId": "eip155:1399811149/slip44:501", + }, "scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "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=", }, @@ -5111,6 +5115,10 @@ exports[`BridgeStatusController submitTx: Solana bridge should successfully subm "method": "signAndSendTransaction", "params": { "accountId": "solana-account-1", + "options": { + "destAssetId": "eip155:1/slip44:60", + "sourceAssetId": "eip155:1399811149/slip44:501", + }, "scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "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=", }, @@ -5391,6 +5399,10 @@ exports[`BridgeStatusController submitTx: Solana swap should handle snap control "method": "signAndSendTransaction", "params": { "accountId": "solana-account-1", + "options": { + "destAssetId": "eip155:1399811149/slip44:501", + "sourceAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", + }, "scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "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=", }, @@ -5490,6 +5502,10 @@ exports[`BridgeStatusController submitTx: Solana swap should successfully submit "method": "signAndSendTransaction", "params": { "accountId": "solana-account-1", + "options": { + "destAssetId": "eip155:1399811149/slip44:501", + "sourceAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", + }, "scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "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=", }, diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index 1117a074cd5..6bed9259368 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -10,6 +10,7 @@ import { formatChainIdToCaip, formatChainIdToHex, isCrossChain, + isSolanaChainId, isStellarTrade, isTronTrade, } from '@metamask/bridge-controller'; @@ -91,10 +92,12 @@ export const getClientRequest = ( let options: Record | undefined; - // Only Stellar trades expect asset IDs in the request options. Passing them - // for other non-EVM chains (e.g. Bitcoin) breaks strict snap request - // validation and prevents the transaction from being broadcast. - if (isStellarTrade(trade)) { + // Stellar and Solana trades expect asset IDs in the request options, and + // nothing else. Passing them for any other non-EVM chain (e.g. Bitcoin) + // breaks strict snap request validation and prevents the transaction from + // being broadcast. The asset IDs let the snap classify the transaction as a + // same-chain swap or a cross-chain bridge. + if (isStellarTrade(trade) || isSolanaChainId(srcChainId)) { if (sourceAssetId !== undefined || destAssetId !== undefined) { options = { ...(sourceAssetId !== undefined && { diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 44d99b443d8..02c5e904e7c 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1779,7 +1779,7 @@ describe('Bridge Status Controller Transaction Utils', () => { expect(result.request.params).not.toHaveProperty('options'); }); - it('should not include asset ID options for Solana trades even when asset IDs are provided', () => { + it('should include Solana asset ID options when asset IDs are provided', () => { const solanaTrade = 'ABCD' as never; const mockAccount = { @@ -1801,6 +1801,44 @@ describe('Bridge Status Controller Transaction Utils', () => { destAssetId, ); + expect(result).toMatchObject({ + origin: 'metamask', + snapId: 'test-snap-id', + handler: 'onClientRequest', + request: { + id: expect.any(String), + jsonrpc: '2.0', + method: 'signAndSendTransaction', + params: { + transaction: 'ABCD', + scope: formatChainIdToCaip(ChainId.SOLANA), + accountId: 'test-account-id', + options: { + sourceAssetId, + destAssetId, + }, + }, + }, + }); + }); + + it('should omit Solana asset ID options when source and destination asset IDs are not provided', () => { + const solanaTrade = 'ABCD' as never; + + const mockAccount = { + id: 'test-account-id', + metadata: { + snap: { id: 'test-snap-id' }, + }, + }; + + const result = snaps.getClientRequest( + solanaTrade, + ChainId.SOLANA, + mockAccount.id, + mockAccount.metadata.snap.id, + ); + expect(result.request.params).not.toHaveProperty('options'); }); }); From 92368fadcf9c103ed0b0dbd9d4ad8f49ec8e3e14 Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Sat, 10 Oct 2026 12:27:59 +0200 Subject: [PATCH 3/7] feat(bridge-status-controller): pass swap asset ids to Bitcoin snap requests Include the sourceAssetId and destAssetId in the Bitcoin signAndSendTransaction options so the Bitcoin snap can classify the transaction as a same-chain swap or a cross-chain bridge, mirroring the Stellar snap. The Bitcoin snap ignores unknown options, so no strict rollout ordering is required. --- oxlint-suppressions.json | 2 +- .../bridge-status-controller/CHANGELOG.md | 1 + .../src/utils/snaps.ts | 11 +++-- .../src/utils/transaction.test.ts | 42 ++++++++++++++++++- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index efbd8787270..3a6d8078b8a 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -2238,7 +2238,7 @@ "count": 2 }, "typescript/no-unsafe-assignment": { - "count": 5 + "count": 6 }, "typescript/no-unsafe-return": { "count": 2 diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index 9856208cb79..645c32fb507 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Pass the swap/bridge `sourceAssetId` and `destAssetId` to Bitcoin Snap `signAndSendTransaction` requests. - Bump `@metamask/profile-sync-controller` from `^34.0.0` to `^34.0.3` ([#10752](https://github.com/MetaMask/core/pull/10752), [#10756](https://github.com/MetaMask/core/pull/10756), [#10761](https://github.com/MetaMask/core/pull/10761)) - Bump `@metamask/snaps-controllers` from `^19.0.0` to `^19.0.1` ([#10719](https://github.com/MetaMask/core/pull/10719)) - Bump `@metamask/transaction-controller` from `^72.1.0` to `^72.2.0` ([#10772](https://github.com/MetaMask/core/pull/10772)) diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index 1117a074cd5..af48e716a53 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -9,6 +9,7 @@ import { extractTradeData, formatChainIdToCaip, formatChainIdToHex, + isBitcoinTrade, isCrossChain, isStellarTrade, isTronTrade, @@ -91,10 +92,12 @@ export const getClientRequest = ( let options: Record | undefined; - // Only Stellar trades expect asset IDs in the request options. Passing them - // for other non-EVM chains (e.g. Bitcoin) breaks strict snap request - // validation and prevents the transaction from being broadcast. - if (isStellarTrade(trade)) { + // Stellar and Bitcoin trades expect asset IDs in the request options, and + // nothing else. Passing them for any other non-EVM chain breaks strict snap + // request validation and prevents the transaction from being broadcast. The + // asset IDs let the snap classify the transaction as a same-chain swap or a + // cross-chain bridge. + if (isStellarTrade(trade) || isBitcoinTrade(trade)) { if (sourceAssetId !== undefined || destAssetId !== undefined) { options = { ...(sourceAssetId !== undefined && { diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 44d99b443d8..219d125c03d 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1752,7 +1752,7 @@ describe('Bridge Status Controller Transaction Utils', () => { ).not.toHaveProperty('destAssetId'); }); - it('should not include asset ID options for Bitcoin trades even when asset IDs are provided', () => { + it('should include Bitcoin asset ID options when asset IDs are provided', () => { const bitcoinTrade = { unsignedPsbtBase64: 'AAAABg==', } as never; @@ -1776,6 +1776,46 @@ describe('Bridge Status Controller Transaction Utils', () => { destAssetId, ); + expect(result).toMatchObject({ + origin: 'metamask', + snapId: 'test-snap-id', + handler: 'onClientRequest', + request: { + id: expect.any(String), + jsonrpc: '2.0', + method: 'signAndSendTransaction', + params: { + transaction: 'AAAABg==', + scope: formatChainIdToCaip(ChainId.BTC), + accountId: 'test-account-id', + options: { + sourceAssetId, + destAssetId, + }, + }, + }, + }); + }); + + it('should omit Bitcoin asset ID options when source and destination asset IDs are not provided', () => { + const bitcoinTrade = { + unsignedPsbtBase64: 'AAAABg==', + } as never; + + const mockAccount = { + id: 'test-account-id', + metadata: { + snap: { id: 'test-snap-id' }, + }, + }; + + const result = snaps.getClientRequest( + bitcoinTrade, + ChainId.BTC, + mockAccount.id, + mockAccount.metadata.snap.id, + ); + expect(result.request.params).not.toHaveProperty('options'); }); From 67548be44f426d89623b09a40f6897ae187ed6c9 Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Sat, 10 Oct 2026 15:31:10 +0200 Subject: [PATCH 4/7] refactor(bridge-status-controller): use chain-id guards for snap asset ids Replace the mixed isStellarTrade/isBitcoinTrade guards with isStellarChainId/isBitcoinChainId so all three non-Tron chains use the same chain-id check, since getClientRequest already receives srcChainId. The Tron branch is unchanged as it reads fields from the trade object. --- .../src/utils/snaps.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index 253f8c658a5..c73d13983fb 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -9,10 +9,10 @@ import { extractTradeData, formatChainIdToCaip, formatChainIdToHex, - isBitcoinTrade, + isBitcoinChainId, isCrossChain, isSolanaChainId, - isStellarTrade, + isStellarChainId, isTronTrade, } from '@metamask/bridge-controller'; import { SnapController } from '@metamask/snaps-controllers'; @@ -93,16 +93,13 @@ export const getClientRequest = ( let options: Record | undefined; - // Stellar, Solana, Bitcoin, and Tron trades expect asset IDs in the request - // options. For Stellar, Solana, and Bitcoin the options contain nothing else; - // Tron also adds its own fields in the branch below. Passing the asset IDs for - // any other non-EVM chain breaks strict snap request validation and prevents - // the transaction from being broadcast. The asset IDs let the snap classify - // the transaction as a same-chain swap or a cross-chain bridge. + // Stellar, Solana, and Bitcoin trades expect the swap/bridge asset IDs in the + // request options; Tron also adds its own fields in the + // branch below. if ( - isStellarTrade(trade) || + isStellarChainId(srcChainId) || isSolanaChainId(srcChainId) || - isBitcoinTrade(trade) + isBitcoinChainId(srcChainId) ) { if (sourceAssetId !== undefined || destAssetId !== undefined) { options = { @@ -117,10 +114,6 @@ export const getClientRequest = ( } if (isTronTrade(trade)) { - // Tron trades need the visible flag and contract type to be included in the - // request options. The swap/bridge asset ids are also passed so the Tron - // snap can classify the transaction as a same-chain swap or a cross-chain - // bridge, mirroring the Stellar snap. options = { visible: trade.visible, type: trade.raw_data?.contract?.[0]?.type, From dc886c9dc133e4c6e83b7ee09dc05c4fdb5c20db Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Sat, 10 Oct 2026 16:06:03 +0200 Subject: [PATCH 5/7] docs(bridge-status-controller): reference the PR in the changelog Add the pull request reference to the combined Tron, Solana, and Bitcoin changelog entry. --- packages/bridge-status-controller/CHANGELOG.md | 2 +- packages/bridge-status-controller/src/utils/snaps.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index d365c0274fd..0da67d862fa 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Pass the swap/bridge `sourceAssetId` and `destAssetId` to Tron, Solana, and Bitcoin Snap `signAndSendTransaction` requests, so those Snaps can classify the transaction as a same-chain swap or a cross-chain bridge. +- Pass the swap/bridge `sourceAssetId` and `destAssetId` to Tron, Solana, and Bitcoin Snap `signAndSendTransaction` requests. ([#10775](https://github.com/MetaMask/core/pull/10775)) - Bump `@metamask/profile-sync-controller` from `^34.0.0` to `^34.0.3` ([#10752](https://github.com/MetaMask/core/pull/10752), [#10756](https://github.com/MetaMask/core/pull/10756), [#10761](https://github.com/MetaMask/core/pull/10761)) - Bump `@metamask/snaps-controllers` from `^19.0.0` to `^19.0.1` ([#10719](https://github.com/MetaMask/core/pull/10719)) - Bump `@metamask/transaction-controller` from `^72.1.0` to `^72.2.0` ([#10772](https://github.com/MetaMask/core/pull/10772)) diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index c73d13983fb..5833d14cd9b 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -93,9 +93,6 @@ export const getClientRequest = ( let options: Record | undefined; - // Stellar, Solana, and Bitcoin trades expect the swap/bridge asset IDs in the - // request options; Tron also adds its own fields in the - // branch below. if ( isStellarChainId(srcChainId) || isSolanaChainId(srcChainId) || From c9cc4be19b0935dd2fd70f552852a7b176ef42c1 Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Sat, 10 Oct 2026 17:14:12 +0200 Subject: [PATCH 6/7] test(bridge-status-controller): use a same-chain Solana asset id in the swap fixture The Solana swap fixture set destChainId to Solana but gave the destination asset an eip155 CAIP id. Now that the swap asset ids are forwarded to the snap, that mismatch would classify the same-chain swap as a bridgeSend, contradicting the fixture's own swap_type of single_chain. Use a Solana asset id for the destination asset and regenerate the affected snapshots. --- .../bridge-status-controller.test.ts.snap | 18 +++++++++--------- .../src/bridge-status-controller.test.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 522fbddedcd..8c45b838290 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -5378,7 +5378,7 @@ exports[`BridgeStatusController submitTx: Solana swap should handle snap control "source_hash_present": false, "stx_enabled": false, "swap_type": "single_chain", - "token_address_destination": "eip155:1399811149/slip44:501", + "token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "token_address_source": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", "token_security_type_destination": null, "token_symbol_destination": "USDC", @@ -5400,7 +5400,7 @@ exports[`BridgeStatusController submitTx: Solana swap should handle snap control "params": { "accountId": "solana-account-1", "options": { - "destAssetId": "eip155:1399811149/slip44:501", + "destAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "sourceAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", }, "scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", @@ -5435,7 +5435,7 @@ exports[`BridgeStatusController submitTx: Solana swap should handle snap control "source_hash_present": false, "stx_enabled": false, "swap_type": "single_chain", - "token_address_destination": "eip155:1399811149/slip44:501", + "token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "token_address_source": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", "token_security_type_destination": null, "token_symbol_destination": "USDC", @@ -5481,7 +5481,7 @@ exports[`BridgeStatusController submitTx: Solana swap should successfully submit "source_hash_present": false, "stx_enabled": false, "swap_type": "single_chain", - "token_address_destination": "eip155:1399811149/slip44:501", + "token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "token_address_source": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", "token_security_type_destination": null, "token_symbol_destination": "USDC", @@ -5503,7 +5503,7 @@ exports[`BridgeStatusController submitTx: Solana swap should successfully submit "params": { "accountId": "solana-account-1", "options": { - "destAssetId": "eip155:1399811149/slip44:501", + "destAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "sourceAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", }, "scope": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", @@ -5550,7 +5550,7 @@ exports[`BridgeStatusController submitTx: Solana swap should successfully submit "source_transaction": "COMPLETE", "stx_enabled": false, "swap_type": "single_chain", - "token_address_destination": "eip155:1399811149/slip44:501", + "token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "token_address_source": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", "token_security_type_destination": null, "token_symbol_destination": "USDC", @@ -5620,7 +5620,7 @@ exports[`BridgeStatusController submitTx: Solana swap should successfully submit "bridges": [], "destAsset": { "address": "0x...", - "assetId": "eip155:1399811149/slip44:501", + "assetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "chainId": 1151111081099710, "decimals": 18, "name": "USDC", @@ -5739,7 +5739,7 @@ exports[`BridgeStatusController submitTx: Solana swap should throw error when sn "source_hash_present": false, "stx_enabled": false, "swap_type": "single_chain", - "token_address_destination": "eip155:1399811149/slip44:501", + "token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "token_address_source": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", "token_security_type_destination": null, "token_symbol_destination": "USDC", @@ -5774,7 +5774,7 @@ exports[`BridgeStatusController submitTx: Solana swap should throw error when sn "source_hash_present": false, "stx_enabled": false, "swap_type": "single_chain", - "token_address_destination": "eip155:1399811149/slip44:501", + "token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "token_address_source": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501", "token_security_type_destination": null, "token_symbol_destination": "USDC", diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index 5b1c8460c1d..3a173ea877c 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -2632,7 +2632,7 @@ describe('BridgeStatusController', () => { symbol: 'USDC', name: 'USDC', decimals: 18, - assetId: 'eip155:1399811149/slip44:501', + assetId: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, }, bridgeId: 'test-bridge', bridges: [], From 131e5e6ee548f9d51e9da67d752a41e980207d0e Mon Sep 17 00:00:00 2001 From: Frederic HENG Date: Sat, 10 Oct 2026 17:20:17 +0200 Subject: [PATCH 7/7] fix(bridge-status-controller): omit swap asset ids from approval snap requests handleNonEvmTx always attached the quote's source and destination asset ids to the signAndSendTransaction options, including for the token approval that precedes a swap or bridge. The Tron snap gives those asset ids precedence over the contract type, so an approve was classified as a swap or bridgeSend instead of tokenApprove. Add an isApproval flag and only attach the asset ids to the main trade. The approval request now falls back to the contract-type classification. --- .../bridge-status-controller.test.ts.snap | 6 --- .../src/strategy/non-evm-strategy.ts | 1 + .../src/utils/snaps.test.ts | 37 +++++++++++++++++++ .../src/utils/snaps.ts | 10 ++++- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 8c45b838290..b24d37676c4 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -5842,8 +5842,6 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should handle "params": { "accountId": "tron-account-1", "options": { - "destAssetId": "tron:728126428/slip44:195", - "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, @@ -5947,8 +5945,6 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should success "params": { "accountId": "tron-account-1", "options": { - "destAssetId": "tron:728126428/slip44:195", - "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, @@ -6174,8 +6170,6 @@ exports[`BridgeStatusController submitTx: Tron swap with approval should success "params": { "accountId": "tron-account-1", "options": { - "destAssetId": "tron:728126428/slip44:195", - "sourceAssetId": "tron:728126428/slip44:195", "type": undefined, "visible": undefined, }, diff --git a/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts b/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts index b513bda50b2..0cef18d4b57 100644 --- a/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts +++ b/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts @@ -38,6 +38,7 @@ const handleTronApproval = async ( quoteResponse.approval, quoteResponse, args.selectedAccount, + true, ); return txMeta.id; } diff --git a/packages/bridge-status-controller/src/utils/snaps.test.ts b/packages/bridge-status-controller/src/utils/snaps.test.ts index 56a0a5d1a8a..05820b4ffed 100644 --- a/packages/bridge-status-controller/src/utils/snaps.test.ts +++ b/packages/bridge-status-controller/src/utils/snaps.test.ts @@ -229,6 +229,43 @@ describe('Snaps Utils', () => { } `); }); + + it('omits the swap asset ids for an approval transaction', async () => { + const snapId = 'test-snap-id'; + const transaction = 'base64-encoded-approval'; + const accountId = 'test-account-id'; + + const mockCall = jest.fn((...args: unknown[]) => { + const [action] = args; + if (action === 'SnapController:handleRequest') { + return Promise.resolve({ transactionId: 'approval-tx-id' }); + } + }); + const messenger = { + call: (...args: unknown[]) => mockCall(...args), + } as unknown as BridgeStatusControllerMessenger; + + await handleNonEvmTx( + messenger, + transaction, + { + quote: { + srcChainId: ChainId.SOLANA, + srcAsset: { symbol: 'SOL', assetId: 'solana:src/slip44:501' }, + destAsset: { symbol: 'USDC', assetId: 'solana:dest/token:usdc' }, + }, + sentAmount: { amount: '1000000000' }, + } as never, + { id: accountId, metadata: { snap: { id: snapId } } } as never, + true, + ); + + const [, request] = mockCall.mock.calls[0] as [ + string, + { request: { params: { options?: unknown } } }, + ]; + expect(request.request.params.options).toBeUndefined(); + }); }); describe('createClientTransactionRequest', () => { diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index 5833d14cd9b..fc583fe70ed 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -262,6 +262,7 @@ export const handleNonEvmTxResponse = ( * @param trade - The trade data (can be approval or main trade) * @param quoteResponse - The quote response containing metadata * @param selectedAccount - The account to submit the transaction for + * @param isApproval - Whether the trade is the approval transaction rather than the main trade * @returns The transaction meta */ export const handleNonEvmTx = async ( @@ -269,6 +270,7 @@ export const handleNonEvmTx = async ( trade: Trade, quoteResponse: QuoteResponseV1 & QuoteMetadata, selectedAccount: AccountsControllerState['internalAccounts']['accounts'][string], + isApproval = false, ): Promise => { if (!selectedAccount.metadata?.snap?.id) { throw new Error( @@ -276,13 +278,17 @@ export const handleNonEvmTx = async ( ); } + // The quote's asset ids describe the swap/bridge transaction, not the token + // approval that precedes it. Passing them for the approval would make the snap + // classify the approval as a swap or a bridge, so they are only attached to + // the main trade. const request = getClientRequest( trade, quoteResponse.quote.srcChainId, selectedAccount.id, selectedAccount.metadata?.snap?.id, - quoteResponse.quote.srcAsset.assetId, - quoteResponse.quote.destAsset.assetId, + isApproval ? undefined : quoteResponse.quote.srcAsset.assetId, + isApproval ? undefined : quoteResponse.quote.destAsset.assetId, ); const requestResponse = (await messenger.call( 'SnapController:handleRequest',