Skip to content

Commit 438de2d

Browse files
chore(runway): cherry-pick fix: cp-7.59.0 Fix minimum BTC amount validation in send flow (#22262)
- fix: cp-7.59.0 Fix minimum BTC amount validation in send flow (#22245) <!-- 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** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> This PR aims to fix BTC amount validation in send flow. As we will implement `onAmountInput` validations - this is a temporary fix to handle it in the client code. In near future this validation will be handled in snap repository. ## **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: null ## **Related issues** Fixes: #22204 ## **Manual testing steps** 1. Go send flow 2. Pick BTC 3. Try sending lower than 0.0001 4. You shouldn't be able to proceed to recipient page ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Enforces a 0.0001 BTC minimum in `useAmountValidation` with Bitcoin-aware logic and updates tests accordingly. > > - **Hooks**: > - `useAmountValidation` > - Add Bitcoin-specific minimum amount check (`0.0001 BTC`) using `BigNumber` and `useSendType`. > - Return `send.invalid_value` when below threshold; update memo deps. > - **Tests**: > - `useAmountValidation.test.ts` > - Mock `useSendType` and add cases for below/at/above Bitcoin minimum. > - Keep existing validations for numeric input and balance checks. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ce11a3e. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> [f3cfb8c](f3cfb8c) Co-authored-by: OGPoyraz <omergoktugpoyraz@gmail.com>
1 parent 6787571 commit 438de2d

2 files changed

Lines changed: 92 additions & 1 deletion

File tree

app/components/Views/confirmations/hooks/send/useAmountValidation.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { evmSendStateMock } from '../../__mocks__/send.mock';
55
import { useSendContext } from '../../context/send-context';
66
import { useAmountValidation } from './useAmountValidation';
77
import { useBalance } from './useBalance';
8+
import { useSendType } from './useSendType';
89

910
jest.mock('../../context/send-context', () => ({
1011
useSendContext: jest.fn(),
@@ -14,6 +15,10 @@ jest.mock('./useBalance', () => ({
1415
useBalance: jest.fn(),
1516
}));
1617

18+
jest.mock('./useSendType', () => ({
19+
useSendType: jest.fn(),
20+
}));
21+
1722
const mockState = {
1823
state: evmSendStateMock,
1924
};
@@ -24,7 +29,15 @@ const mockUseSendContext = useSendContext as jest.MockedFunction<
2429

2530
const mockUseBalance = useBalance as jest.MockedFunction<typeof useBalance>;
2631

32+
const mockUseSendType = useSendType as jest.MockedFunction<typeof useSendType>;
33+
2734
describe('useAmountValidation', () => {
35+
beforeEach(() => {
36+
mockUseSendType.mockReturnValue({
37+
isBitcoinSendType: false,
38+
} as ReturnType<typeof useSendType>);
39+
});
40+
2841
it('return field for amount error', () => {
2942
mockUseSendContext.mockReturnValue({
3043
value: '',
@@ -110,4 +123,67 @@ describe('useAmountValidation', () => {
110123
);
111124
expect(result.current.amountError).toEqual(undefined);
112125
});
126+
127+
it('returns error for Bitcoin amount below minimum threshold', () => {
128+
mockUseSendType.mockReturnValue({
129+
isBitcoinSendType: true,
130+
} as ReturnType<typeof useSendType>);
131+
mockUseSendContext.mockReturnValue({
132+
value: '0.00005',
133+
} as unknown as ReturnType<typeof useSendContext>);
134+
mockUseBalance.mockReturnValue({
135+
balance: '1',
136+
decimals: 8,
137+
rawBalanceBN: new BN('100000000'),
138+
});
139+
140+
const { result } = renderHookWithProvider(
141+
() => useAmountValidation(),
142+
mockState,
143+
);
144+
145+
expect(result.current.amountError).toEqual('Invalid value');
146+
});
147+
148+
it('does not return error for Bitcoin amount at minimum threshold', () => {
149+
mockUseSendType.mockReturnValue({
150+
isBitcoinSendType: true,
151+
} as ReturnType<typeof useSendType>);
152+
mockUseSendContext.mockReturnValue({
153+
value: '0.0001',
154+
} as unknown as ReturnType<typeof useSendContext>);
155+
mockUseBalance.mockReturnValue({
156+
balance: '1',
157+
decimals: 8,
158+
rawBalanceBN: new BN('100000000'),
159+
});
160+
161+
const { result } = renderHookWithProvider(
162+
() => useAmountValidation(),
163+
mockState,
164+
);
165+
166+
expect(result.current.amountError).toEqual(undefined);
167+
});
168+
169+
it('does not return error for Bitcoin amount above minimum threshold', () => {
170+
mockUseSendType.mockReturnValue({
171+
isBitcoinSendType: true,
172+
} as ReturnType<typeof useSendType>);
173+
mockUseSendContext.mockReturnValue({
174+
value: '0.5',
175+
} as unknown as ReturnType<typeof useSendContext>);
176+
mockUseBalance.mockReturnValue({
177+
balance: '1',
178+
decimals: 8,
179+
rawBalanceBN: new BN('100000000'),
180+
});
181+
182+
const { result } = renderHookWithProvider(
183+
() => useAmountValidation(),
184+
mockState,
185+
);
186+
187+
expect(result.current.amountError).toEqual(undefined);
188+
});
113189
});

app/components/Views/confirmations/hooks/send/useAmountValidation.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useMemo } from 'react';
2+
import { BigNumber } from 'bignumber.js';
23

34
import { strings } from '../../../../../../locales/i18n';
45
import {
@@ -8,10 +9,18 @@ import {
89

910
import { useSendContext } from '../../context/send-context';
1011
import { useBalance } from './useBalance';
12+
import { useSendType } from './useSendType';
13+
14+
const MINIMUM_BITCOIN_TRANSACTION_AMOUNT = new BigNumber('0.0001');
15+
const isValidBitcoinAmount = (value: string) => {
16+
const valueBN = new BigNumber(value);
17+
return valueBN.gte(MINIMUM_BITCOIN_TRANSACTION_AMOUNT);
18+
};
1119

1220
export const useAmountValidation = () => {
1321
const { value } = useSendContext();
1422
const { decimals, rawBalanceBN } = useBalance();
23+
const { isBitcoinSendType } = useSendType();
1524

1625
const amountError = useMemo(() => {
1726
if (value === undefined || value === null || value === '') {
@@ -24,8 +33,14 @@ export const useAmountValidation = () => {
2433
if (rawBalanceBN.cmp(amountInputBN) === -1) {
2534
return strings('send.insufficient_funds');
2635
}
36+
37+
// This is a temporary fix for the bitcoin send type.
38+
// Once onAmountInput validation is implemented, this can be removed.
39+
if (isBitcoinSendType && !isValidBitcoinAmount(value)) {
40+
return strings('send.invalid_value');
41+
}
2742
return undefined;
28-
}, [decimals, rawBalanceBN, value]);
43+
}, [decimals, isBitcoinSendType, rawBalanceBN, value]);
2944

3045
return { amountError };
3146
};

0 commit comments

Comments
 (0)