Skip to content

Commit 56c8c14

Browse files
authored
Merge branch 'main' into universal-dev-mode-feature-flag
2 parents 6712100 + 3ee5c69 commit 56c8c14

111 files changed

Lines changed: 4195 additions & 1983 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-ios-upload-to-browserstack.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,13 @@ jobs:
165165
WITHOUT_SRP_ERROR=""
166166
167167
# Find the IPA files (build.yml renames them; find the single .ipa in each directory)
168-
WITH_SRP_IPA=$(find artifacts/with-srp -name "*.ipa" | head -1)
169-
WITHOUT_SRP_IPA=$(find artifacts/without-srp -name "*.ipa" | head -1)
168+
WITH_SRP_IPA=$(find artifacts/with-srp -iname '*.ipa' -type f 2>/dev/null | head -1)
169+
WITHOUT_SRP_IPA=$(find artifacts/without-srp -iname '*.ipa' -type f 2>/dev/null | head -1)
170+
171+
if [ -z "$WITH_SRP_IPA" ] || [ -z "$WITHOUT_SRP_IPA" ]; then
172+
echo "IPA search (artifact layout debug):"
173+
find artifacts/with-srp artifacts/without-srp -type f 2>/dev/null | head -80 || true
174+
fi
170175
171176
# Upload with-SRP IPA
172177
if [ -n "$WITH_SRP_IPA" ]; then

.github/workflows/build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,9 @@ jobs:
461461
- name: Build ${{ matrix.platform }}
462462
timeout-minutes: 115
463463
env:
464+
# Must match Apply build config / artifact naming so build.sh loads the same
465+
# builds.yml entry (e.g. main-e2e-bs-with-srp), not generic main-e2e (sim-only).
466+
BUILD_CONFIG_NAME: ${{ inputs.build_name }}
464467
GIT_BRANCH: ${{ inputs.source_branch || github.ref_name }}
465468
# React Native 0.81's ReactAndroid/build.gradle.kts requests CMake 3.30.5
466469
# via `System.getenv("CMAKE_VERSION") ?: "3.30.5"`. The self-hosted runner

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [7.77.1]
11+
12+
### Added
13+
14+
- Added support for cross-chain withdrawals through MetaMask Pay in Predict for users with a Polymarket Deposit Wallet, while keeping the existing "withdrawals unavailable" sheet for users who do not have the feature enabled. (#29953)
15+
16+
### Fixed
17+
18+
- Fixed a bug that caused a user's first Predict deposit to fail while their Polymarket Deposit Wallet was still being registered. (#30267)
19+
1020
## [7.77.0]
1121

1222
### Added
@@ -11494,7 +11504,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1149411504
- [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957)
1149511505
- [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954)
1149611506

11497-
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.77.0...HEAD
11507+
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.77.1...HEAD
11508+
[7.77.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.77.0...v7.77.1
1149811509
[7.77.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.76.3...v7.77.0
1149911510
[7.76.3]: https://github.com/MetaMask/metamask-mobile/compare/v7.76.0...v7.76.3
1150011511
[7.76.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.75.1...v7.76.0

app/component-library/components-temp/SectionHeader/SectionHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const SectionHeader: React.FC<SectionHeaderProps> = ({
7777
<Icon
7878
testID="section-header-arrow-icon"
7979
name={endIconName}
80-
size={IconSize.Sm}
80+
size={IconSize.Md}
8181
color={endIconColor}
8282
style={tw.style('ml-1')}
8383
/>

app/components/Nav/App/App.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ import ConnectionDetails from '../../../components/Views/AccountPermissions/Conn
5555
import { SRPQuiz } from '../../Views/Quiz';
5656
import { TurnOffRememberMeModal } from '../../../components/UI/TurnOffRememberMeModal';
5757
import AssetHideConfirmation from '../../Views/AssetHideConfirmation';
58-
import AssetOptions from '../../Views/AssetOptions';
5958
import ImportPrivateKey from '../../Views/ImportPrivateKey';
6059
import ImportPrivateKeySuccess from '../../Views/ImportPrivateKeySuccess';
6160
import ConnectQRHardware from '../../Views/ConnectQRHardware';
@@ -580,7 +579,6 @@ const RootModalFlow = (props: RootModalFlowProps) => (
580579
name={'AssetHideConfirmation'}
581580
component={AssetHideConfirmation}
582581
/>
583-
<Stack.Screen name={'AssetOptions'} component={AssetOptions} />
584582
<Stack.Screen name={'NftOptions'} component={NftOptions} />
585583
<Stack.Screen name={Routes.MODAL.UPDATE_NEEDED} component={UpdateNeeded} />
586584
<Stack.Screen

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ import {
118118
ButtonSize,
119119
ButtonVariants,
120120
} from '../../../../../component-library/components/Buttons/Button/Button.types.ts';
121+
import { useIsNetworkFeeUnavailable } from '../../hooks/useIsNetworkFeeUnavailable/index.ts';
121122

122123
const SCROLL_NEAR_BOTTOM_PX = 160;
123124

@@ -271,7 +272,9 @@ const BridgeViewContent = ({ latestSourceBalance }: BridgeViewContentProps) => {
271272
// Destination address is only needed for EVM <> Non-EVM bridges, or Non-EVM <> Non-EVM bridges (when different)
272273
(!hasDestinationPicker || (hasDestinationPicker && Boolean(destAddress)));
273274

275+
const isNetworkFeeUnavailable = useIsNetworkFeeUnavailable(activeQuote);
274276
const hasSufficientGas = useHasSufficientGas({ quote: activeQuote });
277+
const hasInsufficientGas = !isNetworkFeeUnavailable && !hasSufficientGas;
275278
const hasInsufficientBalance = useIsInsufficientBalance({
276279
amount: sourceAmount,
277280
token: sourceToken,
@@ -310,18 +313,20 @@ const BridgeViewContent = ({ latestSourceBalance }: BridgeViewContentProps) => {
310313
(isLoading && !activeQuote) ||
311314
hasInsufficientBalance ||
312315
hasInsufficientNativeReserveError ||
316+
isNetworkFeeUnavailable ||
313317
isSubmittingTx ||
314318
(isHardwareAddress && isSolanaSourced) ||
315319
!!blockaidError ||
316-
!hasSufficientGas ||
320+
hasInsufficientGas ||
317321
!walletAddress;
318322

319323
useBridgeQuoteEvents({
320324
hasInsufficientBalance,
321325
hasInsufficientNativeReserveError,
322326
hasNoQuotesAvailable: isNoQuotesAvailable,
323-
hasInsufficientGas: !hasSufficientGas,
327+
hasInsufficientGas,
324328
hasTxAlert: Boolean(blockaidError),
329+
isNetworkFeeUnavailable,
325330
isSubmitDisabled,
326331
isPriceImpactWarningVisible: shouldShowPriceImpactWarning,
327332
});

app/components/UI/Bridge/components/BridgeTrendingTokensSection/BridgeTrendingTokensSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ const BridgeTrendingTokensSection = ({
146146
testID={BridgeTrendingTokensSectionTestIds.SECTION}
147147
>
148148
<Text
149-
variant={TextVariant.HeadingLg}
149+
variant={TextVariant.HeadingMd}
150150
fontWeight={FontWeight.Bold}
151-
twClassName="mb-3"
151+
twClassName="mt-1 mb-3"
152152
>
153153
{strings('trending.trending_tokens')}
154154
</Text>

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

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import { MOCK_ENTROPY_SOURCE as mockEntropySource } from '../../../../../util/te
2121
import { BigNumber } from 'ethers';
2222
import Engine from '../../../../../core/Engine';
2323
import { setSourceAmount } from '../../../../../core/redux/slices/bridge';
24-
import { MetaMetricsSwapsEventSource } from '@metamask/bridge-controller';
24+
import {
25+
ChainId,
26+
MetaMetricsSwapsEventSource,
27+
} from '@metamask/bridge-controller';
2528
import { PriceImpactModalType } from '../PriceImpactModal/constants';
2629
import { TokenWarningModalMode } from '../TokenWarningModal/constants';
2730
import { SecurityDataType } from '../../types';
@@ -133,6 +136,18 @@ const mockActiveQuote = {
133136
},
134137
};
135138

139+
const mockBtcQuoteWithUnavailableNetworkFee = {
140+
...mockActiveQuote,
141+
quote: {
142+
...mockActiveQuote.quote,
143+
srcChainId: ChainId.BTC,
144+
},
145+
totalNetworkFee: {
146+
...mockActiveQuote.totalNetworkFee,
147+
amount: '0',
148+
},
149+
};
150+
136151
// Mock useBridgeQuoteData
137152
jest.mock('../../hooks/useBridgeQuoteData', () => ({
138153
useBridgeQuoteData: jest
@@ -337,6 +352,30 @@ describe('SwapsConfirmButton', () => {
337352
expect(getByText(strings('bridge.insufficient_gas'))).toBeTruthy();
338353
});
339354

355+
it('displays "Insufficient funds" when BTC network fee is unavailable', () => {
356+
jest
357+
.mocked(useBridgeQuoteData as unknown as jest.Mock)
358+
.mockImplementation(() => ({
359+
...mockUseBridgeQuoteData,
360+
activeQuote: mockBtcQuoteWithUnavailableNetworkFee,
361+
}));
362+
363+
const { getByText } = renderWithProvider(
364+
<SwapsConfirmButton
365+
latestSourceBalance={mockLatestSourceBalance}
366+
location={MetaMetricsSwapsEventSource.MainView}
367+
/>,
368+
{
369+
state: mockState,
370+
},
371+
);
372+
373+
expect(getByText(strings('bridge.insufficient_funds'))).toBeTruthy();
374+
expect(useHasSufficientGas).toHaveBeenCalledWith({
375+
quote: mockBtcQuoteWithUnavailableNetworkFee,
376+
});
377+
});
378+
340379
it('hides label behind loading indicator when submitting', () => {
341380
const submittingState = {
342381
...mockState,
@@ -773,6 +812,30 @@ describe('SwapsConfirmButton', () => {
773812
// Label visible because not loading/submitting
774813
expect(getByText(strings('bridge.insufficient_gas'))).toBeTruthy();
775814
});
815+
816+
it('does not show loading when disabled due to unavailable BTC network fee', () => {
817+
jest
818+
.mocked(useBridgeQuoteData as unknown as jest.Mock)
819+
.mockImplementation(() => ({
820+
...mockUseBridgeQuoteData,
821+
activeQuote: mockBtcQuoteWithUnavailableNetworkFee,
822+
}));
823+
824+
const { getByText, getByTestId, queryByTestId } = renderWithProvider(
825+
<SwapsConfirmButton
826+
latestSourceBalance={mockLatestSourceBalance}
827+
location={MetaMetricsSwapsEventSource.MainView}
828+
/>,
829+
{
830+
state: mockState,
831+
},
832+
);
833+
834+
const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
835+
expect(button.props.accessibilityState?.disabled).toBe(true);
836+
expect(queryByTestId('spinner-container')).toBeNull();
837+
expect(getByText(strings('bridge.insufficient_funds'))).toBeTruthy();
838+
});
776839
});
777840

778841
describe('Stale Quote (isQuoteStale)', () => {
@@ -868,6 +931,33 @@ describe('SwapsConfirmButton', () => {
868931
).toBeTruthy();
869932
});
870933

934+
it('keeps "Get new quote" enabled when BTC network fee is unavailable', () => {
935+
jest
936+
.mocked(useBridgeQuoteData as unknown as jest.Mock)
937+
.mockImplementation(() => ({
938+
...mockUseBridgeQuoteData,
939+
activeQuote: mockBtcQuoteWithUnavailableNetworkFee,
940+
needsNewQuote: true,
941+
isLoading: false,
942+
}));
943+
944+
const { getByText, getByTestId } = renderWithProvider(
945+
<SwapsConfirmButton
946+
latestSourceBalance={mockLatestSourceBalance}
947+
location={MetaMetricsSwapsEventSource.MainView}
948+
/>,
949+
{
950+
state: mockState,
951+
},
952+
);
953+
954+
const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
955+
expect(button.props.accessibilityState?.disabled).toBe(false);
956+
expect(
957+
getByText(strings('quote_expired_modal.get_new_quote')),
958+
).toBeTruthy();
959+
});
960+
871961
it('button is not disabled when quote is expired', () => {
872962
jest
873963
.mocked(useBridgeQuoteData as unknown as jest.Mock)

app/components/UI/Bridge/components/SwapsConfirmButton/index.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import type { TokenWarningModalParams } from '../TokenWarningModal';
4040
import { TokenWarningModalMode } from '../TokenWarningModal/constants';
4141
import type { TransactionActiveAbTestEntry } from '../../../../../util/transactions/transaction-active-ab-test-attribution-registry';
4242
import { useInsufficientNativeReserveError } from '../../hooks/useInsufficientNativeReserveError';
43+
import { useIsNetworkFeeUnavailable } from '../../hooks/useIsNetworkFeeUnavailable';
4344

4445
interface Props {
4546
latestSourceBalance: ReturnType<typeof useLatestBalance>;
@@ -105,7 +106,9 @@ export const SwapsConfirmButton = ({
105106
transactionActiveAbTests,
106107
});
107108

109+
const isNetworkFeeUnavailable = useIsNetworkFeeUnavailable(activeQuote);
108110
const hasSufficientGas = useHasSufficientGas({ quote: activeQuote });
111+
const hasInsufficientGas = !isNetworkFeeUnavailable && !hasSufficientGas;
109112

110113
// Check both the display amount and the atomic amount are non-zero.
111114
// An amount like 0.000000001 BTC (8 decimals) is non-zero as a number but
@@ -166,10 +169,11 @@ export const SwapsConfirmButton = ({
166169
(isLoading && !activeQuote) ||
167170
hasInsufficientBalance ||
168171
hasInsufficientNativeReserveError ||
172+
isNetworkFeeUnavailable ||
169173
isSubmittingTx ||
170174
(isHardwareAddress && isSolanaSourced) ||
171175
hasError ||
172-
!hasSufficientGas ||
176+
hasInsufficientGas ||
173177
!walletAddress;
174178

175179
const handleContinue = async () => {
@@ -248,9 +252,13 @@ export const SwapsConfirmButton = ({
248252
return strings('bridge.confirm_swap');
249253
}
250254

251-
if (hasInsufficientBalance || hasInsufficientNativeReserveError)
255+
if (
256+
hasInsufficientBalance ||
257+
hasInsufficientNativeReserveError ||
258+
isNetworkFeeUnavailable
259+
)
252260
return strings('bridge.insufficient_funds');
253-
if (!hasSufficientGas) return strings('bridge.insufficient_gas');
261+
if (hasInsufficientGas) return strings('bridge.insufficient_gas');
254262
if (isSubmittingTx) return strings('bridge.submitting_transaction');
255263

256264
return strings('bridge.confirm_swap');
@@ -260,7 +268,8 @@ export const SwapsConfirmButton = ({
260268
sourceAmount,
261269
hasInsufficientBalance,
262270
hasInsufficientNativeReserveError,
263-
hasSufficientGas,
271+
isNetworkFeeUnavailable,
272+
hasInsufficientGas,
264273
isSubmittingTx,
265274
needsNewQuote,
266275
]);

app/components/UI/Bridge/hooks/useBridgeQuoteEvents/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const useBridgeQuoteEvents = ({
2222
hasInsufficientNativeReserveError,
2323
hasNoQuotesAvailable,
2424
hasInsufficientGas,
25+
isNetworkFeeUnavailable,
2526
hasTxAlert,
2627
isSubmitDisabled,
2728
isPriceImpactWarningVisible,
@@ -30,6 +31,7 @@ export const useBridgeQuoteEvents = ({
3031
hasInsufficientNativeReserveError: boolean;
3132
hasNoQuotesAvailable: boolean;
3233
hasInsufficientGas: boolean;
34+
isNetworkFeeUnavailable: boolean;
3335
hasTxAlert: boolean;
3436
isSubmitDisabled: boolean;
3537
isPriceImpactWarningVisible: boolean;
@@ -47,8 +49,11 @@ export const useBridgeQuoteEvents = ({
4749
const latestWarnings: QuoteWarning[] = [];
4850

4951
hasNoQuotesAvailable && latestWarnings.push('no_quotes');
50-
hasInsufficientGas &&
52+
if (isNetworkFeeUnavailable) {
53+
latestWarnings.push('network_fee_unavailable' as QuoteWarning);
54+
} else if (hasInsufficientGas) {
5155
latestWarnings.push('insufficient_gas_for_selected_quote');
56+
}
5257
hasInsufficientBalance && latestWarnings.push('insufficient_balance');
5358
hasInsufficientNativeReserveError &&
5459
// @ts-expect-error - 'insufficient_native_reserve' is a valid QuoteWarning
@@ -60,6 +65,7 @@ export const useBridgeQuoteEvents = ({
6065
}, [
6166
hasNoQuotesAvailable,
6267
hasInsufficientGas,
68+
isNetworkFeeUnavailable,
6369
hasInsufficientBalance,
6470
hasInsufficientNativeReserveError,
6571
hasTxAlert,

0 commit comments

Comments
 (0)