Skip to content

Commit ef0deaf

Browse files
sahar-fehrimontelaidevdanjm
committed
Cherry-pick 167036f (#22856) into v11.10.0
fix: fix selectedAddress and nftController instantiation (#22856) Noticed that when you freshly import the extension, usse testDapp to deploy and mint an NFT, then manually import the NFT. you will be able to see the NFT. Then when you switch to another account and go back to the account that has the NFT; 1- you wont be able to see the NFTs you imported earlier 2- When you try to import them again you will get an error (fired from core when trying to verify ownership) Noticed that this was due to core having the wrong userAddress to check ownership for, and then in metamask.js, the selectedAddress retrieved does not match the user's selected Address This behavior is also reported to be on v11.10.0, on this issue: #22796 Fixes: #22796 Fixes: #22798 1. Remove and reimport the extension 2. Use testDapp to deploy and mint the NFT 3. Add the NFT manually by clicking "import NFT" 4. You should be able to see your newly import NFT 5. Create new account 6. Go back to the account that has the NFT 7. You should be able to see the NFT you imported <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> https://github.com/MetaMask/metamask-extension/assets/10994169/a74cbf0b-014b-4e39-82ac-e8452941fc6e https://github.com/MetaMask/metamask-extension/assets/10994169/987dbb27-b938-4bce-9b43-4269d714ee33 - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've clearly explained what problem this PR is solving and how it is solved. - [ ] I've linked related issues - [ ] I've included manual testing steps - [ ] I've included screenshots/recordings if applicable - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. - [ ] I’ve properly set the pull request status: - [ ] In case it's not yet "ready for review", I've set it to "draft". - [ ] In case it's "ready for review", I've changed it from "draft" to "non-draft". - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Monte Lai <monte.lai@consensys.net> Co-authored-by: Dan J Miller <danjm.com@gmail.com>
1 parent c2d8a9b commit ef0deaf

File tree

5 files changed

+114
-6
lines changed

5 files changed

+114
-6
lines changed

app/scripts/metamask-controller.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,10 +2827,10 @@ export default class MetamaskController extends EventEmitter {
28272827
),
28282828
// PreferencesController
28292829
setSelectedAddress: (address) => {
2830-
this.preferencesController.setSelectedAddress(address);
28312830
const account = this.accountsController.getAccountByAddress(address);
28322831
if (account) {
28332832
this.accountsController.setSelectedAccount(account.id);
2833+
this.preferencesController.setSelectedAddress(address);
28342834
} else {
28352835
throw new Error(`No account found for address: ${address}`);
28362836
}
@@ -2875,8 +2875,13 @@ export default class MetamaskController extends EventEmitter {
28752875
///: END:ONLY_INCLUDE_IF
28762876

28772877
// AccountsController
2878-
setSelectedInternalAccount:
2879-
accountsController.setSelectedAccount.bind(accountsController),
2878+
setSelectedInternalAccount: (id) => {
2879+
const account = this.accountsController.getAccount(id);
2880+
if (account) {
2881+
this.preferencesController.setSelectedAddress(account.address);
2882+
this.accountsController.setSelectedAccount(id);
2883+
}
2884+
},
28802885

28812886
setAccountName:
28822887
accountsController.setAccountName.bind(accountsController),

test/e2e/tests/nft/import-nft.spec.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const {
33
defaultGanacheOptions,
44
withFixtures,
55
unlockWallet,
6+
findAnotherAccountFromAccountList,
7+
waitForAccountRendered,
68
} = require('../../helpers');
79
const { SMART_CONTRACTS } = require('../../seeder/smart-contracts');
810
const FixtureBuilder = require('../../fixture-builder');
@@ -57,6 +59,96 @@ describe('Import NFT', function () {
5759
);
5860
});
5961

62+
it('should continue to display an imported NFT after importing, adding a new account, and switching back', async function () {
63+
await withFixtures(
64+
{
65+
dapp: true,
66+
fixtures: new FixtureBuilder()
67+
.withPermissionControllerConnectedToTestDapp()
68+
.build(),
69+
ganacheOptions: defaultGanacheOptions,
70+
smartContract,
71+
title: this.test.fullTitle(),
72+
},
73+
async ({ driver, _, contractRegistry }) => {
74+
const contractAddress =
75+
contractRegistry.getContractAddress(smartContract);
76+
await unlockWallet(driver);
77+
78+
// After login, go to NFTs tab, open the import NFT form
79+
await driver.clickElement('[data-testid="home__nfts-tab"]');
80+
await driver.clickElement({ text: 'Import NFT', tag: 'button' });
81+
82+
// Enter a valid NFT that belongs to user and check success message appears
83+
await driver.fill('#address', contractAddress);
84+
await driver.fill('#token-id', '1');
85+
await driver.clickElement(
86+
'[data-testid="import-nfts-modal-import-button"]',
87+
);
88+
89+
const newNftNotification = await driver.findElement({
90+
text: 'NFT was successfully added!',
91+
tag: 'h6',
92+
});
93+
assert.equal(await newNftNotification.isDisplayed(), true);
94+
95+
// Check the imported NFT and its image are displayed in the NFT tab
96+
const importedNft = await driver.waitForSelector({
97+
css: 'h5',
98+
text: 'TestDappNFTs',
99+
});
100+
const importedNftImage = await driver.findElement(
101+
'.nft-item__container',
102+
);
103+
assert.equal(await importedNft.isDisplayed(), true);
104+
assert.equal(await importedNftImage.isDisplayed(), true);
105+
106+
await driver.clickElement({ text: 'Tokens', tag: 'button' });
107+
await driver.clickElement('[data-testid="account-menu-icon"]');
108+
await driver.clickElement(
109+
'[data-testid="multichain-account-menu-popover-action-button"]',
110+
);
111+
await driver.clickElement({ text: 'Add a new account', tag: 'button' });
112+
113+
// set account name
114+
await driver.fill('[placeholder="Account 2"]', '2nd account');
115+
await driver.delay(400);
116+
await driver.clickElement({ text: 'Create', tag: 'button' });
117+
118+
await driver.isElementPresent({
119+
tag: 'span',
120+
text: '2nd account',
121+
});
122+
123+
const accountOneSelector = await findAnotherAccountFromAccountList(
124+
driver,
125+
1,
126+
'Account 1',
127+
);
128+
await waitForAccountRendered(driver);
129+
await driver.clickElement(accountOneSelector);
130+
131+
await driver.clickElement({ text: 'NFTs', tag: 'button' });
132+
const nftIsStillDisplayed = await driver.isElementPresentAndVisible({
133+
css: 'h5',
134+
text: 'TestDappNFTs',
135+
});
136+
const nftImageIsStillDisplayed =
137+
await driver.isElementPresentAndVisible('.nft-item__container');
138+
assert.equal(
139+
nftIsStillDisplayed,
140+
true,
141+
'Nft is no longer displayed after adding an account and switching back to account 1',
142+
);
143+
assert.equal(
144+
nftImageIsStillDisplayed,
145+
true,
146+
'Nft image is no longer displayed after adding an account and switching back to account 1',
147+
);
148+
},
149+
);
150+
});
151+
60152
it('should not be able to import an NFT that does not belong to user', async function () {
61153
await withFixtures(
62154
{

ui/ducks/metamask/metamask.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
checkNetworkAndAccountSupports1559,
1515
getAddressBook,
1616
getSelectedNetworkClientId,
17+
getSelectedInternalAccount,
1718
} from '../../selectors';
1819
import * as actionConstants from '../../store/actionConstants';
1920
import { updateTransactionGasFees } from '../../store/actions';
@@ -271,19 +272,21 @@ export function getNftsDropdownState(state) {
271272

272273
export const getNfts = (state) => {
273274
const {
274-
metamask: { allNfts, selectedAddress },
275+
metamask: { allNfts },
275276
} = state;
277+
const { address: selectedAddress } = getSelectedInternalAccount(state);
278+
276279
const { chainId } = getProviderConfig(state);
277280

278281
return allNfts?.[selectedAddress]?.[chainId] ?? [];
279282
};
280283

281284
export const getNftContracts = (state) => {
282285
const {
283-
metamask: { allNftContracts, selectedAddress },
286+
metamask: { allNftContracts },
284287
} = state;
288+
const { address: selectedAddress } = getSelectedInternalAccount(state);
285289
const { chainId } = getProviderConfig(state);
286-
287290
return allNftContracts?.[selectedAddress]?.[chainId] ?? [];
288291
};
289292

ui/store/actions.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,15 +889,20 @@ describe('Actions', () => {
889889
const setSelectedInternalAccountSpy = sinon
890890
.stub()
891891
.callsFake((_, cb) => cb());
892+
const setSelectedAddressSpy = sinon.stub().callsFake((_, cb) => cb());
892893

893894
background.getApi.returns({
894895
setSelectedInternalAccount: setSelectedInternalAccountSpy,
896+
setSelectedAddress: setSelectedAddressSpy,
895897
});
896898

897899
setBackgroundConnection(background.getApi());
898900

899901
await store.dispatch(actions.setSelectedAccount('0x123'));
900902
expect(setSelectedInternalAccountSpy.callCount).toStrictEqual(1);
903+
expect(setSelectedInternalAccountSpy.calledWith('mock-id')).toBe(true);
904+
expect(setSelectedAddressSpy.callCount).toStrictEqual(1);
905+
expect(setSelectedAddressSpy.calledWith('0x123')).toBe(true);
901906
});
902907

903908
it('displays warning if setSelectedAccount throws', async () => {
@@ -936,9 +941,11 @@ describe('Actions', () => {
936941
const setSelectedInternalAccountSpy = sinon
937942
.stub()
938943
.callsFake((_, cb) => cb(new Error('error')));
944+
const setSelectedAddressSpy = sinon.stub().callsFake((_, cb) => cb());
939945

940946
background.getApi.returns({
941947
setSelectedInternalAccount: setSelectedInternalAccountSpy,
948+
setSelectedAddress: setSelectedAddressSpy,
942949
});
943950

944951
setBackgroundConnection(background.getApi());

ui/store/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,7 @@ export function setSelectedAccount(
17571757
!currentTabIsConnectedToNextAddress;
17581758

17591759
try {
1760+
await _setSelectedAddress(address);
17601761
await _setSelectedInternalAccount(internalAccount.id);
17611762
await forceUpdateMetamaskState(dispatch);
17621763
} catch (error) {

0 commit comments

Comments
 (0)