Skip to content

Commit d99af47

Browse files
authored
fix: perps activity reducer fires console.error on unhandled HL fill directions (Spot Dust Conversion) (#30174)
## **Description** The perps activity reducer emits `console.error` for HL fills with unrecognized `dir` values like "Spot Dust Conversion" (HL housekeeping — auto-conversion of spot dust to USDC). This pollutes Sentry/dev console and trips automated recipe gating. Fix adds explicit silent skip for "Spot Dust Conversion" and downgrades the default unknown-direction branch from `console.error` to `console.warn`. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [TAT-3090](https://consensyssoftware.atlassian.net/browse/TAT-3090) ## **Manual testing steps** ```gherkin Feature: Perps activity fill direction handling Scenario: Spot Dust Conversion fills are silently skipped Given a HL account with Spot Dust Conversion fill history When the perps activity screen loads Then no console.error is emitted for Spot Dust Conversion fills And the fill is silently dropped from the activity list Scenario: Unknown fill directions emit console.warn Given a HL fill with an unrecognized direction string When transformFillsToTransactions processes the fill Then console.warn is emitted (not console.error) And the fill is dropped from the result ``` ## **Screenshots/Recordings** No visual evidence selected for publication. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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. ## **Validation Recipe** <details> <summary>recipe.json</summary> ```json { "pr": "tat-3090", "title": "Verify Spot Dust Conversion fills are silently skipped and unknown directions emit warn not error", "jira": "TAT-3090", "acceptance_criteria": [ "AC1: Fills with direction 'Spot Dust Conversion' are silently skipped without console.error", "AC2: Fills with unknown direction emit console.warn instead of console.error", "AC3: All previously handled directions continue to work correctly" ], "validate": { "static": ["yarn lint:tsc"], "workflow": { "pre_conditions": ["wallet.unlocked"], "entry": "ac1-eval-dust-direction", "nodes": { "ac1-eval-dust-direction": { "action": "eval_sync", "expression": "(function(){ var d = 'Spot Dust Conversion'; var p1 = d.split(' ')[0]; var p2 = d.split(' ')[1]; var isOpen = p1 === 'Open'; var isClose = p1 === 'Close'; var isFlip = p2 === '>'; var isADL = d === 'Auto-Deleveraging'; var isBuy = d === 'Buy'; var isSell = d === 'Sell'; var isDust = d === 'Spot Dust Conversion'; var handled = isOpen || isClose || isFlip || isADL || isBuy || isSell || isDust; return JSON.stringify({direction: d, isDust: isDust, handled: handled, shouldSkipSilently: isDust}); })()", "assert": { "all": [ { "operator": "eq", "field": "isDust", "value": true }, { "operator": "eq", "field": "handled", "value": true }, { "operator": "eq", "field": "shouldSkipSilently", "value": true } ] }, "next": "ac2-eval-unknown-direction" }, "ac2-eval-unknown-direction": { "action": "eval_sync", "expression": "(function(){ var d = 'TBD-NEW-HL-DIRECTION'; var p1 = d.split(' ')[0]; var p2 = d.split(' ')[1]; var isOpen = p1 === 'Open'; var isClose = p1 === 'Close'; var isFlip = p2 === '>'; var isADL = d === 'Auto-Deleveraging'; var isBuy = d === 'Buy'; var isSell = d === 'Sell'; var isDust = d === 'Spot Dust Conversion'; var handled = isOpen || isClose || isFlip || isADL || isBuy || isSell || isDust; return JSON.stringify({direction: d, handled: handled, shouldWarnNotError: !handled && !!d}); })()", "assert": { "all": [ { "operator": "eq", "field": "handled", "value": false }, { "operator": "eq", "field": "shouldWarnNotError", "value": true } ] }, "next": "ac3-eval-known-directions" }, "ac3-eval-known-directions": { "action": "eval_sync", "expression": "(function(){ var dirs = ['Open Long', 'Close Long', 'Open Short', 'Close Short', 'Long > Short', 'Short > Long', 'Auto-Deleveraging', 'Buy', 'Sell']; var results = []; for (var i = 0; i < dirs.length; i++) { var d = dirs[i]; var p1 = d.split(' ')[0]; var p2 = d.split(' ')[1]; var isOpen = p1 === 'Open'; var isClose = p1 === 'Close'; var isFlip = p2 === '>'; var isADL = d === 'Auto-Deleveraging'; var isBuy = d === 'Buy'; var isSell = d === 'Sell'; var handled = isOpen || isClose || isFlip || isADL || isBuy || isSell; results.push({dir: d, handled: handled}); } var allHandled = results.every(function(r){ return r.handled; }); return JSON.stringify({allHandled: allHandled, count: results.length}); })()", "assert": { "all": [ { "operator": "eq", "field": "allHandled", "value": true }, { "operator": "eq", "field": "count", "value": 9 } ] }, "next": "teardown-done" }, "teardown-done": { "action": "end", "status": "pass" } } } } } ``` </details> ## **Recipe Workflow** <details> <summary>workflow.mmd</summary> ```mermaid graph TD ac1-eval-dust-direction["ac1-eval-dust-direction<br/>eval_sync: Spot Dust Conversion handled"] --> ac2-eval-unknown-direction ac2-eval-unknown-direction["ac2-eval-unknown-direction<br/>eval_sync: unknown direction warn not error"] --> ac3-eval-known-directions ac3-eval-known-directions["ac3-eval-known-directions<br/>eval_sync: all 9 known directions handled"] --> teardown-done teardown-done["teardown-done<br/>end: pass"] ``` </details> [TAT-3090]: https://consensyssoftware.atlassian.net/browse/TAT-3090?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: only adjusts fill-direction handling and logging, plus adds tests, without changing trade calculations for known directions. > > **Overview** > Prevents noisy error logging when transforming HL fills into perps activity transactions. > > `transformFillsToTransactions` now **silently skips** fills with direction `Spot Dust Conversion`, and changes unknown/missing direction handling from `console.error` to `console.warn` while continuing to drop those fills. > > Adds unit tests asserting the skip behavior and that unknown/empty directions warn (and never error). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 982577b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent b30bd2a commit d99af47

2 files changed

Lines changed: 68 additions & 2 deletions

File tree

app/components/UI/Perps/utils/transactionTransforms.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,69 @@ describe('transactionTransforms', () => {
768768
expect(result).toHaveLength(0);
769769
});
770770

771+
it('silently skips Spot Dust Conversion fills without console.error', () => {
772+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
773+
const errorSpy = jest.spyOn(console, 'error').mockImplementation();
774+
775+
const dustFill = {
776+
...mockFill,
777+
direction: 'Spot Dust Conversion',
778+
};
779+
780+
const result = transformFillsToTransactions([dustFill]);
781+
782+
expect(result).toHaveLength(0);
783+
expect(errorSpy).not.toHaveBeenCalled();
784+
expect(warnSpy).not.toHaveBeenCalled();
785+
786+
warnSpy.mockRestore();
787+
errorSpy.mockRestore();
788+
});
789+
790+
it('emits console.warn for unknown fill directions instead of console.error', () => {
791+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
792+
const errorSpy = jest.spyOn(console, 'error').mockImplementation();
793+
794+
const unknownDirFill = {
795+
...mockFill,
796+
direction: 'TBD-NEW-HL-DIRECTION',
797+
};
798+
799+
const result = transformFillsToTransactions([unknownDirFill]);
800+
801+
expect(result).toHaveLength(0);
802+
expect(warnSpy).toHaveBeenCalledWith(
803+
'Unhandled fill direction',
804+
'TBD-NEW-HL-DIRECTION',
805+
);
806+
expect(errorSpy).not.toHaveBeenCalled();
807+
808+
warnSpy.mockRestore();
809+
errorSpy.mockRestore();
810+
});
811+
812+
it('emits console.warn for empty direction instead of console.error', () => {
813+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
814+
const errorSpy = jest.spyOn(console, 'error').mockImplementation();
815+
816+
const noDirectionFill = {
817+
...mockFill,
818+
direction: '',
819+
};
820+
821+
const result = transformFillsToTransactions([noDirectionFill]);
822+
823+
expect(result).toHaveLength(0);
824+
expect(warnSpy).toHaveBeenCalledWith(
825+
'Unknown fill direction',
826+
expect.objectContaining({ direction: '' }),
827+
);
828+
expect(errorSpy).not.toHaveBeenCalled();
829+
830+
warnSpy.mockRestore();
831+
errorSpy.mockRestore();
832+
});
833+
771834
// Integration test for split stop loss bug fix
772835
it('aggregates split stop loss fills and shows combined PnL in transaction', () => {
773836
// Bug scenario: Stop loss split into two fills with different order IDs

app/components/UI/Perps/utils/transactionTransforms.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,13 @@ export function transformFillsToTransactions(
308308
action = 'Flipped';
309309
// Will be set based on calculation below
310310
} else if (!direction) {
311-
console.error('Unknown fill direction', fill);
311+
console.warn('Unknown fill direction', fill);
312+
return acc;
313+
} else if (direction === 'Spot Dust Conversion') {
314+
// HL housekeeping — auto-conversion of spot dust to USDC, not a perps trade
312315
return acc;
313316
} else {
314-
console.error('Unknown action', fill);
317+
console.warn('Unhandled fill direction', direction);
315318
return acc;
316319
}
317320

0 commit comments

Comments
 (0)