Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/core-monorepo",
"version": "1052.0.0",
"version": "1054.0.0",
"private": true,
"description": "Monorepo for packages shared between MetaMask clients",
"repository": {
Expand Down
5 changes: 4 additions & 1 deletion packages/perps-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [8.2.0]

### Added

- Add Perps Discovery analytics constants to `PERPS_EVENT_PROPERTY` and `PERPS_EVENT_VALUE` so mobile can import them from `@metamask/perps-controller` instead of maintaining a local mirror ([#9178](https://github.com/MetaMask/core/pull/9178))
Expand Down Expand Up @@ -389,7 +391,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Bump `@metamask/controller-utils` from `^11.18.0` to `^11.19.0` ([#7995](https://github.com/MetaMask/core/pull/7995))

[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/perps-controller@8.1.0...HEAD
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/perps-controller@8.2.0...HEAD
[8.2.0]: https://github.com/MetaMask/core/compare/@metamask/perps-controller@8.1.0...@metamask/perps-controller@8.2.0
[8.1.0]: https://github.com/MetaMask/core/compare/@metamask/perps-controller@8.0.0...@metamask/perps-controller@8.1.0
[8.0.0]: https://github.com/MetaMask/core/compare/@metamask/perps-controller@7.0.0...@metamask/perps-controller@8.0.0
[7.0.0]: https://github.com/MetaMask/core/compare/@metamask/perps-controller@6.3.0...@metamask/perps-controller@7.0.0
Expand Down
2 changes: 1 addition & 1 deletion packages/perps-controller/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/perps-controller",
"version": "8.1.0",
"version": "8.2.0",
"description": "Controller for perpetual trading functionality in MetaMask",
"keywords": [
"Ethereum",
Expand Down
9 changes: 8 additions & 1 deletion packages/transaction-pay-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [23.12.0]

### Changed

- Post-quote source amount filtering no longer bypasses same-token filtering when `isQuoteRequired` is set ([#9194](https://github.com/MetaMask/core/pull/9194))

## [23.11.0]

### Fixed
Expand Down Expand Up @@ -1111,7 +1117,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Initial release ([#6820](https://github.com/MetaMask/core/pull/6820))

[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@23.11.0...HEAD
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@23.12.0...HEAD
[23.12.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@23.11.0...@metamask/transaction-pay-controller@23.12.0
[23.11.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@23.10.0...@metamask/transaction-pay-controller@23.11.0
[23.10.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@23.9.0...@metamask/transaction-pay-controller@23.10.0
[23.9.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@23.8.0...@metamask/transaction-pay-controller@23.9.0
Expand Down
2 changes: 1 addition & 1 deletion packages/transaction-pay-controller/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/transaction-pay-controller",
"version": "23.11.0",
"version": "23.12.0",
"description": "Manages alternate payment strategies to provide required funds for transactions in MetaMask",
"keywords": [
"Ethereum",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1373,190 +1373,6 @@ describe('Relay Quotes Utils', () => {
expect(estimateGasBatchMock).not.toHaveBeenCalled();
});

it('sets user to txParams.from when same token and chain with accountOverride', async () => {
const txParamsFrom = '0xOriginalSender000000000000000000000000' as Hex;
const accountOverride =
'0xOverrideAccount0000000000000000000000000' as Hex;
const tokenAddress = '0xTokenAddress00000000000000000000000000' as Hex;
const chainId = '0x89' as Hex;

successfulFetchMock.mockResolvedValue({
ok: true,
json: async () => QUOTE_MOCK,
} as never);

await getRelayQuotes({
accountSupports7702: true,
messenger,
requests: [
{
...QUOTE_REQUEST_MOCK,
from: accountOverride,
sourceChainId: chainId,
sourceTokenAddress: tokenAddress,
targetChainId: chainId,
targetTokenAddress: tokenAddress,
},
],
transaction: {
...TRANSACTION_META_MOCK,
txParams: { from: txParamsFrom },
} as TransactionMeta,
});

const body = JSON.parse(
successfulFetchMock.mock.calls[0][1]?.body as string,
);

expect(body.user).toBe(txParamsFrom);
});

it('keeps user as from when same token and chain with accountOverride and isPostQuote', async () => {
const txParamsFrom = '0xOriginalSender000000000000000000000000' as Hex;
const accountOverride =
'0xOverrideAccount0000000000000000000000000' as Hex;
const tokenAddress = '0xTokenAddress00000000000000000000000000' as Hex;
const chainId = '0x89' as Hex;

successfulFetchMock.mockResolvedValue({
ok: true,
json: async () => QUOTE_MOCK,
} as never);

await getRelayQuotes({
accountSupports7702: true,
messenger,
requests: [
{
...QUOTE_REQUEST_MOCK,
from: accountOverride,
isPostQuote: true,
sourceChainId: chainId,
sourceTokenAddress: tokenAddress,
targetChainId: chainId,
targetTokenAddress: tokenAddress,
},
],
transaction: {
...TRANSACTION_META_MOCK,
txParams: { from: txParamsFrom },
} as TransactionMeta,
});

const body = JSON.parse(
successfulFetchMock.mock.calls[0][1]?.body as string,
);

expect(body.user).toBe(accountOverride);
});

it('keeps user as from when same token and chain with accountOverride but recipient differs', async () => {
const txParamsFrom = '0xOriginalSender000000000000000000000000' as Hex;
const accountOverride =
'0xOverrideAccount0000000000000000000000000' as Hex;
const externalRecipient =
'0xExternalRecipient000000000000000000000000' as Hex;
const tokenAddress = '0xTokenAddress00000000000000000000000000' as Hex;
const chainId = '0x89' as Hex;

successfulFetchMock.mockResolvedValue({
ok: true,
json: async () => QUOTE_MOCK,
} as never);

await getRelayQuotes({
accountSupports7702: true,
messenger,
requests: [
{
...QUOTE_REQUEST_MOCK,
from: accountOverride,
recipient: externalRecipient,
sourceChainId: chainId,
sourceTokenAddress: tokenAddress,
targetChainId: chainId,
targetTokenAddress: tokenAddress,
},
],
transaction: {
...TRANSACTION_META_MOCK,
txParams: { from: txParamsFrom },
} as TransactionMeta,
});

const body = JSON.parse(
successfulFetchMock.mock.calls[0][1]?.body as string,
);

expect(body.user).toBe(accountOverride);
});

it('keeps user as from when source and destination differ even with accountOverride', async () => {
const txParamsFrom = '0xOriginalSender000000000000000000000000' as Hex;
const accountOverride =
'0xOverrideAccount0000000000000000000000000' as Hex;

successfulFetchMock.mockResolvedValue({
ok: true,
json: async () => QUOTE_MOCK,
} as never);

await getRelayQuotes({
accountSupports7702: true,
messenger,
requests: [
{
...QUOTE_REQUEST_MOCK,
from: accountOverride,
},
],
transaction: {
...TRANSACTION_META_MOCK,
txParams: { from: txParamsFrom },
} as TransactionMeta,
});

const body = JSON.parse(
successfulFetchMock.mock.calls[0][1]?.body as string,
);

expect(body.user).toBe(accountOverride);
});

it('keeps user as from when same token and chain without accountOverride', async () => {
const tokenAddress = '0xTokenAddress00000000000000000000000000' as Hex;
const chainId = '0x89' as Hex;

successfulFetchMock.mockResolvedValue({
ok: true,
json: async () => QUOTE_MOCK,
} as never);

await getRelayQuotes({
accountSupports7702: true,
messenger,
requests: [
{
...QUOTE_REQUEST_MOCK,
sourceChainId: chainId,
sourceTokenAddress: tokenAddress,
targetChainId: chainId,
targetTokenAddress: tokenAddress,
},
],
transaction: {
...TRANSACTION_META_MOCK,
txParams: { from: FROM_MOCK },
} as TransactionMeta,
});

const body = JSON.parse(
successfulFetchMock.mock.calls[0][1]?.body as string,
);

expect(body.user).toBe(FROM_MOCK);
});

it('does not prepend original transaction for post-quote when txParams.to is missing', async () => {
successfulFetchMock.mockResolvedValue({
ok: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,6 @@ async function getSingleQuote(
isRelayExecuteEnabled(messenger) &&
isEIP7702Chain(messenger, sourceChainId);

const quoteUser = getQuoteUser(request, transaction, from);

const body: RelayQuoteRequest = {
amount: useExactInput ? sourceTokenAmount : targetAmountMinimum,
destinationChainId: Number(targetChainId),
Expand All @@ -294,7 +292,7 @@ async function getSingleQuote(
recipient: request.recipient ?? from,
slippageTolerance,
tradeType: useExactInput ? 'EXACT_INPUT' : 'EXPECTED_OUTPUT',
user: quoteUser,
user: from,
};

if (request.isPolymarketDepositWallet) {
Expand Down Expand Up @@ -1183,50 +1181,6 @@ function getTransferRecipient(data: Hex): Hex {
.decodeFunctionData('transfer', data)
.to.toLowerCase();
}
/**
* Determine the `user` address for a Relay quote request.
*
* When source and destination are the same token on the same chain and an
* accountOverride is active, use the original `txParams.from` so that Relay
* sees the transaction sender rather than the override address.
*
* @param request - Quote request.
* @param transaction - Parent transaction metadata.
* @param from - Resolved wallet address (`accountOverride ?? txParams.from`).
* @returns The address to set as `user` on the quote body.
*/
function getQuoteUser(
request: QuoteRequest,
transaction: TransactionMeta,
from: Hex,
): Hex {
const {
sourceChainId,
sourceTokenAddress,
targetChainId,
targetTokenAddress,
} = request;

const isSameSourceAndTarget =
sourceChainId === targetChainId &&
sourceTokenAddress.toLowerCase() === targetTokenAddress.toLowerCase();

const txParamsFrom = transaction.txParams?.from as Hex | undefined;
const hasAccountOverride =
txParamsFrom && from.toLowerCase() !== txParamsFrom.toLowerCase();

const recipient = request.recipient ?? from;
const isRecipientAccountOverride =
recipient.toLowerCase() === from.toLowerCase();

return isSameSourceAndTarget &&
hasAccountOverride &&
isRecipientAccountOverride &&
!request.isPostQuote
? txParamsFrom
: from;
}

function getSubsidizedFeeAmountUsd(quote: RelayQuote): BigNumber {
const subsidizedFee = quote.fees?.subsidized;
const amountUsd = new BigNumber(subsidizedFee?.amountUsd ?? '0');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,57 +436,6 @@ describe('Source Amounts Utils', () => {
]);
});

it('does not filter out same token when isQuoteRequired is true in post-quote flow', () => {
const transactionData: TransactionData = {
isLoading: false,
isPostQuote: true,
isQuoteRequired: true,
paymentToken: DESTINATION_TOKEN_MOCK,
tokens: [
{
...TRANSACTION_TOKEN_MOCK,
address: DESTINATION_TOKEN_MOCK.address,
chainId: DESTINATION_TOKEN_MOCK.chainId,
skipIfBalance: false,
},
],
};

updateSourceAmounts(TRANSACTION_ID_MOCK, transactionData, messenger);

expect(transactionData.sourceAmounts).toStrictEqual([
{
sourceAmountHuman: TRANSACTION_TOKEN_MOCK.amountHuman,
sourceAmountRaw: TRANSACTION_TOKEN_MOCK.amountRaw,
sourceBalanceRaw: TRANSACTION_TOKEN_MOCK.balanceRaw,
sourceChainId: DESTINATION_TOKEN_MOCK.chainId,
sourceTokenAddress: DESTINATION_TOKEN_MOCK.address,
targetTokenAddress: DESTINATION_TOKEN_MOCK.address,
},
]);
});

it('still filters out same token when isQuoteRequired is false in post-quote flow', () => {
const transactionData: TransactionData = {
isLoading: false,
isPostQuote: true,
isQuoteRequired: false,
paymentToken: DESTINATION_TOKEN_MOCK,
tokens: [
{
...TRANSACTION_TOKEN_MOCK,
address: DESTINATION_TOKEN_MOCK.address,
chainId: DESTINATION_TOKEN_MOCK.chainId,
skipIfBalance: false,
},
],
};

updateSourceAmounts(TRANSACTION_ID_MOCK, transactionData, messenger);

expect(transactionData.sourceAmounts).toStrictEqual([]);
});

it('still filters out same token when isHyperliquidSource is false in post-quote flow', () => {
const transactionData: TransactionData = {
isLoading: false,
Expand Down
Loading
Loading