Skip to content

Commit 19b08a7

Browse files
committed
feat: Add new metadata to snaps controllers
The new metadata properties `includeInStateLogs` and `usedInUi` have been added to all snaps controllers. Relates to https://github.com/MetaMask/decisions/blob/main/decisions/core/0014-Expand-Controller-Metadata.md
1 parent 69f2a5a commit 19b08a7

File tree

12 files changed

+563
-115
lines changed

12 files changed

+563
-115
lines changed

packages/snaps-controllers/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"@types/concat-stream": "^2.0.0",
122122
"@types/gunzip-maybe": "^1.4.0",
123123
"@types/jest": "^27.5.1",
124+
"@types/lodash": "^4",
124125
"@types/luxon": "^3",
125126
"@types/node": "18.14.2",
126127
"@types/readable-stream": "^4.0.15",
@@ -133,6 +134,7 @@
133134
"jest": "^29.0.2",
134135
"jest-fetch-mock": "^3.0.3",
135136
"jest-silent-reporter": "^0.6.0",
137+
"lodash": "^4.17.21",
136138
"prettier": "^3.3.3",
137139
"rimraf": "^4.1.2",
138140
"ts-node": "^10.9.1",

packages/snaps-controllers/src/cronjob/CronjobController.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { deriveStateFromMetadata } from '@metamask/base-controller';
12
import { SnapEndowments } from '@metamask/snaps-rpc-methods';
23
import type { TruncatedSnap } from '@metamask/snaps-utils';
34
import { HandlerType } from '@metamask/snaps-utils';
@@ -1126,4 +1127,66 @@ describe('CronjobController', () => {
11261127
cronjobController.destroy();
11271128
});
11281129
});
1130+
1131+
describe('metadata', () => {
1132+
it('includes expected state in debug snapshots', () => {
1133+
const controller = new CronjobController({
1134+
messenger: getRestrictedCronjobControllerMessenger(),
1135+
stateManager: getMockStateManager(),
1136+
});
1137+
1138+
expect(
1139+
deriveStateFromMetadata(
1140+
controller.state,
1141+
controller.metadata,
1142+
'anonymous',
1143+
),
1144+
).toMatchInlineSnapshot(`{}`);
1145+
});
1146+
1147+
it('includes expected state in state logs', () => {
1148+
const controller = new CronjobController({
1149+
messenger: getRestrictedCronjobControllerMessenger(),
1150+
stateManager: getMockStateManager(),
1151+
});
1152+
1153+
expect(
1154+
deriveStateFromMetadata(
1155+
controller.state,
1156+
controller.metadata,
1157+
'includeInStateLogs',
1158+
),
1159+
).toMatchInlineSnapshot(`{}`);
1160+
});
1161+
1162+
it('persists expected state', () => {
1163+
const controller = new CronjobController({
1164+
messenger: getRestrictedCronjobControllerMessenger(),
1165+
stateManager: getMockStateManager(),
1166+
});
1167+
1168+
expect(
1169+
deriveStateFromMetadata(
1170+
controller.state,
1171+
controller.metadata,
1172+
'persist',
1173+
),
1174+
).toMatchInlineSnapshot(`{}`);
1175+
});
1176+
1177+
it('exposes expected state to UI', () => {
1178+
const controller = new CronjobController({
1179+
messenger: getRestrictedCronjobControllerMessenger(),
1180+
stateManager: getMockStateManager(),
1181+
});
1182+
1183+
expect(
1184+
deriveStateFromMetadata(
1185+
controller.state,
1186+
controller.metadata,
1187+
'usedInUi',
1188+
),
1189+
).toMatchInlineSnapshot(`{}`);
1190+
});
1191+
});
11291192
});

packages/snaps-controllers/src/cronjob/CronjobController.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,12 @@ export class CronjobController extends BaseController<
178178
super({
179179
messenger,
180180
metadata: {
181-
events: { persist: false, anonymous: false },
181+
events: {
182+
includeInStateLogs: false,
183+
persist: false,
184+
anonymous: false,
185+
usedInUi: false,
186+
},
182187
},
183188
name: controllerName,
184189
state: {

packages/snaps-controllers/src/insights/SnapInsightsController.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { deriveStateFromMetadata } from '@metamask/base-controller';
12
import { InternalError } from '@metamask/snaps-sdk';
23
import { HandlerType } from '@metamask/snaps-utils';
34
import {
@@ -649,4 +650,70 @@ describe('SnapInsightsController', () => {
649650
},
650651
);
651652
});
653+
654+
describe('metadata', () => {
655+
it('includes expected state in debug snapshots', () => {
656+
const controller = new SnapInsightsController({
657+
messenger: getRestrictedSnapInsightsControllerMessenger(),
658+
});
659+
660+
expect(
661+
deriveStateFromMetadata(
662+
controller.state,
663+
controller.metadata,
664+
'anonymous',
665+
),
666+
).toMatchInlineSnapshot(`{}`);
667+
});
668+
669+
it('includes expected state in state logs', () => {
670+
const controller = new SnapInsightsController({
671+
messenger: getRestrictedSnapInsightsControllerMessenger(),
672+
});
673+
674+
expect(
675+
deriveStateFromMetadata(
676+
controller.state,
677+
controller.metadata,
678+
'includeInStateLogs',
679+
),
680+
).toMatchInlineSnapshot(`
681+
{
682+
"insights": {},
683+
}
684+
`);
685+
});
686+
687+
it('persists expected state', () => {
688+
const controller = new SnapInsightsController({
689+
messenger: getRestrictedSnapInsightsControllerMessenger(),
690+
});
691+
692+
expect(
693+
deriveStateFromMetadata(
694+
controller.state,
695+
controller.metadata,
696+
'persist',
697+
),
698+
).toMatchInlineSnapshot(`{}`);
699+
});
700+
701+
it('exposes expected state to UI', () => {
702+
const controller = new SnapInsightsController({
703+
messenger: getRestrictedSnapInsightsControllerMessenger(),
704+
});
705+
706+
expect(
707+
deriveStateFromMetadata(
708+
controller.state,
709+
controller.metadata,
710+
'usedInUi',
711+
),
712+
).toMatchInlineSnapshot(`
713+
{
714+
"insights": {},
715+
}
716+
`);
717+
});
718+
});
652719
});

packages/snaps-controllers/src/insights/SnapInsightsController.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ export class SnapInsightsController extends BaseController<
100100
super({
101101
messenger,
102102
metadata: {
103-
insights: { persist: false, anonymous: false },
103+
insights: {
104+
includeInStateLogs: true,
105+
persist: false,
106+
anonymous: false,
107+
usedInUi: true,
108+
},
104109
},
105110
name: controllerName,
106111
state: { insights: {}, ...state },

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

Lines changed: 109 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getPersistentState } from '@metamask/base-controller';
1+
import { deriveStateFromMetadata } from '@metamask/base-controller';
22
import type { SnapId } from '@metamask/snaps-sdk';
33
import {
44
form,
@@ -82,40 +82,6 @@ describe('SnapInterfaceController', () => {
8282
});
8383
});
8484

85-
describe('constructor', () => {
86-
it('persists notification interfaces', () => {
87-
const rootMessenger = getRootSnapInterfaceControllerMessenger();
88-
const controllerMessenger =
89-
getRestrictedSnapInterfaceControllerMessenger(rootMessenger);
90-
91-
const controller = new SnapInterfaceController({
92-
messenger: controllerMessenger,
93-
state: {
94-
interfaces: {
95-
// @ts-expect-error missing properties
96-
'1': {
97-
contentType: ContentType.Notification,
98-
},
99-
// @ts-expect-error missing properties
100-
'2': {
101-
contentType: ContentType.Dialog,
102-
},
103-
},
104-
},
105-
});
106-
107-
expect(
108-
getPersistentState(controller.state, controller.metadata),
109-
).toStrictEqual({
110-
interfaces: {
111-
'1': {
112-
contentType: ContentType.Notification,
113-
},
114-
},
115-
});
116-
});
117-
});
118-
11985
describe('createInterface', () => {
12086
it('can create a new interface', async () => {
12187
const rootMessenger = getRootSnapInterfaceControllerMessenger();
@@ -1745,4 +1711,112 @@ describe('SnapInterfaceController', () => {
17451711
).rejects.toThrow(`Approval request with id '${id}' not found.`);
17461712
});
17471713
});
1714+
1715+
describe('metadata', () => {
1716+
it('includes expected state in debug snapshots', () => {
1717+
const controller = new SnapInterfaceController({
1718+
messenger: getRestrictedSnapInterfaceControllerMessenger(),
1719+
});
1720+
1721+
expect(
1722+
deriveStateFromMetadata(
1723+
controller.state,
1724+
controller.metadata,
1725+
'anonymous',
1726+
),
1727+
).toMatchInlineSnapshot(`{}`);
1728+
});
1729+
1730+
it('includes expected state in state logs', () => {
1731+
const controller = new SnapInterfaceController({
1732+
messenger: getRestrictedSnapInterfaceControllerMessenger(),
1733+
});
1734+
1735+
expect(
1736+
deriveStateFromMetadata(
1737+
controller.state,
1738+
controller.metadata,
1739+
'includeInStateLogs',
1740+
),
1741+
).toMatchInlineSnapshot(`
1742+
{
1743+
"interfaces": {},
1744+
}
1745+
`);
1746+
});
1747+
1748+
describe('persist', () => {
1749+
it('persists expected state', () => {
1750+
const controller = new SnapInterfaceController({
1751+
messenger: getRestrictedSnapInterfaceControllerMessenger(),
1752+
});
1753+
1754+
expect(
1755+
deriveStateFromMetadata(
1756+
controller.state,
1757+
controller.metadata,
1758+
'persist',
1759+
),
1760+
).toMatchInlineSnapshot(`
1761+
{
1762+
"interfaces": {},
1763+
}
1764+
`);
1765+
});
1766+
1767+
it('persists notification interfaces', () => {
1768+
const rootMessenger = getRootSnapInterfaceControllerMessenger();
1769+
const controllerMessenger =
1770+
getRestrictedSnapInterfaceControllerMessenger(rootMessenger);
1771+
1772+
const controller = new SnapInterfaceController({
1773+
messenger: controllerMessenger,
1774+
state: {
1775+
interfaces: {
1776+
// @ts-expect-error missing properties
1777+
'1': {
1778+
contentType: ContentType.Notification,
1779+
},
1780+
// @ts-expect-error missing properties
1781+
'2': {
1782+
contentType: ContentType.Dialog,
1783+
},
1784+
},
1785+
},
1786+
});
1787+
1788+
expect(
1789+
deriveStateFromMetadata(
1790+
controller.state,
1791+
controller.metadata,
1792+
'persist',
1793+
),
1794+
).toStrictEqual({
1795+
interfaces: {
1796+
'1': {
1797+
contentType: ContentType.Notification,
1798+
},
1799+
},
1800+
});
1801+
});
1802+
});
1803+
1804+
it('exposes expected state to UI', () => {
1805+
const controller = new SnapInterfaceController({
1806+
messenger: getRestrictedSnapInterfaceControllerMessenger(),
1807+
});
1808+
1809+
expect(
1810+
deriveStateFromMetadata(
1811+
controller.state,
1812+
controller.metadata,
1813+
'usedInUi',
1814+
),
1815+
).toMatchInlineSnapshot(`
1816+
{
1817+
"interfaces": {},
1818+
}
1819+
`);
1820+
});
1821+
});
17481822
});

packages/snaps-controllers/src/interface/SnapInterfaceController.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ export class SnapInterfaceController extends BaseController<
202202
messenger,
203203
metadata: {
204204
interfaces: {
205+
includeInStateLogs: true,
205206
persist: (interfaces: Record<string, StoredInterface>) => {
206207
return Object.entries(interfaces).reduce<
207208
Record<string, StoredInterface>
@@ -216,6 +217,7 @@ export class SnapInterfaceController extends BaseController<
216217
}, {});
217218
},
218219
anonymous: false,
220+
usedInUi: true,
219221
},
220222
},
221223
name: controllerName,

0 commit comments

Comments
 (0)