Skip to content

Commit 0f840ba

Browse files
committed
support hideExternalAccounts
1 parent ed2a43e commit 0f840ba

File tree

2 files changed

+256
-6
lines changed

2 files changed

+256
-6
lines changed

packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,96 @@ describe('SnapInterfaceController', () => {
368368
});
369369
});
370370

371+
it('can select an account owned by the snap', async () => {
372+
const rootMessenger = getRootSnapInterfaceControllerMessenger();
373+
const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(
374+
rootMessenger,
375+
false,
376+
);
377+
378+
rootMessenger.registerActionHandler(
379+
'AccountsController:getSelectedMultichainAccount',
380+
() => ({
381+
id: MOCK_ACCOUNT_ID,
382+
address: '0x1234567890123456789012345678901234567890',
383+
scopes: ['eip155:0'],
384+
metadata: {
385+
// @ts-expect-error partial mock
386+
snap: {
387+
id: 'npm:[email protected]' as SnapId,
388+
},
389+
},
390+
}),
391+
);
392+
393+
rootMessenger.registerActionHandler(
394+
'AccountsController:listMultichainAccounts',
395+
() => [
396+
{
397+
id: MOCK_ACCOUNT_ID,
398+
address: '7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv',
399+
scopes: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
400+
metadata: {
401+
// @ts-expect-error partial mock
402+
snap: {
403+
id: MOCK_SNAP_ID,
404+
},
405+
},
406+
},
407+
],
408+
);
409+
410+
rootMessenger.registerActionHandler(
411+
'AccountsController:setSelectedAccount',
412+
() => {
413+
// no-op
414+
},
415+
);
416+
417+
// eslint-disable-next-line no-new
418+
new SnapInterfaceController({
419+
messenger: controllerMessenger,
420+
});
421+
422+
const element = (
423+
<Box>
424+
<AccountSelector name="foo" hideExternalAccounts />
425+
</Box>
426+
);
427+
428+
const id = await rootMessenger.call(
429+
'SnapInterfaceController:createInterface',
430+
MOCK_SNAP_ID,
431+
element,
432+
);
433+
434+
const { content, state } = rootMessenger.call(
435+
'SnapInterfaceController:getInterface',
436+
MOCK_SNAP_ID,
437+
id,
438+
);
439+
440+
expect(rootMessenger.call).toHaveBeenNthCalledWith(
441+
2,
442+
'AccountsController:getSelectedMultichainAccount',
443+
);
444+
445+
expect(rootMessenger.call).toHaveBeenNthCalledWith(
446+
3,
447+
'AccountsController:listMultichainAccounts',
448+
);
449+
450+
expect(content).toStrictEqual(element);
451+
expect(state).toStrictEqual({
452+
foo: {
453+
accountId: MOCK_ACCOUNT_ID,
454+
addresses: [
455+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv',
456+
],
457+
},
458+
});
459+
});
460+
371461
it('can get accounts of a specific chain ID from the client', async () => {
372462
const rootMessenger = getRootSnapInterfaceControllerMessenger();
373463
const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(

packages/snaps-controllers/src/interface/utils.test.tsx

Lines changed: 166 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ describe('constructState', () => {
8787
getSelectedAccount: jest.fn(),
8888
setSelectedAccount: jest.fn(),
8989
listAccounts: jest.fn(),
90+
snapOwnsAccount: jest.fn(),
9091
};
9192

9293
it('can construct a new component state', () => {
@@ -1387,6 +1388,7 @@ describe('getDefaultAsset', () => {
13871388
getSelectedAccount: jest.fn(),
13881389
setSelectedAccount: jest.fn(),
13891390
listAccounts: jest.fn(),
1391+
snapOwnsAccount: jest.fn(),
13901392
},
13911393
),
13921394
).toStrictEqual({
@@ -1428,6 +1430,7 @@ describe('getDefaultAsset', () => {
14281430
getSelectedAccount: jest.fn(),
14291431
setSelectedAccount: jest.fn(),
14301432
listAccounts: jest.fn(),
1433+
snapOwnsAccount: jest.fn(),
14311434
},
14321435
),
14331436
).toStrictEqual({
@@ -1462,6 +1465,7 @@ describe('getDefaultAsset', () => {
14621465
getSelectedAccount: jest.fn(),
14631466
setSelectedAccount: jest.fn(),
14641467
listAccounts: jest.fn(),
1468+
snapOwnsAccount: jest.fn(),
14651469
},
14661470
),
14671471
).toBeNull();
@@ -1504,6 +1508,7 @@ describe('getDefaultAsset', () => {
15041508
getSelectedAccount: jest.fn(),
15051509
setSelectedAccount: jest.fn(),
15061510
listAccounts: jest.fn(),
1511+
snapOwnsAccount: jest.fn(),
15071512
},
15081513
),
15091514
).toStrictEqual({
@@ -1535,6 +1540,7 @@ describe('getDefaultAsset', () => {
15351540
getSelectedAccount: jest.fn(),
15361541
setSelectedAccount: jest.fn(),
15371542
listAccounts: jest.fn(),
1543+
snapOwnsAccount: jest.fn(),
15381544
},
15391545
),
15401546
).toThrow(
@@ -1643,6 +1649,7 @@ describe('getAccountSelectorDefaultStateValue', () => {
16431649
getAssetsState: jest.fn(),
16441650
setSelectedAccount: jest.fn(),
16451651
listAccounts: jest.fn(),
1652+
snapOwnsAccount: jest.fn(),
16461653
}),
16471654
).toStrictEqual({
16481655
accountId: MOCK_ACCOUNT_ID,
@@ -1665,6 +1672,7 @@ describe('getAccountSelectorDefaultStateValue', () => {
16651672
getAssetsState: jest.fn(),
16661673
setSelectedAccount: jest.fn(),
16671674
listAccounts: jest.fn(),
1675+
snapOwnsAccount: jest.fn(),
16681676
},
16691677
),
16701678
).toStrictEqual({
@@ -1673,7 +1681,7 @@ describe('getAccountSelectorDefaultStateValue', () => {
16731681
});
16741682
});
16751683

1676-
it('returns the first account of the account that matches the chain Ids if the selected account is not on the same chain ID', () => {
1684+
it('returns the first account of the accounts that matches the chain Ids if the selected account is not on the same chain ID', () => {
16771685
const getSelectedAccount = jest.fn().mockReturnValue({
16781686
id: MOCK_ACCOUNT_ID,
16791687
address: '0x1234567890123456789012345678901234567890',
@@ -1705,6 +1713,85 @@ describe('getAccountSelectorDefaultStateValue', () => {
17051713
getAssetsState: jest.fn(),
17061714
setSelectedAccount: jest.fn(),
17071715
listAccounts,
1716+
snapOwnsAccount: jest.fn(),
1717+
},
1718+
),
1719+
).toStrictEqual({
1720+
accountId: MOCK_ACCOUNT_ID,
1721+
addresses: [
1722+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv',
1723+
],
1724+
});
1725+
});
1726+
1727+
it('returns the currently selected account in the client if the snap owns the account', () => {
1728+
const getSelectedAccount = jest.fn().mockReturnValue({
1729+
id: MOCK_ACCOUNT_ID,
1730+
address: '7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv',
1731+
scopes: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
1732+
});
1733+
1734+
const snapOwnsAccount = jest.fn().mockReturnValue(true);
1735+
expect(
1736+
getAccountSelectorDefaultStateValue(
1737+
<AccountSelector
1738+
name="foo"
1739+
hideExternalAccounts
1740+
chainIds={['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp']}
1741+
/>,
1742+
{
1743+
getSelectedAccount,
1744+
getAccountByAddress: jest.fn(),
1745+
getAssetsState: jest.fn(),
1746+
setSelectedAccount: jest.fn(),
1747+
listAccounts: jest.fn(),
1748+
snapOwnsAccount,
1749+
},
1750+
),
1751+
).toStrictEqual({
1752+
accountId: MOCK_ACCOUNT_ID,
1753+
addresses: [
1754+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv',
1755+
],
1756+
});
1757+
});
1758+
1759+
it('returns the first account of the accounts that matches the chain Ids and that is owned by the snap if the selected account is not on the same chain ID', () => {
1760+
const getSelectedAccount = jest.fn().mockReturnValue({
1761+
id: MOCK_ACCOUNT_ID,
1762+
address: '0x1234567890123456789012345678901234567890',
1763+
scopes: ['eip155:1', 'eip155:2', 'eip155:3'],
1764+
});
1765+
1766+
const listAccounts = jest.fn().mockReturnValue([
1767+
{
1768+
id: MOCK_ACCOUNT_ID,
1769+
address: '7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv',
1770+
scopes: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
1771+
},
1772+
{
1773+
id: MOCK_ACCOUNT_ID,
1774+
address: 'DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK',
1775+
scopes: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
1776+
},
1777+
]);
1778+
1779+
const snapOwnsAccount = jest.fn().mockReturnValue(true);
1780+
1781+
expect(
1782+
getAccountSelectorDefaultStateValue(
1783+
<AccountSelector
1784+
name="foo"
1785+
hideExternalAccounts
1786+
chainIds={['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp']}
1787+
/>,
1788+
{
1789+
getSelectedAccount,
1790+
getAccountByAddress: jest.fn(),
1791+
getAssetsState: jest.fn(),
1792+
setSelectedAccount: jest.fn(),
1793+
listAccounts,
1794+
snapOwnsAccount,
17081795
},
17091796
),
17101797
).toStrictEqual({
@@ -1736,6 +1823,37 @@ describe('getAccountSelectorDefaultStateValue', () => {
17361823
getAssetsState: jest.fn(),
17371824
setSelectedAccount: jest.fn(),
17381825
listAccounts,
1826+
snapOwnsAccount: jest.fn(),
1827+
},
1828+
),
1829+
).toThrow('No accounts found for the provided chain IDs.');
1830+
});
1831+
1832+
it('throws if no account that the snap owns is found for the given chain IDs', () => {
1833+
const getSelectedAccount = jest.fn().mockReturnValue({
1834+
id: MOCK_ACCOUNT_ID,
1835+
address: '0x1234567890123456789012345678901234567890',
1836+
scopes: ['eip155:1', 'eip155:2', 'eip155:3'],
1837+
});
1838+
1839+
const listAccounts = jest.fn().mockReturnValue([]);
1840+
1841+
const snapOwnsAccount = jest.fn().mockReturnValue(false);
1842+
1843+
expect(() =>
1844+
getAccountSelectorDefaultStateValue(
1845+
<AccountSelector
1846+
name="foo"
1847+
hideExternalAccounts
1848+
chainIds={['bip122:000000000019d6689c085ae165831e93']}
1849+
/>,
1850+
{
1851+
getSelectedAccount,
1852+
getAccountByAddress: jest.fn(),
1853+
getAssetsState: jest.fn(),
1854+
setSelectedAccount: jest.fn(),
1855+
listAccounts,
1856+
snapOwnsAccount,
17391857
},
17401858
),
17411859
).toThrow('No accounts found for the provided chain IDs.');
@@ -1776,6 +1894,7 @@ describe('getAccountSelectorDefaultStateValue', () => {
17761894
getAssetsState: jest.fn(),
17771895
setSelectedAccount,
17781896
listAccounts,
1897+
snapOwnsAccount: jest.fn(),
17791898
},
17801899
),
17811900
).toStrictEqual({
@@ -1805,8 +1924,14 @@ describe('getAccountSelectorStateValue', () => {
18051924
name="foo"
18061925
value="eip155:1:0x1234567890123456789012345678901234567890"
18071926
/>,
1808-
getAccountByAddress,
1809-
setSelectedAccount,
1927+
{
1928+
getAccountByAddress,
1929+
setSelectedAccount,
1930+
getSelectedAccount: jest.fn(),
1931+
getAssetsState: jest.fn(),
1932+
listAccounts: jest.fn(),
1933+
snapOwnsAccount: jest.fn(),
1934+
},
18101935
),
18111936
).toStrictEqual({
18121937
accountId: MOCK_ACCOUNT_ID,
@@ -1818,7 +1943,36 @@ describe('getAccountSelectorStateValue', () => {
18181943
});
18191944
});
18201945

1821-
it('returns the null if the account is not found', () => {
1946+
it('returns null if the account is not owned by the snap', () => {
1947+
const getAccountByAddress = jest.fn().mockReturnValue({
1948+
id: MOCK_ACCOUNT_ID,
1949+
address: '0x1234567890123456789012345678901234567890',
1950+
scopes: ['eip155:1', 'eip155:2', 'eip155:3'],
1951+
});
1952+
1953+
const setSelectedAccount = jest.fn();
1954+
const snapOwnsAccount = jest.fn().mockReturnValue(false);
1955+
1956+
expect(
1957+
getAccountSelectorStateValue(
1958+
<AccountSelector
1959+
name="foo"
1960+
hideExternalAccounts
1961+
value="eip155:1:0x1234567890123456789012345678901234567890"
1962+
/>,
1963+
{
1964+
getAccountByAddress,
1965+
setSelectedAccount,
1966+
getSelectedAccount: jest.fn(),
1967+
getAssetsState: jest.fn(),
1968+
listAccounts: jest.fn(),
1969+
snapOwnsAccount,
1970+
},
1971+
),
1972+
).toBeNull();
1973+
});
1974+
1975+
it('returns null if the account is not found', () => {
18221976
const getAccountByAddress = jest.fn().mockReturnValue(undefined);
18231977

18241978
const setSelectedAccount = jest.fn();
@@ -1829,8 +1983,14 @@ describe('getAccountSelectorStateValue', () => {
18291983
name="foo"
18301984
value="eip155:1:0x1234567890123456789012345678901234567890"
18311985
/>,
1832-
getAccountByAddress,
1833-
setSelectedAccount,
1986+
{
1987+
getAccountByAddress,
1988+
setSelectedAccount,
1989+
getSelectedAccount: jest.fn(),
1990+
getAssetsState: jest.fn(),
1991+
listAccounts: jest.fn(),
1992+
snapOwnsAccount: jest.fn(),
1993+
},
18341994
),
18351995
).toBeNull();
18361996
});

0 commit comments

Comments
 (0)