diff --git a/app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx b/app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx index 853ef3e8ff20..33ea2b69f9df 100644 --- a/app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx +++ b/app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx @@ -800,7 +800,6 @@ const PerpsClosePositionView: React.FC = () => { (orderType === 'limit' && (!limitPrice || parseFloat(limitPrice) <= 0)) || (orderType === 'market' && closePercentage === 0) || - receiveAmount <= 0 || !validationResult.isValid } loading={isClosing} diff --git a/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.test.ts b/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.test.ts index 4b9b309ddf16..1244c1490a05 100644 --- a/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.test.ts +++ b/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.test.ts @@ -110,7 +110,7 @@ describe('usePerpsClosePositionValidation', () => { expect(result.current.errors).toHaveLength(0); }); - it('should return error when user will receive negative amount', async () => { + it('returns warning (not error) when user will receive negative amount', async () => { mockValidateClosePosition.mockResolvedValue({ isValid: true }); const params = { @@ -126,9 +126,12 @@ describe('usePerpsClosePositionValidation', () => { expect(result.current.isValidating).toBe(false); }); - expect(result.current.isValid).toBe(false); - expect(result.current.errors).toContain( - strings('perps.close_position.negative_receive_amount'), + expect(result.current.isValid).toBe(true); + expect(result.current.errors).toHaveLength(0); + expect(result.current.warnings).toContain( + strings('perps.close_position.negative_receive_warning', { + amount: '100.00', + }), ); }); diff --git a/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.ts b/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.ts index 62af8b8dfee0..0e55d2451045 100644 --- a/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.ts +++ b/app/components/UI/Perps/hooks/usePerpsClosePositionValidation.ts @@ -54,17 +54,12 @@ interface ValidationResult { * - Example: ERROR - Trying to close a $5 position (below $6 minimum on mainnet) * - Note: This shouldn't happen if positions were opened correctly * - * 4. NEGATIVE RECEIVE AMOUNT - * - When fees exceed the position value + P&L - * - Example: ERROR - Position worth $10, fees are $12, user would receive -$2 - * - Protects users from paying to close losing positions - * - * 5. MISSING LIMIT PRICE (checked by protocol validation) + * 4. MISSING LIMIT PRICE (checked by protocol validation) * - Limit orders require a price to be specified * - Example: ERROR - Limit order with no price or price <= 0 * - Note: This is validated by the provider, not duplicate-checked in UI * - * 6. ZERO AMOUNT FOR MARKET ORDER + * 5. ZERO AMOUNT FOR MARKET ORDER * - Market orders with 0% close percentage * - Example: ERROR - Slider at 0% for market order * @@ -84,6 +79,11 @@ interface ValidationResult { * - Example: WARNING - Closing only 2% of a $5,000 position ($100) * - Useful for taking small profits but may not justify transaction costs * + * 3. NEGATIVE RECEIVE AMOUNT + * - When fees exceed the recoverable value from the position + * - Example: WARNING - Closing will cost you $2 due to fees and losses + * - User can still proceed to close the position if they choose to exit + * * ========================================== * SPECIAL BEHAVIORS: * ========================================== @@ -168,9 +168,13 @@ export function usePerpsClosePositionValidation( ); } - // Check if user will receive a positive amount after fees - if (receiveAmount <= 0) { - errors.push(strings('perps.close_position.negative_receive_amount')); + // Warn if user will receive negative amount (but allow them to proceed) + if (receiveAmount < 0) { + warnings.push( + strings('perps.close_position.negative_receive_warning', { + amount: Math.abs(receiveAmount).toFixed(2), + }), + ); } // Limit order specific validation (price warning only - required check is done by protocol) diff --git a/locales/languages/en.json b/locales/languages/en.json index 86e04c5c09b4..1dc404063fb1 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -1196,6 +1196,7 @@ "minimum_remaining_error": "Cannot partial close: remaining position (${{remaining}}) would be below minimum order size (${{minimum}}). Please close 100% instead.", "must_close_full_below_minimum": "Position value is below $10. You must close 100% of the position.", "negative_receive_amount": "The fees exceed your position value", + "negative_receive_warning": "Closing this position will cost you ${{amount}} due to fees and losses.", "no_amount_selected": "Please select an amount to close", "order_type_reverted_to_market_order": "Changed to market order", "you_need_set_price_limit_order": "You need to set a price for a limit order."