Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d884bd3
feat: removed action route param to differentiate between pooled-stak…
Matt561 May 7, 2025
e4ed67c
feat: fixed incorrect title being displayed on Ddeposit and withdrawa…
Matt561 May 7, 2025
c712775
feat: added withdraw and deposit more buttons for supported stablecoins
Matt561 May 7, 2025
e5abb5f
feat: fixed breaking tests
Matt561 May 9, 2025
acc7565
feat: fixing lint error in EarnLendingBalance.styles.ts
Matt561 May 9, 2025
5480d5a
feat: added receipt token balance and abstracted staking and lending …
Matt561 May 20, 2025
26768cd
feat: added EarnLendingBalance tests
Matt561 May 20, 2025
32a7238
feat: added EarnBalance tests
Matt561 May 20, 2025
6e069ae
feat: replaced hardcoded lendingDeposit tx type with TransactionTypes…
Matt561 May 20, 2025
ccf17ff
feat: added useLendingPair tests
Matt561 May 20, 2025
25091bf
feat: added missing navigation tests for EarnLendingBalance buttons
Matt561 May 20, 2025
75edee7
feat: added tests for isSupportedLendingReceiptTokenByChainId helper
Matt561 May 21, 2025
842b41a
feat: fixed breaking Balance component tests
Matt561 May 21, 2025
33edfa8
feat: updated useEarnTokens test to include filter param and receipt …
Matt561 May 21, 2025
9857563
feat: added receipt tokens to getSupportedEarnTokens and filterEligib…
Matt561 May 21, 2025
894f6f6
feat: removed completed todo in earn token utils
Matt561 May 21, 2025
8476df7
Merge branch 'main' into feat/stake-1023-add-lending-withdraw-and-dep…
Matt561 May 27, 2025
9028dfb
Merge branch 'main' into feat/stake-1023-add-lending-withdraw-and-dep…
Matt561 May 27, 2025
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
4 changes: 2 additions & 2 deletions app/components/UI/AssetOverview/Balance/Balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import Text, {
} from '../../../../component-library/components/Texts/Text';
import { TokenI } from '../../Tokens/types';
import { useNavigation } from '@react-navigation/native';
import StakingBalance from '../../Stake/components/StakingBalance/StakingBalance';
import {
PopularList,
UnpopularNetworkList,
CustomNetworkImgMapping,
getNonEvmNetworkImageSourceByChainId,
} from '../../../../util/networks/customNetworks';
import { RootState } from '../../../../reducers';
import EarnBalance from '../../Earn/components/EarnBalance';

interface BalanceProps {
asset: TokenI;
Expand Down Expand Up @@ -152,7 +152,7 @@ const Balance = ({ asset, mainBalance, secondaryBalance }: BalanceProps) => {
{asset.name || asset.symbol}
</Text>
</AssetElement>
{asset?.isETH && <StakingBalance asset={asset} />}
<EarnBalance asset={asset} />
</View>
);
};
Expand Down
31 changes: 31 additions & 0 deletions app/components/UI/AssetOverview/Balance/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import configureMockStore from 'redux-mock-store';
import { backgroundState } from '../../../../util/test/initial-root-state';
import { NetworkBadgeSource } from './Balance';
import { MOCK_VAULT_APY_AVERAGES } from '../../Stake/components/PoolStakingLearnMoreModal/mockVaultRewards';
import { EARN_EXPERIENCES } from '../../Earn/constants/experiences';
import { MOCK_DAI_MAINNET_ASSET } from '../../Stake/__mocks__/stakeMockData';
import { createMockToken } from '../../Stake/testUtils';

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
Expand Down Expand Up @@ -112,9 +115,37 @@ jest.mock('../../Stake/hooks/useStakingEligibility', () => ({
__esModule: true,
default: () => ({
isEligible: true,
isLoadingEligibility: false,
}),
}));

const mockDaiMainnet = {
...MOCK_DAI_MAINNET_ASSET,
apr: '4.5',
balanceFiat: '$100',
balanceFormatted: '100 DAI',
balanceMinimalUnit: '100',
balanceFiatNumber: 100,
experience: EARN_EXPERIENCES.STABLECOIN_LENDING,
};

const mockADaiMainnet = {
...createMockToken({ chainId: '0x1', symbol: 'ADAI', name: 'Aave v3 DAI' }),
apr: '4.5',
balanceFiat: '$100',
balanceFormatted: '100 ADAI',
balanceMinimalUnit: '100',
balanceFiatNumber: 100,
experience: EARN_EXPERIENCES.STABLECOIN_LENDING,
};

const mockEarnTokens = [mockDaiMainnet, mockADaiMainnet];

jest.mock('../../Earn/hooks/useEarnTokens', () => ({
__esModule: true,
default: () => mockEarnTokens,
}));

const mockInitialState = {
engine: {
backgroundState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import { selectMultichainAssetsRates } from '../../../../selectors/multichain';
import { selectIsEvmNetworkSelected } from '../../../../selectors/multichainNetworkController';
import { selectStablecoinLendingEnabledFlag } from '../../Earn/selectors/featureFlags';

jest.mock('../../Earn/constants/tempLendingConstants', () => ({
USER_HAS_LENDING_POSITIONS: false,
}));

jest.mock('../../../../core/Engine', () => ({
getTotalEvmFiatAccountBalance: jest.fn(),
context: {
Expand Down
14 changes: 12 additions & 2 deletions app/components/UI/AssetOverview/TokenDetails/TokenDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import {
isAssetFromSearch,
selectTokenDisplayData,
} from '../../../../selectors/tokenSearchDiscoveryDataController';
import { isSupportedLendingTokenByChainId } from '../../Earn/utils/token';
import {
isSupportedLendingReceiptTokenByChainId,
isSupportedLendingTokenByChainId,
} from '../../Earn/utils/token';
import EarnEmptyStateCta from '../../Earn/components/EmptyStateCta';
import { parseFloatSafe } from '../../Earn/utils';
import { selectStablecoinLendingEnabledFlag } from '../../Earn/selectors/featureFlags';
Expand All @@ -38,6 +41,7 @@ import { MarketDataDetails } from '@metamask/assets-controllers';
import { formatMarketDetails } from '../utils/marketDetails';
import { getTokenDetails } from '../utils/getTokenDetails';
import { formatChainIdToCaip } from '@metamask/bridge-controller';
import { USER_HAS_LENDING_POSITIONS } from '../../Earn/constants/tempLendingConstants';

export interface TokenDetails {
contractAddress: string | null;
Expand Down Expand Up @@ -189,10 +193,16 @@ const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {

return (
<View style={styles.tokenDetailsContainer}>
{/* TODO: Abstract StakingEarnings and EarnEmptyStateCta (Earn Lending Experience) into single entrypoint */}
{asset.isETH && <StakingEarnings asset={asset} />}
{isStablecoinLendingEnabled &&
isSupportedLendingTokenByChainId(asset.symbol, asset.chainId ?? '') &&
hasAssetBalance && <EarnEmptyStateCta token={asset} />}
!isSupportedLendingReceiptTokenByChainId(
asset.symbol,
asset.chainId ?? '',
) &&
hasAssetBalance &&
!USER_HAS_LENDING_POSITIONS && <EarnEmptyStateCta token={asset} />}
{(asset.isETH || tokenMetadata || isNonEvmAsset) && (
<TokenDetailsList tokenDetails={tokenDetails} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ import * as useBalance from '../../../Stake/hooks/useBalance';
import usePoolStakedDeposit from '../../../Stake/hooks/usePoolStakedDeposit';
// eslint-disable-next-line import/no-namespace
import * as useStakingGasFee from '../../../Stake/hooks/useStakingGasFee';
import {
EARN_INPUT_VIEW_ACTIONS,
EarnInputViewProps,
} from './EarnInputView.types';
import { EarnInputViewProps } from './EarnInputView.types';
import { Stake } from '../../../Stake/sdk/stakeSdkProvider';
import {
createMockToken,
Expand Down Expand Up @@ -263,7 +260,6 @@ describe('EarnInputView', () => {
const baseProps: EarnInputViewProps = {
route: {
params: {
action: EARN_INPUT_VIEW_ACTIONS.STAKE,
token: MOCK_ETH_MAINNET_ASSET,
},
key: Routes.STAKING.STAKE,
Expand Down Expand Up @@ -328,7 +324,6 @@ describe('EarnInputView', () => {
const { getByText, getAllByText } = render(EarnInputView, {
params: {
...baseProps.route.params,
action: EARN_INPUT_VIEW_ACTIONS.LEND,
token: MOCK_USDC_MAINNET_ASSET,
},
key: Routes.STAKING.STAKE,
Expand Down Expand Up @@ -573,7 +568,6 @@ describe('EarnInputView', () => {

const routeParamsWithUSDC: EarnInputViewProps['route'] = {
params: {
action: EARN_INPUT_VIEW_ACTIONS.STAKE,
token: MOCK_USDC_MAINNET_ASSET,
},
key: Routes.STAKING.STAKE,
Expand Down Expand Up @@ -626,4 +620,39 @@ describe('EarnInputView', () => {
});
});
});

describe('title bar', () => {
it('displays "stake" title when asset is not support by lending', () => {
render(EarnInputView);

expect(mockGetStakingNavbar).toHaveBeenCalledWith(
'Stake',
expect.anything(),
expect.anything(),
expect.anything(),
expect.anything(),
);
});

it('displays "deposit" when asset is supported lending token', () => {
selectStablecoinLendingEnabledFlagMock.mockReturnValue(true);

render(EarnInputView, {
params: {
...baseProps.route.params,
token: MOCK_USDC_MAINNET_ASSET,
},
key: Routes.STAKING.STAKE,
name: 'params',
});

expect(mockGetStakingNavbar).toHaveBeenCalledWith(
'Deposit',
expect.anything(),
expect.anything(),
expect.anything(),
expect.anything(),
);
});
});
});
23 changes: 15 additions & 8 deletions app/components/UI/Earn/Views/EarnInputView/EarnInputView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
useRoute,
} from '@react-navigation/native';
import { formatEther } from 'ethers/lib/utils';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { View } from 'react-native';
import { useSelector } from 'react-redux';
import { strings } from '../../../../../../locales/i18n';
Expand Down Expand Up @@ -41,7 +41,6 @@ import {
EARN_INPUT_VIEW_ACTIONS,
EarnInputViewProps,
} from './EarnInputView.types';
import { getEarnInputViewTitle } from './utils';
import { useEarnTokenDetails } from '../../hooks/useEarnTokenDetails';
import useEarnInputHandlers from '../../hooks/useEarnInput';
import { selectStablecoinLendingEnabledFlag } from '../../selectors/featureFlags';
Expand All @@ -51,12 +50,13 @@ import {
getErc20SpendingLimit,
} from '../../utils/tempLending';
import BigNumber from 'bignumber.js';
import { isSupportedLendingTokenByChainId } from '../../utils';

const EarnInputView = () => {
// navigation hooks
const navigation = useNavigation();
const route = useRoute<EarnInputViewProps['route']>();
const { action, token } = route.params;
const { token } = route.params;

// state
const [
Expand Down Expand Up @@ -131,7 +131,7 @@ const EarnInputView = () => {
const handleLendingFlow = useCallback(async () => {
if (!activeAccount?.address) return;

// TODO: Add GasCostImpact for lending deposit flow.
// TODO: Add GasCostImpact for lending deposit flow after launch.
const amountTokenMinimalUnitString = amountTokenMinimalUnit.toString();

const tokenContractAddress = earnToken?.address;
Expand Down Expand Up @@ -238,6 +238,7 @@ const EarnInputView = () => {
undefined,
true,
);

navigation.navigate('StakeScreens', {
screen: Routes.STANDALONE_CONFIRMATIONS.STAKE_DEPOSIT,
});
Expand Down Expand Up @@ -390,9 +391,16 @@ const EarnInputView = () => {
const navBarEventOptions = isStablecoinLendingEnabled
? earnNavBarEventOptions
: stakingNavBarEventOptions;
const title = isStablecoinLendingEnabled
? getEarnInputViewTitle(action)
: strings('stake.stake_eth');
const title = useMemo(() => {
if (
isStablecoinLendingEnabled &&
token?.chainId &&
isSupportedLendingTokenByChainId(token.symbol, token.chainId)
) {
return strings('earn.deposit');
}
return strings('stake.stake');
}, [isStablecoinLendingEnabled, token.chainId, token.symbol]);

useEffect(() => {
navigation.setOptions(
Expand All @@ -406,7 +414,6 @@ const EarnInputView = () => {
);
}, [
navigation,
action,
token,
theme.colors,
navBarEventOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import { RouteProp } from '@react-navigation/native';
import { TokenI } from '../../../Tokens/types';
import { strings } from '../../../../../../locales/i18n';

export enum EARN_INPUT_VIEW_ACTIONS {
STAKE = 'STAKE',
LEND = 'LEND',
ALLOWANCE_INCREASE = 'ALLOWANCE_INCREASE',
}

export const EARN_INPUT_ACTION_TO_LABEL_MAP = {
[EARN_INPUT_VIEW_ACTIONS.STAKE]: strings('stake.stake'),
[EARN_INPUT_VIEW_ACTIONS.LEND]: strings('stake.deposit'),
[EARN_INPUT_VIEW_ACTIONS.ALLOWANCE_INCREASE]: strings('stake.deposit'),
};

interface EarnInputViewRouteParams {
token: TokenI;
action: EARN_INPUT_VIEW_ACTIONS;
}

export interface EarnInputViewProps {
Expand Down
25 changes: 0 additions & 25 deletions app/components/UI/Earn/Views/EarnInputView/utils.test.ts

This file was deleted.

10 changes: 0 additions & 10 deletions app/components/UI/Earn/Views/EarnInputView/utils.ts

This file was deleted.

Loading
Loading