Skip to content

Commit a3fd8bf

Browse files
committed
Merge branch 'main' of https://github.com/MetaMask/metamask-mobile into feat/deposit/transactions-analytics
2 parents 1ddd5db + e6bfd36 commit a3fd8bf

File tree

39 files changed

+1099
-120
lines changed

39 files changed

+1099
-120
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Stable Branch Sync
2+
3+
permissions:
4+
pull-requests: write
5+
contents: write
6+
issues: write
7+
8+
on:
9+
push:
10+
branches:
11+
- stable
12+
13+
jobs:
14+
get-next-version:
15+
name: Get next version vX.Y.Z
16+
runs-on: ubuntu-latest
17+
outputs:
18+
next-version: ${{ env.NEXT_SEMVER_VERSION }}
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
25+
- name: Configure Git
26+
run: |
27+
git config --global user.name "github-actions[bot]"
28+
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
29+
30+
- name: Get the next semver version
31+
id: get-next-semver-version
32+
env:
33+
FORCE_NEXT_SEMVER_VERSION: ${{ vars.FORCE_NEXT_SEMVER_VERSION }}
34+
run: ./get-next-semver-version.sh "$FORCE_NEXT_SEMVER_VERSION"
35+
working-directory: '.github/scripts'
36+
37+
run-stable-sync:
38+
name: Run Stable branch sync
39+
needs: get-next-version
40+
uses: metamask/github-tools/.github/workflows/stable-sync.yml@86272c846618950a4e5518ff5d8dffc5679d88ce
41+
secrets:
42+
GITHUB_TOKEN: ${{ secrets.STABLE_SYNC_TOKEN }}
43+
with:
44+
semver-version: ${{ needs.get-next-version.outputs.next-version }}
45+
repo-type: 'mobile' # Accepts 'mobile' or 'extension'
46+
github-tools-version: '86272c846618950a4e5518ff5d8dffc5679d88ce'
47+
stable-branch-name: 'stable'
-514 Bytes
Loading

app/components/UI/AssetElement/__snapshots__/index.test.tsx.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ exports[`AssetElement should render correctly 1`] = `
1111
"flex": 1,
1212
"flexDirection": "row",
1313
"height": 64,
14-
"paddingHorizontal": 16,
1514
}
1615
}
1716
testID="asset-DAI"

app/components/UI/AssetElement/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const createStyles = (colors: Colors) =>
3737
itemWrapper: {
3838
flex: 1,
3939
flexDirection: 'row',
40-
paddingHorizontal: 16,
4140
height: 64,
4241
alignItems: 'center',
4342
},

app/components/UI/AssetOverview/Balance/__snapshots__/index.test.tsx.snap

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ exports[`Balance should render correctly with main and secondary balance 1`] = `
3434
"flex": 1,
3535
"flexDirection": "row",
3636
"height": 64,
37-
"paddingHorizontal": 16,
3837
}
3938
}
4039
testID="asset-DAI"
@@ -278,7 +277,6 @@ exports[`Balance should render correctly without a secondary balance 1`] = `
278277
"flex": 1,
279278
"flexDirection": "row",
280279
"height": 64,
281-
"paddingHorizontal": 16,
282280
}
283281
}
284282
testID="asset-DAI"

app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,6 @@ exports[`AssetOverview should render correctly 1`] = `
926926
"flex": 1,
927927
"flexDirection": "row",
928928
"height": 64,
929-
"paddingHorizontal": 16,
930929
}
931930
}
932931
testID="asset-ETH"
@@ -2081,7 +2080,6 @@ exports[`AssetOverview should render native balances even if there are no accoun
20812080
"flex": 1,
20822081
"flexDirection": "row",
20832082
"height": 64,
2084-
"paddingHorizontal": 16,
20852083
}
20862084
}
20872085
testID="asset-ETH"
@@ -3242,7 +3240,6 @@ exports[`AssetOverview should render native balances when non evm network is sel
32423240
"flex": 1,
32433241
"flexDirection": "row",
32443242
"height": 64,
3245-
"paddingHorizontal": 16,
32463243
}
32473244
}
32483245
testID="asset-ETH"

app/components/UI/Bridge/Views/BridgeView/BridgeView.test.tsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,100 @@ describe('BridgeView', () => {
390390
});
391391
});
392392

393+
it('should not display max button when source token is native token', () => {
394+
const stateWithNativeToken = {
395+
...mockState,
396+
bridge: {
397+
...mockState.bridge,
398+
sourceToken: {
399+
address: '0x0000000000000000000000000000000000000000', // Native ETH address
400+
chainId: '0x1' as Hex,
401+
decimals: 18,
402+
image: '',
403+
name: 'Ether',
404+
symbol: 'ETH',
405+
},
406+
},
407+
};
408+
409+
const { queryByText } = renderScreen(
410+
BridgeView,
411+
{
412+
name: Routes.BRIDGE.ROOT,
413+
},
414+
{ state: stateWithNativeToken },
415+
);
416+
417+
// Verify max button is not present for native token
418+
expect(queryByText('Max')).toBeNull();
419+
});
420+
421+
it('should display max button when source token is not native token', () => {
422+
const stateWithERC20Token = {
423+
...mockState,
424+
bridge: {
425+
...mockState.bridge,
426+
sourceToken: {
427+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC token address
428+
chainId: '0x1' as Hex,
429+
decimals: 6,
430+
image:
431+
'https://static.cx.metamask.io/api/v1/tokenIcons/1/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.png',
432+
name: 'USD Coin',
433+
symbol: 'USDC',
434+
},
435+
},
436+
};
437+
438+
const { queryByText } = renderScreen(
439+
BridgeView,
440+
{
441+
name: Routes.BRIDGE.ROOT,
442+
},
443+
{ state: stateWithERC20Token },
444+
);
445+
446+
// Verify max button is present for ERC-20 token
447+
expect(queryByText('Max')).toBeTruthy();
448+
});
449+
450+
it('should set source amount to maximum balance when max button is pressed', async () => {
451+
const stateWithERC20Token = {
452+
...mockState,
453+
bridge: {
454+
...mockState.bridge,
455+
sourceToken: {
456+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC token address
457+
chainId: '0x1' as Hex,
458+
decimals: 6,
459+
image:
460+
'https://static.cx.metamask.io/api/v1/tokenIcons/1/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.png',
461+
name: 'USD Coin',
462+
symbol: 'USDC',
463+
},
464+
},
465+
};
466+
467+
const { getByText, getByTestId } = renderScreen(
468+
BridgeView,
469+
{
470+
name: Routes.BRIDGE.ROOT,
471+
},
472+
{ state: stateWithERC20Token },
473+
);
474+
475+
// Find and press the max button
476+
const maxButton = getByText('Max');
477+
expect(maxButton).toBeTruthy();
478+
fireEvent.press(maxButton);
479+
480+
// Verify the input value is set to the maximum available balance (2.0 from useLatestBalance mock)
481+
const input = getByTestId('source-token-area-input');
482+
await waitFor(() => {
483+
expect(input.props.value).toBe('2.0');
484+
});
485+
});
486+
393487
it('should switch tokens when clicking arrow button', () => {
394488
const mockStateWithTokens = {
395489
...mockState,

app/components/UI/Bridge/Views/BridgeView/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ const BridgeView = () => {
257257
}
258258
}, [isError]);
259259

260+
// Keypad already handles max token decimals, so we don't need to check here
260261
const handleKeypadChange = ({
261262
value,
262263
}: {
@@ -445,6 +446,11 @@ const BridgeView = () => {
445446
onFocus={() => setIsInputFocused(true)}
446447
onBlur={() => setIsInputFocused(false)}
447448
onInputPress={() => setIsInputFocused(true)}
449+
onMaxPress={() => {
450+
if (latestSourceBalance?.displayBalance) {
451+
dispatch(setSourceAmount(latestSourceBalance.displayBalance));
452+
}
453+
}}
448454
/>
449455
<Box style={styles.arrowContainer}>
450456
<Box style={styles.arrowCircle}>

app/components/UI/Bridge/components/TokenInputArea/TokenInputArea.test.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ import {
88
calculateFontSize,
99
getDisplayAmount,
1010
} from '.';
11+
import { BridgeToken } from '../../types';
12+
13+
jest.mock('../../hooks/useLatestBalance', () => ({
14+
useLatestBalance: jest.fn(),
15+
}));
1116

1217
const mockOnTokenPress = jest.fn();
1318
const mockOnFocus = jest.fn();
1419
const mockOnBlur = jest.fn();
1520
const mockOnInputPress = jest.fn();
21+
const mockOnMaxPress = jest.fn();
1622

1723
describe('TokenInputArea', () => {
1824
beforeEach(() => {
@@ -63,6 +69,100 @@ describe('TokenInputArea', () => {
6369
fireEvent(input, 'blur');
6470
expect(mockOnBlur).toHaveBeenCalled();
6571
});
72+
73+
it('displays max button for source token with balance and calls onMaxPress when clicked', () => {
74+
// Arrange
75+
const mockToken: BridgeToken = {
76+
address: '0x1234567890123456789012345678901234567890',
77+
symbol: 'TEST',
78+
decimals: 18,
79+
chainId: '0x1' as `0x${string}`,
80+
};
81+
const tokenBalance = '100.5';
82+
83+
const { getByText } = renderScreen(
84+
() => (
85+
<TokenInputArea
86+
testID="token-input"
87+
tokenType={TokenInputAreaType.Source}
88+
token={mockToken}
89+
tokenBalance={tokenBalance}
90+
onMaxPress={mockOnMaxPress}
91+
/>
92+
),
93+
{
94+
name: 'TokenInputArea',
95+
},
96+
{ state: initialState },
97+
);
98+
99+
// Act
100+
const maxButton = getByText('Max');
101+
fireEvent.press(maxButton);
102+
103+
// Assert
104+
expect(mockOnMaxPress).toHaveBeenCalledTimes(1);
105+
});
106+
107+
it('does not display max button for destination token', () => {
108+
// Arrange
109+
const mockToken: BridgeToken = {
110+
address: '0x1234567890123456789012345678901234567890',
111+
symbol: 'TEST',
112+
decimals: 18,
113+
chainId: '0x1' as `0x${string}`,
114+
};
115+
const tokenBalance = '100.5';
116+
117+
const { queryByText } = renderScreen(
118+
() => (
119+
<TokenInputArea
120+
testID="token-input"
121+
tokenType={TokenInputAreaType.Destination}
122+
token={mockToken}
123+
tokenBalance={tokenBalance}
124+
onMaxPress={mockOnMaxPress}
125+
/>
126+
),
127+
{
128+
name: 'TokenInputArea',
129+
},
130+
{ state: initialState },
131+
);
132+
133+
// Assert
134+
expect(queryByText('Max')).toBeNull();
135+
});
136+
137+
it('does not display max button for native assets', () => {
138+
// Arrange
139+
const nativeToken: BridgeToken = {
140+
address: '0x0000000000000000000000000000000000000000', // AddressZero for native ETH
141+
symbol: 'ETH',
142+
decimals: 18,
143+
chainId: '0x1' as `0x${string}`,
144+
};
145+
const tokenBalance = '1.5';
146+
147+
const { queryByText } = renderScreen(
148+
() => (
149+
<TokenInputArea
150+
testID="token-input"
151+
tokenType={TokenInputAreaType.Source}
152+
token={nativeToken}
153+
tokenBalance={tokenBalance}
154+
onMaxPress={mockOnMaxPress}
155+
/>
156+
),
157+
{
158+
name: 'TokenInputArea',
159+
},
160+
{ state: initialState },
161+
);
162+
163+
// Assert
164+
expect(queryByText('Max')).toBeNull();
165+
});
66166
});
67167

68168
describe('calculateFontSize', () => {

0 commit comments

Comments
 (0)