diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index efbd8787270..fb44e736339 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -2238,7 +2238,7 @@ "count": 2 }, "typescript/no-unsafe-assignment": { - "count": 5 + "count": 9 }, "typescript/no-unsafe-return": { "count": 2 diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index 9856208cb79..0da67d862fa 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, 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/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 2352bef56b2..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 @@ -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=", }, @@ -5370,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", @@ -5391,6 +5399,10 @@ exports[`BridgeStatusController submitTx: Solana swap should handle snap control "method": "signAndSendTransaction", "params": { "accountId": "solana-account-1", + "options": { + "destAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "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=", }, @@ -5423,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", @@ -5469,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", @@ -5490,6 +5502,10 @@ exports[`BridgeStatusController submitTx: Solana swap should successfully submit "method": "signAndSendTransaction", "params": { "accountId": "solana-account-1", + "options": { + "destAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "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=", }, @@ -5534,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", @@ -5604,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", @@ -5723,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", @@ -5758,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", @@ -5951,6 +5967,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 +6192,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/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: [], 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 1117a074cd5..fc583fe70ed 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -9,8 +9,10 @@ import { extractTradeData, formatChainIdToCaip, formatChainIdToHex, + isBitcoinChainId, isCrossChain, - isStellarTrade, + isSolanaChainId, + isStellarChainId, isTronTrade, } from '@metamask/bridge-controller'; import { SnapController } from '@metamask/snaps-controllers'; @@ -91,10 +93,11 @@ 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)) { + if ( + isStellarChainId(srcChainId) || + isSolanaChainId(srcChainId) || + isBitcoinChainId(srcChainId) + ) { if (sourceAssetId !== undefined || destAssetId !== undefined) { options = { ...(sourceAssetId !== undefined && { @@ -108,10 +111,15 @@ export const getClientRequest = ( } if (isTronTrade(trade)) { - // Tron trades need the visible flag and contract type to be included in the request options options = { visible: trade.visible, type: trade.raw_data?.contract?.[0]?.type, + ...(sourceAssetId !== undefined && { + sourceAssetId, + }), + ...(destAssetId !== undefined && { + destAssetId, + }), }; } @@ -254,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 ( @@ -261,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( @@ -268,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', diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 44d99b443d8..9536f4728ed 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", - ], - ] - `); + }, + }, + }); + }); - createClientRequestSpy.mockRestore(); + 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' }, + }, + }; + + 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', () => { @@ -1752,7 +1803,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,10 +1827,50 @@ 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'); }); - 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 +1892,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'); }); });