From 0954f71c168a83a843d4a673dda8fed84f8713a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Tani=C3=A7a?= Date: Fri, 7 Nov 2025 18:23:41 -0700 Subject: [PATCH] fix(predict): cap slippage to avoid sharePrice > 1 (#22354) ## **Description** This PR fixes a bug in the Polymarket prediction provider where orders could fail due to an invalid share price calculation. The issue occurred when slippage was applied to `minAmountReceived`, causing the calculated share price to exceed 1 (which is mathematically impossible for prediction market shares). The fix introduces a cap on `minAmountWithSlippage` to ensure it never falls below `maxAmountSpent + tickSize`, preventing the share price from exceeding 1 while still accounting for slippage to reduce order failure rates. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: Polymarket prediction order execution Scenario: user creates a prediction market order with slippage Given the user is on the Predict feature And a Polymarket prediction market is available When user creates an order with slippage applied Then the order should execute successfully without sharePrice > 1 errors And the minAmountReceived should be capped appropriately ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **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. --- > [!NOTE] > Adjusts slippage handling in `PolymarketProvider.placeOrder` by flooring BUY `minAmountWithSlippage` at `maxAmountSpent + tickSize` before rounding to avoid invalid share prices. > > - **Predict/Polymarket**: > - Update `placeOrder` slippage logic: > - Compute `_minWithSlippage = minAmountReceived * (1 - slippage)`. > - For `BUY`, floor to `maxAmountSpent + tickSize` before rounding via `roundOrderAmount`. > - Use the rounded value to derive `takerAmount`. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit dc8a157e44c5f3074e9daebb749580c19e83c3d0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../polymarket/PolymarketProvider.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/components/UI/Predict/providers/polymarket/PolymarketProvider.ts b/app/components/UI/Predict/providers/polymarket/PolymarketProvider.ts index 788db1b8deb..09a49af9e84 100644 --- a/app/components/UI/Predict/providers/polymarket/PolymarketProvider.ts +++ b/app/components/UI/Predict/providers/polymarket/PolymarketProvider.ts @@ -588,11 +588,27 @@ export class PolymarketProvider implements PredictProvider { throw new Error('Maker address not found'); } - // Introduce slippage into minAmountReceived to reduce failure rate + /* + * Introduce slippage into minAmountReceived to reduce failure rate. + */ const roundConfig = ROUNDING_CONFIG[tickSize.toString() as TickSize]; const decimals = roundConfig.amount ?? 4; + + let _minWithSlippage = minAmountReceived * (1 - slippage); + /* + * For BUY orders, the minAmountWithSlippage needs to be capped at + * maxAmountSpent + tickSize, otherwise, the order will fail due to + * sharePrice being >= 1 (which is impossible). + */ + if (side === Side.BUY) { + _minWithSlippage = Math.max( + _minWithSlippage, + maxAmountSpent + tickSize, + ); + } + const minAmountWithSlippage = roundOrderAmount({ - amount: minAmountReceived * (1 - slippage), + amount: _minWithSlippage, decimals, });