Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ app/core/DeeplinkManager/Handlers/handlePerpsUrl.ts @MetaMask/perps
**/Perps/** @MetaMask/perps
**/perps/** @MetaMask/perps

# Predict Team
app/components/UI/Predict/ @MetaMask/predict
app/core/Engine/controllers/predict-controller @MetaMask/predict
app/core/Engine/messengers/predict-controller-messenger @MetaMask/predict
app/core/DeeplinkManager/handlers/legacy/handlePredictUrl.ts @MetaMask/predict
**/Predict/** @MetaMask/predict
**/predict/** @MetaMask/predict

# Assets Team
app/components/hooks/useIsOriginalNativeTokenSymbol @MetaMask/metamask-assets
app/components/hooks/useTokenBalancesController @MetaMask/metamask-assets
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-release-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
pull-requests: write
steps:
- name: Create Release PR
uses: MetaMask/github-tools/.github/actions/create-release-pr@v1.1.0
uses: MetaMask/github-tools/.github/actions/create-release-pr@v1.1.2
with:
platform: mobile
checkout-base-branch: ${{ needs.resolve-bases.outputs.checkout_base }}
Expand Down
2 changes: 0 additions & 2 deletions .js.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ export MM_PERMISSIONS_SETTINGS_V1_ENABLED=""
## Stablecoin Lending
export MM_STABLECOIN_LENDING_UI_ENABLED="true"
export MM_STABLE_COIN_SERVICE_INTERRUPTION_BANNER_ENABLED="true"
### Redesigned stablecoin lending
export MM_STABLECOIN_LENDING_UI_ENABLED_REDESIGNED="true"
## Pooled-Staking
export MM_POOLED_STAKING_ENABLED="true"
export MM_POOLED_STAKING_SERVICE_INTERRUPTION_BANNER_ENABLED="true"
Expand Down
22 changes: 14 additions & 8 deletions app/components/UI/Bridge/hooks/useSwapBridgeNavigation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,15 @@ export const useSwapBridgeNavigation = ({

// Unified swaps/bridge UI
const goToNativeBridge = useCallback(
(bridgeViewMode: BridgeViewMode) => {
(bridgeViewMode: BridgeViewMode, tokenOverride?: BridgeToken) => {
// Use tokenOverride if provided, otherwise fall back to tokenBase
const effectiveTokenBase = tokenOverride ?? tokenBase;

// Determine effective chain ID - use home page filter network when no sourceToken provided
const getEffectiveChainId = (): CaipChainId | Hex => {
if (tokenBase) {
if (effectiveTokenBase) {
// If specific token provided, use its chainId
return tokenBase.chainId;
return effectiveTokenBase.chainId;
}

// No token provided - check home page filter network
Expand All @@ -82,7 +85,7 @@ export const useSwapBridgeNavigation = ({

let bridgeSourceNativeAsset;
try {
if (!tokenBase) {
if (!effectiveTokenBase) {
bridgeSourceNativeAsset = getNativeAssetForChainId(effectiveChainId);
}
} catch (error) {
Expand All @@ -104,7 +107,7 @@ export const useSwapBridgeNavigation = ({
: undefined;

const candidateSourceToken =
tokenBase ?? bridgeNativeSourceTokenFormatted;
effectiveTokenBase ?? bridgeNativeSourceTokenFormatted;
const isBridgeEnabledSource = getIsBridgeEnabledSource(effectiveChainId);
let sourceToken = isBridgeEnabledSource
? candidateSourceToken
Expand Down Expand Up @@ -167,9 +170,12 @@ export const useSwapBridgeNavigation = ({
);
const { networkModal } = useAddNetwork();

const goToSwaps = useCallback(() => {
goToNativeBridge(BridgeViewMode.Unified);
}, [goToNativeBridge]);
const goToSwaps = useCallback(
(tokenOverride?: BridgeToken) => {
goToNativeBridge(BridgeViewMode.Unified, tokenOverride);
},
[goToNativeBridge],
);

return {
goToSwaps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ jest.mock('../../../../hooks/useMetrics', () => {
};
});

const mockGetIsBridgeEnabledSource = jest.fn(() => true);
jest.mock('../../../../../core/redux/slices/bridge', () => ({
...jest.requireActual('../../../../../core/redux/slices/bridge'),
selectIsBridgeEnabledSourceFactory: jest.fn(() => () => true),
selectIsBridgeEnabledSourceFactory: jest.fn(
() => mockGetIsBridgeEnabledSource,
),
}));

const mockGoToPortfolioBridge = jest.fn();
Expand Down Expand Up @@ -140,6 +143,9 @@ describe('useSwapBridgeNavigation', () => {

// Reset selectChainId mock to default
(selectChainId as unknown as jest.Mock).mockReturnValue(mockChainId);

// Reset bridge enabled mock to default (enabled)
mockGetIsBridgeEnabledSource.mockReturnValue(true);
});

it('uses native token when no token is provided', () => {
Expand Down Expand Up @@ -202,6 +208,93 @@ describe('useSwapBridgeNavigation', () => {
});
});

it('uses tokenOverride when passed to goToSwaps', () => {
const configuredToken: BridgeToken = {
address: '0x0000000000000000000000000000000000000001',
symbol: 'TOKEN',
name: 'Test Token',
decimals: 18,
chainId: mockChainId,
};

const overrideToken: BridgeToken = {
address: '0x0000000000000000000000000000000000000002',
symbol: 'OVERRIDE',
name: 'Override Token',
decimals: 18,
chainId: '0x89' as Hex,
};

const { result } = renderHookWithProvider(
() =>
useSwapBridgeNavigation({
location: mockLocation,
sourcePage: mockSourcePage,
sourceToken: configuredToken,
}),
{ state: initialState },
);

result.current.goToSwaps(overrideToken);

expect(mockNavigate).toHaveBeenCalledWith('Bridge', {
screen: 'BridgeView',
params: {
sourceToken: overrideToken,
sourcePage: mockSourcePage,
bridgeViewMode: BridgeViewMode.Unified,
},
});
});

it('falls back to ETH on mainnet when bridge is not enabled for source chain', () => {
mockGetIsBridgeEnabledSource.mockReturnValue(false);

// Mock that getNativeAssetForChainId returns ETH for mainnet fallback
(getNativeAssetForChainId as jest.Mock).mockReturnValue({
address: '0x0000000000000000000000000000000000000000',
name: 'Ether',
symbol: 'ETH',
decimals: 18,
});

const unsupportedToken: BridgeToken = {
address: '0x0000000000000000000000000000000000000001',
symbol: 'UNSUPPORTED',
name: 'Unsupported Token',
decimals: 18,
chainId: '0x999' as Hex,
};

const { result } = renderHookWithProvider(
() =>
useSwapBridgeNavigation({
location: mockLocation,
sourcePage: mockSourcePage,
sourceToken: unsupportedToken,
}),
{ state: initialState },
);

result.current.goToSwaps();

expect(mockNavigate).toHaveBeenCalledWith('Bridge', {
screen: 'BridgeView',
params: {
sourceToken: {
address: '0x0000000000000000000000000000000000000000',
name: 'Ether',
symbol: 'ETH',
image: '',
decimals: 18,
chainId: '0x1',
},
sourcePage: mockSourcePage,
bridgeViewMode: BridgeViewMode.Unified,
},
});
});

it('navigates to Bridge when goToSwaps is called and bridge UI is enabled', () => {
const { result } = renderHookWithProvider(
() =>
Expand Down
Loading
Loading