diff --git a/packages/client-utils/CHANGELOG.md b/packages/client-utils/CHANGELOG.md index 500ea36ffa3..182d74bb31c 100644 --- a/packages/client-utils/CHANGELOG.md +++ b/packages/client-utils/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/core-backend` from `^8.0.0` to `^8.1.0` ([#9735](https://github.com/MetaMask/core/pull/9735)) - Bump `@metamask/transaction-controller` from `^69.3.0` to `^69.4.0` ([#9735](https://github.com/MetaMask/core/pull/9735)) +### Fixed + +- Prefer the subject's fungible `from` movement when mapping keyring send activity, so multi-party Solana txs (e.g. bridge source legs) no longer surface another address's token as the sent asset ([#9749](https://github.com/MetaMask/core/pull/9749)) + ## [1.5.0] ### Added diff --git a/packages/client-utils/src/mappers/keyring-transaction-mapper.test.ts b/packages/client-utils/src/mappers/keyring-transaction-mapper.test.ts index d6624ffef73..89f8de81d1f 100644 --- a/packages/client-utils/src/mappers/keyring-transaction-mapper.test.ts +++ b/packages/client-utils/src/mappers/keyring-transaction-mapper.test.ts @@ -215,6 +215,29 @@ describe('mapKeyringTransaction', () => { }); }); + it('uses the subject outflow for a Solana bridge send', () => { + const item = mapKeyringTransaction( + keyringTransactionFixtures.mapArgs.solanaBridgeSendWithForeignUsdc, + ); + + expect(item).toMatchObject({ + type: 'send', + chainId: SolScope.Mainnet, + status: 'success', + hash: '3Tph6Faw2YMshJt7pkCaCbTHTX4mYJLE6h72DR6Q4uDta9HmrNCfReXuDDPKUCbCxn7NUNALvgNjii19fKdgWBfA', + data: { + from: keyringTransactionFixtures.addresses.solanaSubject, + to: keyringTransactionFixtures.addresses.solanaCounterparty, + token: { + amount: '0.00531264', + assetId: `${SolScope.Mainnet}/slip44:501`, + direction: 'out', + symbol: 'SOL', + }, + }, + }); + }); + it('maps trustline approve TokenApprove to assetActivation', () => { const item = mapKeyringTransaction( keyringTransactionFixtures.mapArgs.trustlineApprove, diff --git a/packages/client-utils/src/mappers/keyring-transaction-mapper.ts b/packages/client-utils/src/mappers/keyring-transaction-mapper.ts index 84edd2da9e4..35c6e62025f 100644 --- a/packages/client-utils/src/mappers/keyring-transaction-mapper.ts +++ b/packages/client-utils/src/mappers/keyring-transaction-mapper.ts @@ -138,7 +138,10 @@ export function mapKeyringTransaction({ switch (type) { case KeyringTransactionType.Send: { - const fromToken = getToken(transaction.from, 'out'); + const fromSubject = subjectAddress + ? transaction.from.filter(({ address }) => address === subjectAddress) + : transaction.from; + const fromToken = getToken(fromSubject, 'out'); const token = !fromToken && chainId.startsWith('bip122:') ? getToken(transaction.to, 'out') diff --git a/packages/client-utils/test/fixtures/keyring-transactions.ts b/packages/client-utils/test/fixtures/keyring-transactions.ts index d79a1ac9a39..a9b6db56ffc 100644 --- a/packages/client-utils/test/fixtures/keyring-transactions.ts +++ b/packages/client-utils/test/fixtures/keyring-transactions.ts @@ -20,6 +20,8 @@ export const keyringTransactionFixtures = { senderAddress: 'sender-address', bitcoinSubject: 'bc1qcj8v4ft5uvt59jjrxd856a48xegclwne78h0ye', bitcoinOutput: 'bc1qc5tzsfpd3zjecma6529kanjtug69rf58mtfxmu', + solanaSubject: 'EsEduLCwNdAbJZ2oTr1wB1ymQw76NuwswWwG6imzQN7H', + solanaCounterparty: '8ekCy2jHHUbW2yeNGFWYJT9Hm9FW7SvZcZK66dSZCDiF', }, constants: { uint256Max: @@ -394,5 +396,121 @@ export const keyringTransactionFixtures = { events: [], } as never, }, + // MultichainTransactionsController payload for a Solana→Base bridge source + // leg. First fungible in `from` is another party's USDC; the subject's + // outflow is SOL. + solanaBridgeSendWithForeignUsdc: { + subjectAddress: 'EsEduLCwNdAbJZ2oTr1wB1ymQw76NuwswWwG6imzQN7H', + transaction: { + account: '625152a4-4667-4328-99bd-b38db43658f5', + chain: SolScope.Mainnet, + events: [ + { + status: TransactionStatus.Confirmed, + timestamp: 1784776645, + }, + ], + fees: [ + { + asset: { + amount: '0.000005', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + type: 'base', + }, + { + asset: { + amount: '0.000050564', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + type: 'priority', + }, + ], + from: [ + { + address: '8ekCy2jHHUbW2yeNGFWYJT9Hm9FW7SvZcZK66dSZCDiF', + asset: { + amount: '0.069181', + fungible: true, + type: `${SolScope.Mainnet}/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, + unit: 'USDC', + }, + }, + { + address: 'EsEduLCwNdAbJZ2oTr1wB1ymQw76NuwswWwG6imzQN7H', + asset: { + amount: '0.00531264', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + }, + ], + id: '3Tph6Faw2YMshJt7pkCaCbTHTX4mYJLE6h72DR6Q4uDta9HmrNCfReXuDDPKUCbCxn7NUNALvgNjii19fKdgWBfA', + status: TransactionStatus.Confirmed, + timestamp: 1784776645, + to: [ + { + address: '8ekCy2jHHUbW2yeNGFWYJT9Hm9FW7SvZcZK66dSZCDiF', + asset: { + amount: '0.000892125', + fungible: true, + type: `${SolScope.Mainnet}/token:So11111111111111111111111111111111111111112`, + unit: '', + }, + }, + { + address: '2Gr2S7Nk7nbXzWkzGD1FSqvgHqdPnyN2rLJWsQnenP3w', + asset: { + amount: '0.069181', + fungible: true, + type: `${SolScope.Mainnet}/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, + unit: 'USDC', + }, + }, + { + address: '2Gr2S7Nk7nbXzWkzGD1FSqvgHqdPnyN2rLJWsQnenP3w', + asset: { + amount: '0.00237336', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + }, + { + address: '47YRE7eLAdYzvGqSH1XLg2o8xUtywk7sS5BKv1oR4Y7i', + asset: { + amount: '0.000007875', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + }, + { + address: 'AyJKchXeuZgW8ZQ2uC2EvgPJXE9bKJRYmWs9gt3Ug8JP', + asset: { + amount: '0.00203928', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + }, + { + address: '5pVN5XZB8cYBjNLFrsBCPWkCQBan5K5Mq2dWGzwPgGJV', + asset: { + amount: '0.000892125', + fungible: true, + type: `${SolScope.Mainnet}/slip44:501`, + unit: 'SOL', + }, + }, + ], + type: TransactionType.Send, + } as never, + }, }, };