Skip to content
Open
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
4 changes: 4 additions & 0 deletions packages/transaction-pay-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add Across quote support for post-quote Predict withdraw flows ([#8760](https://github.com/MetaMask/core/pull/8760))

### Changed

- Derive fiat order source amount from on-chain transaction data (`order.txHash`) with fallback to `order.cryptoAmount` ([#8694](https://github.com/MetaMask/core/pull/8694))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Hex } from '@metamask/utils';

import { ARBITRUM_USDC_ADDRESS, CHAIN_ID_ARBITRUM } from '../../constants';
import type {
PayStrategyCheckQuoteSupportRequest,
PayStrategyExecuteRequest,
PayStrategyGetQuotesRequest,
TransactionPayQuote,
Expand Down Expand Up @@ -56,6 +57,25 @@ describe('AcrossStrategy', () => {
],
} as PayStrategyGetQuotesRequest;

const quoteWithAuthorizationList = {
request: {
...baseRequest.requests[0],
},
original: {
metamask: {
gasLimits: [],
is7702: true,
requiresAuthorizationList: true,
},
quote: {},
request: {
actions: [],
amount: '100',
tradeType: 'exactInput',
},
},
} as TransactionPayQuote<AcrossQuote>;

beforeEach(() => {
jest.resetAllMocks();
getPayStrategiesConfigMock.mockReturnValue({
Expand Down Expand Up @@ -351,6 +371,79 @@ describe('AcrossStrategy', () => {
expect(result).toBe(false);
});

it('supports source 7702 authorization lists for Predict withdraw post-quote quotes without Across actions', () => {
const strategy = new AcrossStrategy();
const request = {
messenger,
quotes: [
{
...quoteWithAuthorizationList,
request: {
...quoteWithAuthorizationList.request,
isPostQuote: true,
},
},
],
transaction: {
...TRANSACTION_META_MOCK,
type: TransactionType.predictWithdraw,
} as TransactionMeta,
} as PayStrategyCheckQuoteSupportRequest<AcrossQuote>;

expect(strategy.checkQuoteSupport(request)).toBe(true);
});

it('does not support first-time 7702 authorization lists for non-post-quote Across quotes', () => {
const strategy = new AcrossStrategy();
const request = {
messenger,
quotes: [quoteWithAuthorizationList],
transaction: {
...TRANSACTION_META_MOCK,
type: TransactionType.predictWithdraw,
} as TransactionMeta,
} as PayStrategyCheckQuoteSupportRequest<AcrossQuote>;

expect(strategy.checkQuoteSupport(request)).toBe(false);
});

it('does not support first-time 7702 authorization lists when the Across quote has embedded destination actions', () => {
const strategy = new AcrossStrategy();
const request = {
messenger,
quotes: [
{
...quoteWithAuthorizationList,
request: {
...quoteWithAuthorizationList.request,
isPostQuote: true,
},
original: {
...quoteWithAuthorizationList.original,
request: {
...quoteWithAuthorizationList.original.request,
actions: [
{
args: [],
functionSignature: 'function transfer(address,uint256)',
isNativeTransfer: false,
target: '0xdef' as Hex,
value: '0',
},
],
},
},
},
],
transaction: {
...TRANSACTION_META_MOCK,
type: TransactionType.predictWithdraw,
} as TransactionMeta,
} as PayStrategyCheckQuoteSupportRequest<AcrossQuote>;

expect(strategy.checkQuoteSupport(request)).toBe(false);
});

it('supports 7702 quotes that do not require an authorization list', () => {
const strategy = new AcrossStrategy();
const quote = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,26 @@ export class AcrossStrategy implements PayStrategy<AcrossQuote> {
// Gas planning can discover that TransactionController would add an
// authorization list for a first-time 7702 upgrade. `is7702` alone is not a
// blocker because it also covers already-upgraded accounts.
return !request.quotes.some(
const requiresAuthorizationList = request.quotes.some(
(quote) => quote.original.metamask.requiresAuthorizationList,
);

if (!requiresAuthorizationList) {
return true;
}

if (!isPredictWithdrawTransaction(request.transaction)) {
return false;
}

// A first-time 7702 authorization list is acceptable here only because it is
// attached to MetaMask's source-chain batch transaction. It must not be
// smuggled into Across destination post-swap actions.
return request.quotes.every(
(quote) =>
quote.request.isPostQuote === true &&
quote.original.request.actions.length === 0,
);
}

async getQuotes(
Expand Down
Loading
Loading