Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add live staking data with geo-blocking support #11891

Merged
merged 54 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
2350b61
refactor: move StakingEarnings component to the stake directory
amitabh94 Oct 16, 2024
587143f
chore: update @metamask/stake-sdk to version 0.2.13
amitabh94 Oct 18, 2024
190aba9
feat: update stakeSdkProvider to use new stakingApiService
amitabh94 Oct 19, 2024
18f2edf
refactor: update useStakeContext to throw error when used outside Sta…
amitabh94 Oct 19, 2024
50cdd2a
feat: add new hooks for staking functionality
amitabh94 Oct 19, 2024
567c0ca
feat: update StakingBalance component to replace mock data
amitabh94 Oct 19, 2024
51bfd2e
feat: update StakingEarnings component to use new hooks and provider
amitabh94 Oct 19, 2024
5ba67f8
feat: remove unused useGetStakingEligibility hook and stakingApi slice
amitabh94 Oct 19, 2024
388b1f9
refactor: update StakeInputView to use new hooks and remove unused code
amitabh94 Oct 19, 2024
2282621
refactor: update import path for mock data in StakeConfirmationView
amitabh94 Oct 19, 2024
29f8259
refactor: update UnstakeInputView.test.tsx to use new hooks and mock …
amitabh94 Oct 19, 2024
e391fa6
feat: update StakeButton to use new hooks and provider
amitabh94 Oct 19, 2024
fca10d2
feat: update TokenListItem to use new hooks for staking
amitabh94 Oct 19, 2024
d950219
feat: update usePooledStakes hook to include additional status checks
amitabh94 Oct 21, 2024
1f87722
refactor: rename useIsStakingSupportedChain to useStakingChain
amitabh94 Oct 21, 2024
7817947
refactor: remove unused code in useStakingEligibility hook
amitabh94 Oct 21, 2024
839a29d
refactor: optimize useVaultData hook and add annual reward rate calcu…
amitabh94 Oct 21, 2024
e866d9c
refactor: optimize useBalance hook and add staked balance calculations
amitabh94 Oct 21, 2024
ddaa3f4
feat: create useStakingEarnings hook for calculating staking earnings
amitabh94 Oct 21, 2024
84962b5
refactor: optimize useStakingInput hook and improve annual rewards ca…
amitabh94 Oct 21, 2024
c92d1a5
feat: replace staking earnings mock data with api data
amitabh94 Oct 21, 2024
5b2e7fd
feat: add staking earnings mock data
amitabh94 Oct 21, 2024
daf3f2f
refactor: optimize EstimatedAnnualRewardsCard component and add loadi…
amitabh94 Oct 21, 2024
e45b3de
feat: replace staking mocked asset data with actual values
amitabh94 Oct 21, 2024
779cb1f
refactor: remove unused state and optimize TokenDetails component
amitabh94 Oct 21, 2024
6d1230b
refactor: optimize StakingButtons component and update props
amitabh94 Oct 21, 2024
11bc9d8
refactor: update reward rate display in RewardsCard component
amitabh94 Oct 21, 2024
4e9b2bd
feat: update UnstakeInputView component and use actual staked balance
amitabh94 Oct 21, 2024
7472c9e
refactor: update StakeInputView component and optimize estimated rewa…
amitabh94 Oct 21, 2024
e10a9bd
refactor: update TokenListItem component to use updated staking chain…
amitabh94 Oct 21, 2024
d0a8f5a
refactor: update StakeConfirmationView to use actual staked balance a…
amitabh94 Oct 21, 2024
3762dfe
refactor: move stake button component to stake directory
amitabh94 Oct 21, 2024
2e61d77
refactor: update import path for Theme type in StakingEarnings.styles…
amitabh94 Oct 21, 2024
a3751b5
refactor: mock network configuration in StakingBalance and StakingEar…
amitabh94 Oct 22, 2024
f6b70e2
feat: rename stake button label to use "Earn" instead of "Stake"
amitabh94 Oct 22, 2024
905d082
refactor: update FooterButtonGroup component in StakingConfirmation
amitabh94 Oct 22, 2024
2754839
refactor: mock usePooledStakes hook in stake tests
amitabh94 Oct 22, 2024
f80467e
refactor: mock network configuration in Tokens test
amitabh94 Oct 22, 2024
66b64ad
feat: update test snapshot to use "Earn" instead of "Stake"
amitabh94 Oct 22, 2024
f9101cf
refactor: update button label to use "Continue" instead of "Confirm"
amitabh94 Oct 22, 2024
a74cfa5
feat: update StakeButton to refresh pooled staking eligibility on but…
amitabh94 Oct 23, 2024
43edf41
chore: revert changes to yarn.lock
amitabh94 Oct 23, 2024
1670052
feat: add test for useBalance hook
amitabh94 Oct 23, 2024
00d74bd
feat: add useStakingChain hook test
amitabh94 Oct 23, 2024
0c0eaa9
feat: add usePooledStakes hook test
amitabh94 Oct 23, 2024
43e5ad1
feat: add useVaultData hook test and update annual reward rate calcul…
amitabh94 Oct 23, 2024
91b047d
feat: add useStakingEligibility hook test
amitabh94 Oct 23, 2024
8ee1967
refactor: update variable name for annual rewards in StakeInputView a…
amitabh94 Oct 23, 2024
ba8b80c
feat: add useStakingEarnings hook test
amitabh94 Oct 23, 2024
fbad41c
Merge branch 'main' into STAKE-806--fe-integrate-stake-and-vault-data
amitabh94 Oct 23, 2024
74be242
feat: redirect user back to input screen on cancel button press
amitabh94 Oct 23, 2024
f5596eb
Merge branch 'main' into STAKE-806--fe-integrate-stake-and-vault-data
amitabh94 Oct 23, 2024
8fa9ac9
Merge branch 'main' into STAKE-806--fe-integrate-stake-and-vault-data
amitabh94 Oct 23, 2024
ec92c8e
Merge branch 'main' into STAKE-806--fe-integrate-stake-and-vault-data
amitabh94 Oct 23, 2024
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
6 changes: 4 additions & 2 deletions app/components/UI/AssetOverview/Balance/Balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface BalanceProps {
secondaryBalance?: string;
}

const NetworkBadgeSource = (chainId: string, ticker: string) => {
export const NetworkBadgeSource = (chainId: string, ticker: string) => {
const isMainnet = isMainnetByChainId(chainId);
const isLineaMainnet = isLineaMainnetByChainId(chainId);

Expand Down Expand Up @@ -88,7 +88,9 @@ const Balance = ({ asset, mainBalance, secondaryBalance }: BalanceProps) => {
{asset.name || asset.symbol}
</Text>
</AssetElement>
{isPooledStakingFeatureEnabled() && asset?.isETH && <StakingBalance />}
{isPooledStakingFeatureEnabled() && asset?.isETH && (
<StakingBalance asset={asset} />
)}
</View>
);
};
Expand Down

This file was deleted.

11 changes: 3 additions & 8 deletions app/components/UI/AssetOverview/TokenDetails/TokenDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { zeroAddress } from 'ethereumjs-util';
import React, { useState } from 'react';
import React from 'react';
import { View } from 'react-native';
import { useSelector } from 'react-redux';
import i18n from '../../../../../locales/i18n';
Expand All @@ -21,8 +21,8 @@ import Logger from '../../../../util/Logger';
import TokenDetailsList from './TokenDetailsList';
import MarketDetailsList from './MarketDetailsList';
import { TokenI } from '../../Tokens/types';
import StakingEarnings from '../StakingEarnings';
import { isPooledStakingFeatureEnabled } from '../../Stake/constants';
import StakingEarnings from '../../Stake/components/StakingEarnings';

export interface TokenDetails {
contractAddress: string | null;
Expand Down Expand Up @@ -52,9 +52,6 @@ const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {
const currentCurrency = useSelector(selectCurrentCurrency);
const tokenContractAddress = safeToChecksumAddress(asset.address);

// TEMP: Remove once component has been implemented.
const [hasStakingPositions] = useState(true);

let tokenMetadata;
let marketData;

Expand Down Expand Up @@ -126,9 +123,7 @@ const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {

return (
<View style={styles.tokenDetailsContainer}>
{asset.isETH &&
hasStakingPositions &&
isPooledStakingFeatureEnabled() && <StakingEarnings />}
{asset.isETH && isPooledStakingFeatureEnabled() && <StakingEarnings />}
{(asset.isETH || tokenMetadata) && (
<TokenDetailsList tokenDetails={tokenDetails} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,25 @@ jest.mock('../../hooks/usePoolStakedDeposit', () => ({
}),
}));

jest.mock('../../hooks/usePooledStakes', () => ({
__esModule: true,
default: () => ({
refreshPooledStakes: jest.fn(),
}),
}));

describe('StakeConfirmationView', () => {
it('render matches snapshot', () => {
const props: StakeConfirmationViewProps = {
route: {
key: '1',
params: { amountWei: '3210000000000000', amountFiat: '7.46' },
params: {
amountWei: '3210000000000000',
amountFiat: '7.46',
annualRewardRate: '2.5%',
annualRewardsETH: '2.5 ETH',
annualRewardsFiat: '$5000',
},
name: 'params',
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,9 @@ import AccountHeaderCard from '../../components/StakingConfirmation/AccountHeade
import RewardsCard from '../../components/StakingConfirmation/RewardsCard/RewardsCard';
import ConfirmationFooter from '../../components/StakingConfirmation/ConfirmationFooter/ConfirmationFooter';
import { StakeConfirmationViewProps } from './StakeConfirmationView.types';
import { MOCK_GET_VAULT_RESPONSE } from '../../components/StakingBalance/mockData';
import { strings } from '../../../../../../locales/i18n';
import { FooterButtonGroupActions } from '../../components/StakingConfirmation/ConfirmationFooter/FooterButtonGroup/FooterButtonGroup.types';

const MOCK_REWARD_DATA = {
REWARDS: {
ETH: '0.13 ETH',
FIAT: '$334.93',
},
};

const MOCK_STAKING_CONTRACT_NAME = 'MM Pooled Staking';

const StakeConfirmationView = ({ route }: StakeConfirmationViewProps) => {
Expand Down Expand Up @@ -47,9 +39,9 @@ const StakeConfirmationView = ({ route }: StakeConfirmationViewProps) => {
<View style={styles.cardsContainer}>
<AccountHeaderCard contractName={MOCK_STAKING_CONTRACT_NAME} />
<RewardsCard
rewardRate={MOCK_GET_VAULT_RESPONSE.apy}
rewardsEth={MOCK_REWARD_DATA.REWARDS.ETH}
rewardsFiat={MOCK_REWARD_DATA.REWARDS.FIAT}
rewardRate={route.params.annualRewardRate}
rewardsEth={route.params.annualRewardsETH}
rewardsFiat={route.params.annualRewardsFiat}
/>
</View>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { RouteProp } from '@react-navigation/native';
interface StakeConfirmationViewRouteParams {
amountWei: string;
amountFiat: string;
annualRewardsETH: string;
annualRewardsFiat: string;
annualRewardRate: string;
}

export interface StakeConfirmationViewProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ exports[`StakeConfirmationView render matches snapshot 1`] = `
}
testID="label"
>
2.8%
2.5%
</Text>
</View>
</View>
Expand Down Expand Up @@ -1100,7 +1100,7 @@ exports[`StakeConfirmationView render matches snapshot 1`] = `
}
}
>
$334.93
$5000
</Text>
<Text
accessibilityRole="text"
Expand All @@ -1115,7 +1115,7 @@ exports[`StakeConfirmationView render matches snapshot 1`] = `
}
}
>
0.13 ETH
2.5 ETH
</Text>
</View>
</View>
Expand Down Expand Up @@ -1415,7 +1415,7 @@ exports[`StakeConfirmationView render matches snapshot 1`] = `
}
}
>
Confirm
Continue
</Text>
</TouchableOpacity>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BN } from 'ethereumjs-util';
import { Stake } from '../../sdk/stakeSdkProvider';
import { ChainId, PooledStakingContract } from '@metamask/stake-sdk';
import { Contract } from 'ethers';
import { MOCK_GET_VAULT_RESPONSE } from '../../__mocks__/mockData';

function render(Component: React.ComponentType) {
return renderScreen(
Expand Down Expand Up @@ -89,6 +90,31 @@ jest.mock('../../hooks/useBalance', () => ({
}),
}));

const mockVaultData = MOCK_GET_VAULT_RESPONSE;
// Mock hooks

jest.mock('../../hooks/useStakingEligibility', () => ({
__esModule: true,
default: () => ({
isEligible: true,
loading: false,
error: null,
refreshPooledStakingEligibility: jest.fn(),
}),
}));

jest.mock('../../hooks/useVaultData', () => ({
__esModule: true,
default: () => ({
vaultData: mockVaultData,
loading: false,
error: null,
refreshVaultData: jest.fn(),
annualRewardRate: '2.5%',
annualRewardRateDecimal: 0.025,
}),
}));

describe('StakeInputView', () => {
it('render matches snapshot', () => {
render(StakeInputView);
Expand Down Expand Up @@ -122,7 +148,7 @@ describe('StakeInputView', () => {

fireEvent.press(screen.getByText('2'));

expect(screen.getByText('0.052 ETH')).toBeTruthy();
expect(screen.getByText('0.05 ETH')).toBeTruthy();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const StakeInputView = () => {
handleKeypadChange,
calculateEstimatedAnnualRewards,
estimatedAnnualRewards,
annualRewardsETH,
annualRewardsFiat,
annualRewardRate,
isLoadingVaultData,
} = useStakingInputHandlers(balanceWei);

const navigateToLearnMoreModal = () => {
Expand All @@ -55,9 +59,19 @@ const StakeInputView = () => {
params: {
amountWei: amountWei.toString(),
amountFiat: fiatAmount,
annualRewardsETH,
annualRewardsFiat,
annualRewardRate,
},
});
}, [amountWei, fiatAmount, navigation]);
}, [
navigation,
amountWei,
fiatAmount,
annualRewardsETH,
annualRewardsFiat,
annualRewardRate,
]);

const balanceText = strings('stake.balance');

Expand Down Expand Up @@ -101,6 +115,7 @@ const StakeInputView = () => {
<EstimatedAnnualRewardsCard
estimatedAnnualRewards={estimatedAnnualRewards}
onIconPress={navigateToLearnMoreModal}
isLoading={isLoadingVaultData}
/>
</View>
<QuickAmounts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ exports[`StakeInputView render matches snapshot 1`] = `
}
}
>
2.6%
2.5%
</Text>
<Text
accessibilityRole="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import UnstakeInputView from './UnstakeInputView';
import { renderScreen } from '../../../../../util/test/renderWithProvider';
import Routes from '../../../../../constants/navigation/Routes';
import { backgroundState } from '../../../../../util/test/initial-root-state';
import {
MOCK_GET_POOLED_STAKES_API_RESPONSE,
MOCK_GET_VAULT_RESPONSE,
MOCK_STAKED_ETH_ASSET,
} from '../../__mocks__/mockData';

function render(Component: React.ComponentType) {
return renderScreen(
Expand Down Expand Up @@ -48,6 +53,38 @@ jest.mock('../../../../../selectors/currencyRateController.ts', () => ({
selectCurrentCurrency: jest.fn(() => 'USD'),
}));

const mockVaultData = MOCK_GET_VAULT_RESPONSE;
const mockPooledStakeData = MOCK_GET_POOLED_STAKES_API_RESPONSE.accounts[0];

jest.mock('../../hooks/useStakingEligibility', () => ({
__esModule: true,
default: () => ({
isEligible: true,
loading: false,
error: null,
refreshPooledStakingEligibility: jest.fn(),
}),
}));

jest.mock('../../hooks/useVaultData', () => ({
__esModule: true,
default: () => ({
vaultData: mockVaultData,
loading: false,
error: null,
annualRewardRate: '2.5%',
annualRewardRateDecimal: 0.025,
}),
}));

jest.mock('../../hooks/useBalance', () => ({
__esModule: true,
default: () => ({
stakedBalanceWei: mockPooledStakeData.assets,
stakedBalanceFiat: MOCK_STAKED_ETH_ASSET.balanceFiat,
}),
}));

describe('UnstakeInputView', () => {
it('render matches snapshot', () => {
render(UnstakeInputView);
Expand Down Expand Up @@ -81,7 +118,7 @@ describe('UnstakeInputView', () => {

fireEvent.press(screen.getByText('25%'));

expect(screen.getByText('1.14999')).toBeTruthy();
expect(screen.getByText('1.44783')).toBeTruthy();
});
});

Expand All @@ -96,13 +133,14 @@ describe('UnstakeInputView', () => {
render(UnstakeInputView);

fireEvent.press(screen.getByText('1'));

expect(screen.getByText('Review')).toBeTruthy();
});

it('displays `Not enough ETH` when input exceeds balance', () => {
render(UnstakeInputView);

fireEvent.press(screen.getByText('6'));
fireEvent.press(screen.getByText('8'));
expect(screen.queryAllByText('Not enough ETH')).toHaveLength(2);
});
});
Expand Down
Loading
Loading