Skip to content

Commit 59389a0

Browse files
authored
feat: increasing command support for dapp swap comparison (#38546)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Increasing command support for dapp swap comparison ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: MetaMask/MetaMask-planning#6386 ## **Manual testing steps** 1. Submit swap command with required version 2. Ensure that dapp swap comparison works as expected ## **Screenshots/Recordings** TODO ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds parsing/handling for V2 exact-in swaps (including batched/native paths) and expands tests; exact-out still rejected. > > - **Core**: > - Add V2 exact-in ABI to `BASE_COMMANDS_ABI_DEFINITION` (`V2_SWAP_EXACT_IN`). > - Register `handleV2CommandSwapExactIn` in parser (`DAPP_SWAP_COMMANDS_PARSER` maps `08`). > - Implement `handleV2CommandSwapExactIn` to decode `recipient`, `amountIn`, `amountOutMin`, and resolve `srcTokenAddress`/`destTokenAddress` from `path`. > - **Tests** (`shared/modules/dapp-swap-comparison/dapp-swap-command-utils.test.ts`): > - Add V2 exact-in cases: ERC20→ERC20, ERC20→native, native→ERC20, and batched variants. > - Expand exact-out suite label to "V4/V3/v2 swaps" and add V2 exact-out scenarios expecting "Exact-out commands are not supported yet". > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 810c440. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent cbbe8a1 commit 59389a0

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

shared/modules/dapp-swap-comparison/dapp-swap-command-utils.test.ts

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,130 @@ describe('dapp-swap command utils', () => {
246246
});
247247
});
248248
});
249+
250+
describe('V2 swaps', () => {
251+
it('returns the correct result for v2 erc20 -> erc20 swap', () => {
252+
const result = getCommandValues(
253+
['0a', '08'],
254+
[
255+
'0x000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb2000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006958fda900000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ff5693b99212da76ad316178a184ab56d299b4300000000000000000000000000000000000000000000000000000000693177b100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000414972f334a70c894af24a4d337ee6f834c5832550c968fbd48c5cac9664925393659ae3739dded1a3856548e4dfb3f4fd3d1dc73dadfd53e3b3092e89b8a07d221b00000000000000000000000000000000000000000000000000000000000000',
256+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe00000000000000000000000000000000000000000000000000000000000007d0000000000000000000000000000000000000000000000000000000000000079b00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb2000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913',
257+
],
258+
'0x2105',
259+
);
260+
expect(result).toStrictEqual({
261+
amountMin: '0x079b',
262+
quotesInput: {
263+
destChainId: '0x2105',
264+
destTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
265+
gasIncluded: false,
266+
gasIncluded7702: false,
267+
srcChainId: '0x2105',
268+
srcTokenAddress: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
269+
srcTokenAmount: '0x07d0',
270+
},
271+
});
272+
});
273+
274+
it('returns the correct result for v2 erc20 -> native swap', () => {
275+
const result = getCommandValues(
276+
['08', '06', '0c'],
277+
[
278+
'0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000007d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000004200000000000000000000000000000000000006',
279+
'0x00000000000000000000000042000000000000000000000000000000000000060000000000000000000000005d64d14d2cf4fe5fe4e65b1c7e3d11e18d4930910000000000000000000000000000000000000000000000000000000000000019',
280+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000008d78e74467',
281+
],
282+
'0x2105',
283+
);
284+
expect(result).toStrictEqual({
285+
amountMin: '0x8d78e74467',
286+
quotesInput: {
287+
destChainId: '0x2105',
288+
destTokenAddress: '0x0000000000000000000000000000000000000000',
289+
gasIncluded: false,
290+
gasIncluded7702: false,
291+
srcChainId: '0x2105',
292+
srcTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
293+
srcTokenAmount: '0x07d0',
294+
},
295+
});
296+
});
297+
298+
it('returns the correct result for v2 native -> erc20 swap', () => {
299+
const result = getCommandValues(
300+
['0b', '08', '06', '04'],
301+
[
302+
'0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000008bb2c97000',
303+
'0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000008bb2c97000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913',
304+
'0x000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000007ffc3dbf3b2b50ff3a1d5523bc24bb5043837b140000000000000000000000000000000000000000000000000000000000000019',
305+
'0x000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000000742',
306+
],
307+
'0x2105',
308+
);
309+
expect(result).toStrictEqual({
310+
amountMin: '0x0742',
311+
quotesInput: {
312+
destChainId: '0x2105',
313+
destTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
314+
gasIncluded: false,
315+
gasIncluded7702: false,
316+
srcChainId: '0x2105',
317+
srcTokenAddress: '0x0000000000000000000000000000000000000000',
318+
srcTokenAmount: '0x8bb2c97000',
319+
},
320+
});
321+
});
322+
323+
it('returns the correct result for batched v2 erc20 -> erc20 swap', () => {
324+
const result = getCommandValues(
325+
['08'],
326+
[
327+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe00000000000000000000000000000000000000000000000000000000000007d0000000000000000000000000000000000000000000000000000000000000079b00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb2000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913',
328+
],
329+
'0x2105',
330+
);
331+
expect(result).toStrictEqual({
332+
amountMin: '0x079b',
333+
quotesInput: {
334+
destChainId: '0x2105',
335+
destTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
336+
gasIncluded: false,
337+
gasIncluded7702: false,
338+
srcChainId: '0x2105',
339+
srcTokenAddress: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
340+
srcTokenAmount: '0x07d0',
341+
},
342+
});
343+
});
344+
345+
it('returns the correct result for batched v2 erc20 -> native swap', () => {
346+
const result = getCommandValues(
347+
['08', '06', '0c'],
348+
[
349+
'0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000007d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb20000000000000000000000004200000000000000000000000000000000000006',
350+
'0x00000000000000000000000042000000000000000000000000000000000000060000000000000000000000005d64d14d2cf4fe5fe4e65b1c7e3d11e18d4930910000000000000000000000000000000000000000000000000000000000000019',
351+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000008d56fe3e04',
352+
],
353+
'0x2105',
354+
);
355+
expect(result).toStrictEqual({
356+
amountMin: '0x8d56fe3e04',
357+
quotesInput: {
358+
destChainId: '0x2105',
359+
destTokenAddress: '0x0000000000000000000000000000000000000000',
360+
gasIncluded: false,
361+
gasIncluded7702: false,
362+
srcChainId: '0x2105',
363+
srcTokenAddress: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
364+
srcTokenAmount: '0x07d0',
365+
},
366+
});
367+
});
368+
});
249369
});
250370

251371
describe('getCommandValues - exactout', () => {
252-
describe('V4 swaps', () => {
372+
describe('V4/V3/v2 swaps', () => {
253373
const exactOutCommands = [
254374
[
255375
'V4 ERC20 to ERC20 Swap',
@@ -325,6 +445,35 @@ describe('dapp-swap command utils', () => {
325445
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000000000',
326446
],
327447
],
448+
[
449+
'V2_erc20_erc20',
450+
['09'],
451+
[
452+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe00000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000040600000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb2',
453+
],
454+
],
455+
[
456+
'V2_erc20_native',
457+
['09', '05', '0c'],
458+
[
459+
[
460+
'0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000008c0c319f0000000000000000000000000000000000000000000000000000000000000007b900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb20000000000000000000000004200000000000000000000000000000000000006',
461+
'0x00000000000000000000000042000000000000000000000000000000000000060000000000000000000000005d64d14d2cf4fe5fe4e65b1c7e3d11e18d4930910000000000000000000000000000000000000000000000000000000059682f00',
462+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000008bb2c97000',
463+
],
464+
],
465+
],
466+
[
467+
'V2_native_erc20',
468+
['0b', '09', '05', '04', '0c'],
469+
[
470+
'0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000004b0b37ec93',
471+
'0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000003ea0000000000000000000000000000000000000000000000000000004b0b37ec9300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb2',
472+
'0x000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb20000000000000000000000005d64d14d2cf4fe5fe4e65b1c7e3d11e18d4930910000000000000000000000000000000000000000000000000000000000000002',
473+
'0x000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb200000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe00000000000000000000000000000000000000000000000000000000000003e8',
474+
'0x00000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000000000',
475+
],
476+
],
328477
];
329478
exactOutCommands.forEach((input) => {
330479
it(`returns no result for the swap ${input[0] as string}`, () => {

shared/modules/dapp-swap-comparison/dapp-swap-command-utils.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ const BASE_COMMANDS_ABI_DEFINITION: Partial<
5454
name: 'payerIsUser',
5555
},
5656
],
57+
[SwapCommands.V2_SWAP_EXACT_IN]: [
58+
{
59+
type: 'address',
60+
name: 'recipient',
61+
},
62+
{
63+
type: 'uint256',
64+
name: 'amountIn',
65+
},
66+
{
67+
type: 'uint256',
68+
name: 'amountOutMin',
69+
},
70+
{
71+
type: 'address[]',
72+
name: 'path',
73+
},
74+
{
75+
type: 'bool',
76+
name: 'payerIsUser',
77+
},
78+
],
5779
[NonSwapCommands.SWEEP]: [
5880
{
5981
type: 'address',
@@ -150,6 +172,7 @@ type DAPP_SWAP_COMMANDS_PARSER_TYPE = {
150172

151173
const DAPP_SWAP_COMMANDS_PARSER: DAPP_SWAP_COMMANDS_PARSER_TYPE[] = [
152174
{ value: '00', handler: handleV3CommandSwapExactIn },
175+
{ value: '08', handler: handleV2CommandSwapExactIn },
153176
{
154177
value: '01',
155178
handler: handleCommandExactOut as DAPP_SWAP_COMMANDS_PARSER_TYPE['handler'],
@@ -371,6 +394,32 @@ function handleV3CommandSwapExactIn(
371394
};
372395
}
373396

397+
function handleV2CommandSwapExactIn(
398+
data: string,
399+
decodedResult: COMMAND_VALUES_RESULT,
400+
_chainId: Hex,
401+
) {
402+
const { amountMin, quotesInput } = decodedResult;
403+
const result = decodeCommandData(
404+
SwapCommands.V2_SWAP_EXACT_IN,
405+
data,
406+
BASE_COMMANDS_ABI_DEFINITION,
407+
);
408+
409+
return {
410+
amountMin: amountMin || result[2].toHexString(),
411+
quotesInput: {
412+
...(quotesInput ?? {}),
413+
srcTokenAmount: result[1].toHexString(),
414+
srcTokenAddress:
415+
quotesInput?.srcTokenAddress ?? result[3]?.[0]?.toLowerCase(),
416+
destTokenAddress:
417+
quotesInput?.destTokenAddress ??
418+
result[3]?.[result[3]?.length - 1]?.toLowerCase(),
419+
} as GenericQuoteRequest,
420+
};
421+
}
422+
374423
function handleCommandSweep(
375424
data: string,
376425
decodedResult: COMMAND_VALUES_RESULT,

0 commit comments

Comments
 (0)