Skip to content

Commit d3e1925

Browse files
committed
Merge branch 'improv/account_syncing_various_updates' of github.com:MetaMask/metamask-extension into improv/account_syncing_various_updates
2 parents 570ee0a + 0ed48a0 commit d3e1925

File tree

19 files changed

+646
-83
lines changed

19 files changed

+646
-83
lines changed

app/_locales/en/messages.json

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import assert from 'assert';
2+
import { Mockttp, MockedEndpoint } from 'mockttp';
3+
import { withFixtures, regularDelayMs } from '../../helpers';
4+
import FixtureBuilder from '../../fixture-builder';
5+
import HomePage from '../../page-objects/pages/homepage';
6+
import OnboardingCompletePage from '../../page-objects/pages/onboarding/onboarding-complete-page';
7+
import {
8+
importSRPOnboardingFlow,
9+
createNewWalletOnboardingFlow,
10+
} from '../../page-objects/flows/onboarding.flow';
11+
12+
// Mock function implementation for Token Price requests
13+
async function mockTokenPriceApi(
14+
mockServer: Mockttp,
15+
): Promise<MockedEndpoint[]> {
16+
return [
17+
// mainnet
18+
await mockServer
19+
.forGet('https://price.api.cx.metamask.io/v2/chains/1/spot-prices')
20+
.thenCallback(() => ({
21+
statusCode: 200,
22+
json: {},
23+
})),
24+
// linea
25+
await mockServer
26+
.forGet('https://price.api.cx.metamask.io/v2/chains/59144/spot-prices')
27+
.thenCallback(() => ({
28+
statusCode: 200,
29+
json: {},
30+
})),
31+
];
32+
}
33+
34+
describe('MetaMask onboarding @no-mmi', function () {
35+
it("doesn't make any token price API requests before create new wallet onboarding is completed", async function () {
36+
await withFixtures(
37+
{
38+
fixtures: new FixtureBuilder({ onboarding: true })
39+
.withNetworkControllerOnMainnet()
40+
.build(),
41+
title: this.test?.fullTitle(),
42+
testSpecificMock: mockTokenPriceApi,
43+
},
44+
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
45+
await createNewWalletOnboardingFlow({ driver });
46+
47+
// Check no requests are made before completing creat new wallet onboarding
48+
// Intended delay to ensure we cover at least 1 polling loop of time for the network request
49+
await driver.delay(regularDelayMs);
50+
for (const mockedEndpoint of mockedEndpoints) {
51+
const isPending = await mockedEndpoint.isPending();
52+
assert.equal(
53+
isPending,
54+
true,
55+
`${mockedEndpoint} mock should still be pending before onboarding`,
56+
);
57+
const requests = await mockedEndpoint.getSeenRequests();
58+
assert.equal(
59+
requests.length,
60+
0,
61+
`${mockedEndpoint} should make no requests before onboarding`,
62+
);
63+
}
64+
65+
// complete create new wallet onboarding
66+
const onboardingCompletePage = new OnboardingCompletePage(driver);
67+
await onboardingCompletePage.check_pageIsLoaded();
68+
await onboardingCompletePage.completeOnboarding();
69+
const homePage = new HomePage(driver);
70+
await homePage.check_pageIsLoaded();
71+
72+
// network requests happen here
73+
for (const mockedEndpoint of mockedEndpoints) {
74+
await driver.wait(async () => {
75+
const isPending = await mockedEndpoint.isPending();
76+
return isPending === false;
77+
}, driver.timeout);
78+
79+
const requests = await mockedEndpoint.getSeenRequests();
80+
assert.equal(
81+
requests.length > 0,
82+
true,
83+
`${mockedEndpoint} should make requests after onboarding`,
84+
);
85+
}
86+
},
87+
);
88+
});
89+
90+
it("doesn't make any token price API requests before onboarding by import is completed", async function () {
91+
await withFixtures(
92+
{
93+
fixtures: new FixtureBuilder({ onboarding: true })
94+
.withNetworkControllerOnMainnet()
95+
.build(),
96+
title: this.test?.fullTitle(),
97+
testSpecificMock: mockTokenPriceApi,
98+
},
99+
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
100+
await importSRPOnboardingFlow({ driver });
101+
102+
// Check no requests before completing onboarding
103+
// Intended delay to ensure we cover at least 1 polling loop of time for the network request
104+
await driver.delay(regularDelayMs);
105+
for (const mockedEndpoint of mockedEndpoints) {
106+
const requests = await mockedEndpoint.getSeenRequests();
107+
assert.equal(
108+
requests.length,
109+
0,
110+
`${mockedEndpoint} should make no requests before import wallet onboarding complete`,
111+
);
112+
}
113+
114+
// complete import wallet onboarding
115+
const onboardingCompletePage = new OnboardingCompletePage(driver);
116+
await onboardingCompletePage.check_pageIsLoaded();
117+
await onboardingCompletePage.completeOnboarding();
118+
const homePage = new HomePage(driver);
119+
await homePage.check_pageIsLoaded();
120+
121+
// requests happen here
122+
for (const mockedEndpoint of mockedEndpoints) {
123+
await driver.wait(async () => {
124+
const isPending = await mockedEndpoint.isPending();
125+
return isPending === false;
126+
}, driver.timeout);
127+
128+
const requests = await mockedEndpoint.getSeenRequests();
129+
assert.equal(
130+
requests.length > 0,
131+
true,
132+
`${mockedEndpoint} should make requests after onboarding`,
133+
);
134+
}
135+
},
136+
);
137+
});
138+
});

ui/ducks/metamask/metamask.js

+9
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,15 @@ export const getNfts = (state) => {
338338
return allNfts?.[selectedAddress]?.[chainId] ?? [];
339339
};
340340

341+
export const getNFTsByChainId = (state, chainId) => {
342+
const {
343+
metamask: { allNfts },
344+
} = state;
345+
const { address: selectedAddress } = getSelectedInternalAccount(state);
346+
347+
return allNfts?.[selectedAddress]?.[chainId] ?? [];
348+
};
349+
341350
export const getNftContracts = (state) => {
342351
const {
343352
metamask: { allNftContracts },

ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx

+11-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
TextAlign,
1515
} from '../../../../../../../helpers/constants/design-system';
1616
import { useI18nContext } from '../../../../../../../hooks/useI18nContext';
17-
import { SPENDING_CAP_UNLIMITED_MSG } from '../../../../../constants';
1817
import { useConfirmContext } from '../../../../../context/confirm';
1918
import { useAssetDetails } from '../../../../../hooks/useAssetDetails';
2019
import StaticSimulation from '../../shared/static-simulation/static-simulation';
@@ -37,8 +36,13 @@ export const ApproveStaticSimulation = () => {
3736

3837
const decimals = initialDecimals || '0';
3938

40-
const { spendingCap, formattedSpendingCap, value, pending } =
41-
useApproveTokenSimulation(transactionMeta, decimals);
39+
const {
40+
spendingCap,
41+
isUnlimitedSpendingCap,
42+
formattedSpendingCap,
43+
value,
44+
pending,
45+
} = useApproveTokenSimulation(transactionMeta, decimals);
4246

4347
const { isNFT } = useIsNFT(transactionMeta);
4448

@@ -61,9 +65,7 @@ export const ApproveStaticSimulation = () => {
6165
textAlign={TextAlign.Center}
6266
alignItems={AlignItems.center}
6367
>
64-
{spendingCap === SPENDING_CAP_UNLIMITED_MSG
65-
? t('unlimited')
66-
: spendingCap}
68+
{isUnlimitedSpendingCap ? t('unlimited') : formattedSpendingCap}
6769
</Text>
6870
);
6971

@@ -78,10 +80,9 @@ export const ApproveStaticSimulation = () => {
7880
marginInlineEnd={1}
7981
minWidth={BlockSize.Zero}
8082
>
81-
{spendingCap === SPENDING_CAP_UNLIMITED_MSG ? (
82-
<Tooltip title={formattedSpendingCap}>
83-
{formattedTokenText}
84-
</Tooltip>
83+
{Boolean(isUnlimitedSpendingCap) ||
84+
spendingCap !== formattedSpendingCap ? (
85+
<Tooltip title={spendingCap}>{formattedTokenText}</Tooltip>
8586
) : (
8687
formattedTokenText
8788
)}

ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx

+10-13
Original file line numberDiff line numberDiff line change
@@ -62,41 +62,38 @@ export const EditSpendingCapModal = ({
6262
Number(decimals ?? '0'),
6363
).toFixed();
6464

65-
const { formattedSpendingCap } = useApproveTokenSimulation(
65+
const { formattedSpendingCap, spendingCap } = useApproveTokenSimulation(
6666
transactionMeta,
6767
decimals || '0',
6868
);
6969

7070
const [customSpendingCapInputValue, setCustomSpendingCapInputValue] =
71-
useState(formattedSpendingCap.toString());
71+
useState(spendingCap);
7272

7373
useEffect(() => {
74-
if (formattedSpendingCap) {
75-
setCustomSpendingCapInputValue(formattedSpendingCap.toString());
74+
if (spendingCap) {
75+
setCustomSpendingCapInputValue(spendingCap);
7676
}
77-
}, [formattedSpendingCap]);
77+
}, [spendingCap]);
7878

7979
const handleCancel = useCallback(() => {
8080
setIsOpenEditSpendingCapModal(false);
81-
setCustomSpendingCapInputValue(formattedSpendingCap.toString());
81+
setCustomSpendingCapInputValue(spendingCap);
8282
}, [
8383
setIsOpenEditSpendingCapModal,
8484
setCustomSpendingCapInputValue,
85-
formattedSpendingCap,
85+
spendingCap,
8686
]);
8787

8888
const [isModalSaving, setIsModalSaving] = useState(false);
8989

9090
const handleSubmit = useCallback(async () => {
9191
setIsModalSaving(true);
92-
const parsedValue = parseInt(String(customSpendingCapInputValue), 10);
9392

9493
const customTxParamsData = getCustomTxParamsData(
9594
transactionMeta?.txParams?.data,
9695
{
97-
customPermissionAmount:
98-
// coerce negative numbers to zero
99-
parsedValue < 0 ? '0' : customSpendingCapInputValue || '0',
96+
customPermissionAmount: customSpendingCapInputValue || '0',
10097
decimals: decimals || '0',
10198
},
10299
);
@@ -117,8 +114,8 @@ export const EditSpendingCapModal = ({
117114

118115
setIsModalSaving(false);
119116
setIsOpenEditSpendingCapModal(false);
120-
setCustomSpendingCapInputValue(formattedSpendingCap.toString());
121-
}, [customSpendingCapInputValue, formattedSpendingCap]);
117+
setCustomSpendingCapInputValue(spendingCap);
118+
}, [customSpendingCapInputValue, spendingCap]);
122119

123120
const showDecimalError =
124121
decimals &&

ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.test.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ describe('useApproveTokenSimulation', () => {
6565

6666
expect(result.current).toMatchInlineSnapshot(`
6767
{
68-
"formattedSpendingCap": "7",
68+
"formattedSpendingCap": "#7",
69+
"isUnlimitedSpendingCap": false,
6970
"pending": undefined,
7071
"spendingCap": "#7",
7172
"value": {
@@ -132,8 +133,9 @@ describe('useApproveTokenSimulation', () => {
132133
expect(result.current).toMatchInlineSnapshot(`
133134
{
134135
"formattedSpendingCap": "1,000,000,000,000,000",
136+
"isUnlimitedSpendingCap": true,
135137
"pending": undefined,
136-
"spendingCap": "UNLIMITED MESSAGE",
138+
"spendingCap": "1000000000000000",
137139
"value": {
138140
"data": [
139141
{
@@ -197,7 +199,8 @@ describe('useApproveTokenSimulation', () => {
197199

198200
expect(result.current).toMatchInlineSnapshot(`
199201
{
200-
"formattedSpendingCap": "0.0000000000001",
202+
"formattedSpendingCap": "<0.000001",
203+
"isUnlimitedSpendingCap": false,
201204
"pending": undefined,
202205
"spendingCap": "0.0000000000001",
203206
"value": {

ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { TransactionMeta } from '@metamask/transaction-controller';
22
import { isHexString } from '@metamask/utils';
3+
import { BigNumber } from 'bignumber.js';
34
import { isBoolean } from 'lodash';
45
import { useMemo } from 'react';
56
import { useSelector } from 'react-redux';
67
import { calcTokenAmount } from '../../../../../../../../shared/lib/transactions-controller-utils';
78
import { getIntlLocale } from '../../../../../../../ducks/locale/locale';
8-
import { SPENDING_CAP_UNLIMITED_MSG } from '../../../../../constants';
9+
import { formatAmount } from '../../../../simulation-details/formatAmount';
910
import { useDecodedTransactionData } from '../../hooks/useDecodedTransactionData';
1011
import { useIsNFT } from './use-is-nft';
1112

@@ -46,22 +47,26 @@ export const useApproveTokenSimulation = (
4647
).toFixed();
4748
}, [value, decimals]);
4849

50+
const tokenPrefix = isNFT ? '#' : '';
51+
4952
const formattedSpendingCap = useMemo(() => {
50-
// formatting coerces small numbers to 0
51-
return isNFT || parseInt(decodedSpendingCap, 10) < 1
52-
? decodedSpendingCap
53-
: new Intl.NumberFormat(locale).format(parseInt(decodedSpendingCap, 10));
53+
return isNFT
54+
? `${tokenPrefix}${decodedSpendingCap}`
55+
: formatAmount(locale, new BigNumber(decodedSpendingCap));
5456
}, [decodedSpendingCap, isNFT, locale]);
5557

56-
const spendingCap = useMemo(() => {
58+
const { spendingCap, isUnlimitedSpendingCap } = useMemo(() => {
5759
if (!isNFT && isSpendingCapUnlimited(parseInt(decodedSpendingCap, 10))) {
58-
return SPENDING_CAP_UNLIMITED_MSG;
60+
return { spendingCap: decodedSpendingCap, isUnlimitedSpendingCap: true };
5961
}
60-
const tokenPrefix = isNFT ? '#' : '';
61-
return `${tokenPrefix}${formattedSpendingCap}`;
62+
return {
63+
spendingCap: `${tokenPrefix}${decodedSpendingCap}`,
64+
isUnlimitedSpendingCap: false,
65+
};
6266
}, [decodedSpendingCap, formattedSpendingCap, isNFT]);
6367

6468
return {
69+
isUnlimitedSpendingCap,
6570
spendingCap,
6671
formattedSpendingCap,
6772
value,

0 commit comments

Comments
 (0)