slotdiff 0.1.0-alpha.1 → 0.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +16 -9
  2. package/README.md +196 -105
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +22 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/build-info.d.ts +45 -0
  8. package/dist/build-info.d.ts.map +1 -0
  9. package/dist/build-info.js +319 -0
  10. package/dist/build-info.js.map +1 -0
  11. package/dist/cli.d.ts +25 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +363 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/erc7201.d.ts +72 -0
  16. package/dist/erc7201.d.ts.map +1 -0
  17. package/dist/erc7201.js +340 -0
  18. package/dist/erc7201.js.map +1 -0
  19. package/dist/errors.d.ts +5 -0
  20. package/dist/errors.d.ts.map +1 -1
  21. package/dist/errors.js +8 -0
  22. package/dist/errors.js.map +1 -1
  23. package/dist/explorer.js +2 -2
  24. package/dist/explorer.js.map +1 -1
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +3 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/reconstruct.d.ts +51 -3
  30. package/dist/reconstruct.d.ts.map +1 -1
  31. package/dist/reconstruct.js +37 -8
  32. package/dist/reconstruct.js.map +1 -1
  33. package/dist/report.d.ts +26 -0
  34. package/dist/report.d.ts.map +1 -0
  35. package/dist/report.js +109 -0
  36. package/dist/report.js.map +1 -0
  37. package/dist/resolve.d.ts +11 -0
  38. package/dist/resolve.d.ts.map +1 -1
  39. package/dist/resolve.js +11 -1
  40. package/dist/resolve.js.map +1 -1
  41. package/package.json +6 -3
  42. package/src/bin.ts +25 -0
  43. package/src/build-info.ts +405 -0
  44. package/src/cli.ts +423 -0
  45. package/src/erc7201.ts +412 -0
  46. package/src/errors.ts +9 -0
  47. package/src/explorer.ts +2 -2
  48. package/src/index.ts +18 -1
  49. package/src/reconstruct.ts +111 -10
  50. package/src/report.ts +155 -0
  51. package/src/resolve.ts +17 -2
package/dist/cli.js ADDED
@@ -0,0 +1,363 @@
1
+ /**
2
+ * `slotdiff <old> <new>`: each side is a contract in the project's build info
3
+ * or an address: a proxy (read as the implementation it runs now) or an
4
+ * implementation.
5
+ *
6
+ * Exit codes: 0 every check ran and passed (or was skipped with --no-safety),
7
+ * 1 a check failed, 2 something could not be checked. Failing to check is
8
+ * never reported as compatible.
9
+ */
10
+ import { readFile } from "node:fs/promises";
11
+ import { relative, resolve } from "node:path";
12
+ import { parseArgs, styleText } from "node:util";
13
+ import { findBuildInfo } from "./build-info.js";
14
+ import { readCode, readProxy } from "./chain.js";
15
+ import { diffLayouts } from "./diff.js";
16
+ import { checkNamespaces } from "./erc7201.js";
17
+ import { checkLines, namespaceLines, plain } from "./report.js";
18
+ import { errorCode, LayoutUnavailableError } from "./errors.js";
19
+ import { canQueryExplorer } from "./explorer.js";
20
+ import { rebuild } from "./resolve.js";
21
+ import { analyzeSource, SAFETY_ERROR_KINDS, } from "./reconstruct.js";
22
+ import { getSolc } from "./solc.js";
23
+ const USAGE = `Usage: slotdiff <old> <new> [options]
24
+
25
+ Checks that <new> can replace <old> behind a proxy: OpenZeppelin's storage layout
26
+ rules, that each ERC-7201 namespace stays in the slot the code really uses, and
27
+ OpenZeppelin's safety rules for <new> (constructors, initializers, selfdestruct...).
28
+ Each side is either:
29
+ a contract in this project's build info src/Vault.sol:Vault, or Vault when unique
30
+ an address a proxy (the implementation it runs now),
31
+ or an implementation
32
+
33
+ Options:
34
+ --rpc <url> JSON-RPC endpoint, required when a side is an address
35
+ --root <dir> Project root (default: the current directory)
36
+ --build-info <file> Read local sides from this build-info file, as is
37
+ (not compared with the sources on disk)
38
+ --explorer-url <url> Another Etherscan-compatible API, such as Blockscout
39
+ --unsafe-allow-renames Accept renames without @custom:oz-renamed-from
40
+ --unsafe-allow <kinds> Comma-separated safety errors to accept, e.g. constructor;
41
+ prefer @custom:oz-upgrades-unsafe-allow in the source
42
+ --no-safety Skip the safety rules
43
+ -h, --help Show this help
44
+ -v, --version Show the version
45
+
46
+ An address needs verified source: set ETHERSCAN_API_KEY once (one key covers every
47
+ chain), or use --explorer-url, with that explorer's key in its query if it needs one
48
+ (ETHERSCAN_API_KEY is sent only to Etherscan). --rpc picks the chain for each run.
49
+
50
+ Exit codes: 0 every check passed (or was skipped with --no-safety), 1 a check
51
+ failed, 2 something could not be checked.
52
+
53
+ A pass means these checks found no problem, not that an upgrade is safe in every
54
+ way. No warranty; see LICENSE.`;
55
+ const HELP_HINT = "Run slotdiff --help for usage.";
56
+ // "slot" and "diff" in block letters, split where the two words meet.
57
+ const BANNER = [
58
+ [" _ _ ", " _ _ __ __"],
59
+ [" ___| | ___ | |_", " __| (_)/ _|/ _|"],
60
+ ["/ __| |/ _ \\| __", "/ _` | | |_| |_"],
61
+ ["\\__ \\ | (_) | |", "| (_| | | _| _|"],
62
+ ["|___/_|\\___/ \\__", "\\__,_|_|_| |_|"],
63
+ ];
64
+ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
65
+ /** Runs the CLI and resolves to its exit code. */
66
+ export async function run(argv, io) {
67
+ let parsed;
68
+ try {
69
+ parsed = parseArgs({
70
+ args: argv,
71
+ allowPositionals: true,
72
+ options: {
73
+ rpc: { type: "string" },
74
+ root: { type: "string" },
75
+ "build-info": { type: "string" },
76
+ "explorer-url": { type: "string" },
77
+ "unsafe-allow-renames": { type: "boolean" },
78
+ "unsafe-allow": { type: "string" },
79
+ "no-safety": { type: "boolean" },
80
+ help: { type: "boolean", short: "h" },
81
+ version: { type: "boolean", short: "v" },
82
+ },
83
+ });
84
+ }
85
+ catch (e) {
86
+ // Node's text for an unknown option runs on about `--`; the option is enough.
87
+ const message = e.message;
88
+ const unknown = /^Unknown option '([^']+)'/.exec(message)?.[1];
89
+ const text = unknown !== undefined ? `Unknown option ${unknown}` : message;
90
+ io.stderr(`${text.replace(/\.?$/, ".")} ${HELP_HINT}`);
91
+ return 2;
92
+ }
93
+ const { values, positionals } = parsed;
94
+ const paint = io.color
95
+ ? (format, text) => styleText(format, text, { validateStream: false })
96
+ : plain;
97
+ // Only for a person at a terminal: never in a pipe, a log or CI.
98
+ const banner = async () => [
99
+ ...BANNER.map(([slot, diff]) => paint("blueBright", slot) + paint("magenta", diff)),
100
+ ` ${paint("dim", `storage layout diff for upgradeable contracts · ${await packageVersion()}`)}`,
101
+ ].join("\n");
102
+ if (values.help) {
103
+ io.stdout(io.tty ? `${await banner()}\n\n${USAGE}` : USAGE);
104
+ return 0;
105
+ }
106
+ if (values.version) {
107
+ io.stdout(io.tty ? await banner() : await packageVersion());
108
+ return 0;
109
+ }
110
+ if (positionals.length !== 2) {
111
+ io.stderr(`Expected <old> and <new>, got ${positionals.length} argument(s). ${HELP_HINT}`);
112
+ return 2;
113
+ }
114
+ if (positionals.some((p) => p.includes("://"))) {
115
+ // Not echoed: an RPC URL usually carries an API key.
116
+ io.stderr("A side looks like a URL. Pass the endpoint with --rpc <url>.");
117
+ return 2;
118
+ }
119
+ const short = positionals.find((p) => /^0x[0-9a-fA-F]*$/.test(p) && !ADDRESS.test(p));
120
+ if (short !== undefined) {
121
+ io.stderr(`${short} is not an address: an address is 0x and 40 hex digits.`);
122
+ return 2;
123
+ }
124
+ const hasAddress = positionals.some((p) => ADDRESS.test(p));
125
+ if (values.rpc === undefined && hasAddress) {
126
+ io.stderr("An address needs --rpc <url>.");
127
+ return 2;
128
+ }
129
+ const safety = !values["no-safety"];
130
+ if (!safety && values["unsafe-allow"] !== undefined) {
131
+ io.stderr("--unsafe-allow does nothing with --no-safety.");
132
+ return 2;
133
+ }
134
+ const unsafeAllow = (values["unsafe-allow"]?.split(",") ?? [])
135
+ .map((k) => k.trim())
136
+ .filter((k) => k !== "");
137
+ const unknownKind = unsafeAllow.find((k) => !SAFETY_ERROR_KINDS.includes(k));
138
+ if (unknownKind !== undefined) {
139
+ io.stderr(`--unsafe-allow: unknown error kind "${unknownKind}". Known: ${SAFETY_ERROR_KINDS.join(", ")}.`);
140
+ return 2;
141
+ }
142
+ // Neither URL is ever echoed: endpoints often carry an API key.
143
+ let rpcUrl;
144
+ try {
145
+ rpcUrl = values.rpc !== undefined ? httpUrl(values.rpc, "--rpc") : undefined;
146
+ const explorerUrl = values["explorer-url"];
147
+ if (explorerUrl !== undefined && hasCredentials(httpUrl(explorerUrl, "--explorer-url"))) {
148
+ throw new Error("--explorer-url must not include a user name or password.");
149
+ }
150
+ }
151
+ catch (e) {
152
+ io.stderr(e.message);
153
+ return 2;
154
+ }
155
+ const cwd = io.cwd ?? process.cwd();
156
+ const root = resolve(cwd, values.root ?? ".");
157
+ const buildInfoFile = values["build-info"] !== undefined ? resolve(cwd, values["build-info"]) : undefined;
158
+ const env = io.env ?? process.env;
159
+ // The Etherscan key goes to Etherscan only, never to another explorer's URL.
160
+ const explorer = values["explorer-url"] !== undefined
161
+ ? { apiUrl: values["explorer-url"] }
162
+ : { apiKey: env.ETHERSCAN_API_KEY };
163
+ const provider = rpcUrl !== undefined ? jsonRpcProvider(rpcUrl, io.fetch ?? fetch) : undefined;
164
+ if (io.tty)
165
+ io.stdout(`${await banner()}\n`);
166
+ const readSide = async (spec) => {
167
+ if (ADDRESS.test(spec)) {
168
+ // A proxy stands for what it runs now; any other address is an implementation.
169
+ const proxy = await readProxy(provider, spec);
170
+ const implementation = proxy?.implementation ?? spec.toLowerCase();
171
+ if (!canQueryExplorer(explorer)) {
172
+ await readCode(provider, implementation); // a wrong address is reported as such first
173
+ throw new Error(`${implementation} needs an explorer key to fetch its verified source: set ` +
174
+ "ETHERSCAN_API_KEY (or use --explorer-url for another explorer).");
175
+ }
176
+ const { record, analysis } = await rebuild(provider, implementation, { explorer });
177
+ return {
178
+ description: [
179
+ proxy === undefined
180
+ ? `${implementation} (implementation)`
181
+ : `${spec.toLowerCase()} → implementation ${implementation}` +
182
+ (proxy.beacon !== undefined ? ` (via beacon ${proxy.beacon})` : ""),
183
+ `${record.contract} (verified source, ${record.bytecodeMatch} match)`,
184
+ ],
185
+ kind: proxy?.beacon !== undefined ? "beacon" : analysis.kind,
186
+ analysis,
187
+ };
188
+ }
189
+ const build = await findBuildInfo(root, spec, { file: buildInfoFile });
190
+ const solc = await getSolc(build.solcVersion);
191
+ const analysis = await analyzeSource(build.input, solc, build.contract);
192
+ return {
193
+ description: [
194
+ build.contract,
195
+ relative(root, build.file) +
196
+ (buildInfoFile !== undefined ? " (as given, not compared with the sources on disk)" : ""),
197
+ ],
198
+ kind: analysis.kind,
199
+ analysis,
200
+ };
201
+ };
202
+ let old;
203
+ let next;
204
+ try {
205
+ old = await readSide(positionals[0]);
206
+ // The same side twice (e.g. a proxy against itself) is fetched and compiled once.
207
+ next = positionals[1] === positionals[0] ? old : await readSide(positionals[1]);
208
+ }
209
+ catch (e) {
210
+ io.stderr(`Could not check: ${e.message}`);
211
+ return 2;
212
+ }
213
+ for (const [label, side] of [
214
+ ["old", old],
215
+ ["new", next],
216
+ ]) {
217
+ io.stdout(`${paint("bold", label)} ${side.description[0]}\n ${paint("dim", side.description[1])}`);
218
+ }
219
+ // OpenZeppelin matches namespaces by annotation; slotdiff pairs them by the
220
+ // slot the code uses, and OpenZeppelin then compares the structs of each pair.
221
+ const namespaces = checkNamespaces(old.analysis, next.analysis);
222
+ const grid = namespaceLines(namespaces.pairs, paint);
223
+ if (grid.length > 0)
224
+ io.stdout(`\n${grid.join("\n")}`);
225
+ const report = diffLayouts(namespaces.layout, next.analysis.layout, {
226
+ unsafeAllowRenames: values["unsafe-allow-renames"],
227
+ });
228
+ const renames = values["unsafe-allow-renames"] ? " (renames accepted)" : "";
229
+ const checks = [
230
+ report.ok
231
+ ? { name: "Storage layout", status: "pass", summary: `compatible${renames}` }
232
+ : {
233
+ name: "Storage layout",
234
+ status: "fail",
235
+ summary: `incompatible${renames}`,
236
+ details: report.explain(),
237
+ },
238
+ ];
239
+ if (namespaces.pairs.length > 0) {
240
+ checks.push({
241
+ name: "ERC-7201 slots",
242
+ ...(namespaces.result === "fail"
243
+ ? { status: "fail", summary: "problems found in <new> (see above)" }
244
+ : namespaces.result === "unread"
245
+ ? { status: "skip", summary: "not checked: some slots could not be read (see above)" }
246
+ : grid.length > 0
247
+ ? { status: "pass", summary: "compared by the slot in code (see above)" }
248
+ : { status: "pass", summary: "match their annotations" }),
249
+ });
250
+ }
251
+ // The kind comes from <old>, the proxy being upgraded: inferring it from <new>
252
+ // would miss a UUPS implementation that lost its upgrade function.
253
+ const accepted = unsafeAllow.length > 0 ? `; accepted: ${unsafeAllow.join(", ")}` : "";
254
+ const safetyCheck = safety ? next.analysis.safety({ kind: old.kind, unsafeAllow }) : undefined;
255
+ checks.push(safetyCheck === undefined
256
+ ? { name: "Safety rules", status: "skip", summary: "skipped (--no-safety)" }
257
+ : safetyCheck.ok
258
+ ? {
259
+ name: "Safety rules",
260
+ status: "pass",
261
+ summary: `no problems found (${old.kind} proxy${accepted})`,
262
+ }
263
+ : {
264
+ name: "Safety rules",
265
+ status: "fail",
266
+ summary: `problems found (${old.kind} proxy${accepted})`,
267
+ details: safetyCheck.explain(),
268
+ });
269
+ io.stdout(`\n${checkLines(checks, paint).join("\n")}`);
270
+ // One verdict, so a passing check next to a failing one is never read as safe.
271
+ const problems = [
272
+ ...(report.ok ? [] : ["the storage layout is incompatible"]),
273
+ ...(namespaces.result === "fail" ? ["an ERC-7201 namespace fails the slot check"] : []),
274
+ ...(safetyCheck?.ok === false ? ["<new> breaks the safety rules"] : []),
275
+ ];
276
+ if (problems.length > 0) {
277
+ io.stdout(`\n${paint(["bold", "red"], "Not safe to upgrade:")} ${problems.join(" and ")}.`);
278
+ return 1;
279
+ }
280
+ if (namespaces.result === "unread") {
281
+ io.stdout(`\n${paint(["bold", "yellow"], "Could not check:")} some ERC-7201 slots could not be read.`);
282
+ return 2;
283
+ }
284
+ io.stdout(safetyCheck === undefined
285
+ ? `\n${paint("yellow", "Storage layout compatible; safety rules not checked (--no-safety).")}`
286
+ : `\n${paint(["bold", "green"], "Safe to upgrade.")}`);
287
+ return 0;
288
+ }
289
+ function httpUrl(value, flag) {
290
+ let url;
291
+ try {
292
+ url = new URL(value);
293
+ }
294
+ catch {
295
+ throw new Error(`${flag} is not a valid URL; it needs a scheme, such as https://.`);
296
+ }
297
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
298
+ throw new Error(`${flag} must be an http or https URL.`);
299
+ }
300
+ return url;
301
+ }
302
+ function hasCredentials(url) {
303
+ return url.username !== "" || url.password !== "";
304
+ }
305
+ /**
306
+ * A minimal JSON-RPC client. Errors never include the URL: endpoints often
307
+ * carry an API key in the path or query. Credentials in the URL, which fetch
308
+ * refuses, are sent as basic auth instead.
309
+ */
310
+ function jsonRpcProvider(url, fetchImpl) {
311
+ const headers = { "content-type": "application/json" };
312
+ if (hasCredentials(url)) {
313
+ const user = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`;
314
+ headers.authorization = `Basic ${Buffer.from(user).toString("base64")}`;
315
+ url = new URL(url);
316
+ url.username = "";
317
+ url.password = "";
318
+ }
319
+ const endpoint = url.href;
320
+ let id = 0;
321
+ return {
322
+ async send(method, params) {
323
+ let response;
324
+ try {
325
+ response = await fetchImpl(endpoint, {
326
+ method: "POST",
327
+ headers,
328
+ body: JSON.stringify({ jsonrpc: "2.0", id: ++id, method, params }),
329
+ });
330
+ }
331
+ catch (e) {
332
+ throw new LayoutUnavailableError(`Cannot reach the RPC endpoint (${errorCode(e)}).`);
333
+ }
334
+ if (!response.ok) {
335
+ throw new LayoutUnavailableError(`The RPC endpoint answered HTTP ${response.status}.`);
336
+ }
337
+ let body;
338
+ try {
339
+ body = (await response.json());
340
+ }
341
+ catch {
342
+ body = {};
343
+ }
344
+ if (typeof body !== "object" || body === null || !("result" in body || "error" in body)) {
345
+ throw new LayoutUnavailableError(`The RPC endpoint did not answer JSON-RPC.`);
346
+ }
347
+ if (body.error !== undefined) {
348
+ // The server's text, without control characters (terminal escapes) and capped.
349
+ const message = String(body.error.message ?? "unknown error")
350
+ // eslint-disable-next-line no-control-regex
351
+ .replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
352
+ .slice(0, 300);
353
+ throw new Error(`RPC ${method} failed: ${message}`);
354
+ }
355
+ return body.result;
356
+ },
357
+ };
358
+ }
359
+ async function packageVersion() {
360
+ const pkg = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
361
+ return pkg.version;
362
+ }
363
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,EAA0B,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,aAAa,EACb,kBAAkB,GAInB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BA+BiB,CAAC;AAEhC,MAAM,SAAS,GAAG,gCAAgC,CAAC;AAEnD,sEAAsE;AACtE,MAAM,MAAM,GAA4B;IACtC,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;IACvC,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IACxC,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;IACxC,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAC1C,CAAC,oBAAoB,EAAE,iBAAiB,CAAC;CAC1C,CAAC;AAEF,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAwBtC,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,EAAS;IACjD,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC;YACjB,IAAI,EAAE,IAAI;YACV,gBAAgB,EAAE,IAAI;YACtB,OAAO,EAAE;gBACP,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACvB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACxB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAChC,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAClC,sBAAsB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;gBAC3C,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAClC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;gBAChC,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;gBACrC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;aACzC;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,8EAA8E;QAC9E,MAAM,OAAO,GAAI,CAAW,CAAC,OAAO,CAAC;QACrC,MAAM,OAAO,GAAG,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3E,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC;QACvD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACvC,MAAM,KAAK,GAAU,EAAE,CAAC,KAAK;QAC3B,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QACtE,CAAC,CAAC,KAAK,CAAC;IACV,iEAAiE;IACjE,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE,CACxB;QACE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACnF,IAAI,KAAK,CAAC,KAAK,EAAE,mDAAmD,MAAM,cAAc,EAAE,EAAE,CAAC,EAAE;KAChG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,cAAc,EAAE,CAAC,CAAC;QAC5D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,EAAE,CAAC,MAAM,CAAC,iCAAiC,WAAW,CAAC,MAAM,iBAAiB,SAAS,EAAE,CAAC,CAAC;QAC3F,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC/C,qDAAqD;QACrD,EAAE,CAAC,MAAM,CAAC,8DAA8D,CAAC,CAAC;QAC1E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,yDAAyD,CAAC,CAAC;QAC7E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,IAAI,UAAU,EAAE,CAAC;QAC3C,EAAE,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC;QAC3C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,CAAC,KAAK,SAAS,EAAE,CAAC;QACpD,EAAE,CAAC,MAAM,CAAC,+CAA+C,CAAC,CAAC;QAC3D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;SAC3D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAsB,CAAC;IAChD,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAE,kBAAwC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC9D,CAAC;IACF,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,EAAE,CAAC,MAAM,CACP,uCAAuC,WAAW,aAAa,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAChG,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,gEAAgE;IAChE,IAAI,MAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;QAC3C,IAAI,WAAW,KAAK,SAAS,IAAI,cAAc,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC;YACxF,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,EAAE,CAAC,MAAM,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;IAC9C,MAAM,aAAa,GACjB,MAAM,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,MAAM,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IAClC,6EAA6E;IAC7E,MAAM,QAAQ,GACZ,MAAM,CAAC,cAAc,CAAC,KAAK,SAAS;QAClC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE;QACpC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,iBAAiB,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE/F,IAAI,EAAE,CAAC,GAAG;QAAE,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,MAAM,EAAE,IAAI,CAAC,CAAC;IAE7C,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAY,EAAiB,EAAE;QACrD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,+EAA+E;YAC/E,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,QAAS,EAAE,IAAI,CAAC,CAAC;YAC/C,MAAM,cAAc,GAAG,KAAK,EAAE,cAAc,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,MAAM,QAAQ,CAAC,QAAS,EAAE,cAAc,CAAC,CAAC,CAAC,4CAA4C;gBACvF,MAAM,IAAI,KAAK,CACb,GAAG,cAAc,2DAA2D;oBAC1E,iEAAiE,CACpE,CAAC;YACJ,CAAC;YACD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,OAAO,CAAC,QAAS,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpF,OAAO;gBACL,WAAW,EAAE;oBACX,KAAK,KAAK,SAAS;wBACjB,CAAC,CAAC,GAAG,cAAc,mBAAmB;wBACtC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,qBAAqB,cAAc,EAAE;4BAC1D,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvE,GAAG,MAAM,CAAC,QAAQ,sBAAsB,MAAM,CAAC,aAAa,SAAS;iBACtE;gBACD,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI;gBAC5D,QAAQ;aACT,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxE,OAAO;YACL,WAAW,EAAE;gBACX,KAAK,CAAC,QAAQ;gBACd,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;oBACxB,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,EAAE,CAAC;aAC5F;YACD,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,QAAQ;SACT,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,GAAS,CAAC;IACd,IAAI,IAAU,CAAC;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,kFAAkF;QAClF,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,EAAE,CAAC,MAAM,CAAC,oBAAqB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI;QAC1B,CAAC,KAAK,EAAE,GAAG,CAAC;QACZ,CAAC,KAAK,EAAE,IAAI,CAAC;KACL,EAAE,CAAC;QACX,EAAE,CAAC,MAAM,CACP,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAC7F,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,+EAA+E;IAC/E,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,cAAc,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACrD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,EAAE,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEvD,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;QAClE,kBAAkB,EAAE,MAAM,CAAC,sBAAsB,CAAC;KACnD,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,MAAM,GAAY;QACtB,MAAM,CAAC,EAAE;YACP,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,OAAO,EAAE,EAAE;YAC7E,CAAC,CAAC;gBACE,IAAI,EAAE,gBAAgB;gBACtB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,eAAe,OAAO,EAAE;gBACjC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;aAC1B;KACN,CAAC;IAEF,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,gBAAgB;YACtB,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM;gBAC9B,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,qCAAqC,EAAE;gBACpE,CAAC,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ;oBAC9B,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,uDAAuD,EAAE;oBACtF,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;wBACf,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,0CAA0C,EAAE;wBACzE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;SAChE,CAAC,CAAC;IACL,CAAC;IAED,+EAA+E;IAC/E,mEAAmE;IACnE,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/F,MAAM,CAAC,IAAI,CACT,WAAW,KAAK,SAAS;QACvB,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,EAAE;QAC5E,CAAC,CAAC,WAAW,CAAC,EAAE;YACd,CAAC,CAAC;gBACE,IAAI,EAAE,cAAc;gBACpB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,sBAAsB,GAAG,CAAC,IAAI,SAAS,QAAQ,GAAG;aAC5D;YACH,CAAC,CAAC;gBACE,IAAI,EAAE,cAAc;gBACpB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,mBAAmB,GAAG,CAAC,IAAI,SAAS,QAAQ,GAAG;gBACxD,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE;aAC/B,CACR,CAAC;IACF,EAAE,CAAC,MAAM,CAAC,KAAK,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEvD,+EAA+E;IAC/E,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC;QAC5D,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,GAAG,CAAC,WAAW,EAAE,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACxE,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,sBAAsB,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5F,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACnC,EAAE,CAAC,MAAM,CACP,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,CAAC,yCAAyC,CAC5F,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,EAAE,CAAC,MAAM,CACP,WAAW,KAAK,SAAS;QACvB,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ,EAAE,oEAAoE,CAAC,EAAE;QAC9F,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,CAAC,EAAE,CACxD,CAAC;IACF,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY;IAC1C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,2DAA2D,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,gCAAgC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,GAAQ;IAC9B,OAAO,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,GAAQ,EAAE,SAAoB;IACrD,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;IAC/E,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvF,OAAO,CAAC,aAAa,GAAG,SAAS,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxE,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC;QAClB,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC;IACpB,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC;IAC1B,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;YACvB,IAAI,QAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE;oBACnC,MAAM,EAAE,MAAM;oBACd,OAAO;oBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;iBACnE,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,sBAAsB,CAAC,kCAAkC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACvF,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,sBAAsB,CAAC,kCAAkC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YACzF,CAAC;YACD,IAAI,IAAwD,CAAC;YAC7D,IAAI,CAAC;gBACH,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAgB,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,EAAE,CAAC;YACZ,CAAC;YACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC;gBACxF,MAAM,IAAI,sBAAsB,CAAC,2CAA2C,CAAC,CAAC;YAChF,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,+EAA+E;gBAC/E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,eAAe,CAAC;oBAC3D,4CAA4C;qBAC3C,OAAO,CAAC,+BAA+B,EAAE,GAAG,CAAC;qBAC7C,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,OAAO,MAAM,YAAY,OAAO,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,cAAc;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAEzF,CAAC;IACF,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * ERC-7201 namespaces by the slot the code really uses.
3
+ *
4
+ * OpenZeppelin matches namespaces by their `@custom:storage-location
5
+ * erc7201:<id>` annotation and never checks the `bytes32` slot the code assigns
6
+ * (`assembly { $.slot := LOCATION }`). The two can disagree. A changed
7
+ * constant behind an unchanged annotation moves the storage while the layout
8
+ * check passes; a corrected annotation over an unchanged constant leaves the
9
+ * storage in place while the check reports a deleted namespace. Reading the
10
+ * slot from the AST catches the first and fixes the second.
11
+ */
12
+ import type { SolcOutput, StorageLayout } from "@openzeppelin/upgrades-core";
13
+ export interface NamespaceSlot {
14
+ /** `erc7201:<id>`, as annotated. */
15
+ namespace: string;
16
+ /** The slot the annotation implies. */
17
+ expected: string;
18
+ /** The slot the code assigns, when it could be read and is unique. */
19
+ actual?: string;
20
+ /**
21
+ * `match`: the code uses the annotated slot. `mismatch`: it uses `actual`
22
+ * instead. `conflict`: it assigns different slots in different places.
23
+ * `unread`: no assignment was found, or one could not be evaluated.
24
+ */
25
+ status: "match" | "mismatch" | "conflict" | "unread";
26
+ }
27
+ /**
28
+ * How each of `namespaces` (`erc7201:<id>` keys of `contract`'s layout,
29
+ * `source.sol:Name`) is assigned in `output`. Only structs declared in the
30
+ * contract or its bases count, as for OpenZeppelin: a sibling contract in the
31
+ * same build (a V1 next to its V2, a test copy) may declare the same id.
32
+ */
33
+ export declare function namespaceSlots(output: SolcOutput, contract: string, namespaces: string[]): NamespaceSlot[];
34
+ export interface NamespacePair {
35
+ old?: NamespaceSlot;
36
+ next?: NamespaceSlot;
37
+ /**
38
+ * Why this pair fails the check: `<new>` assigns several slots (`conflict`),
39
+ * leaves the old data behind (`moved`), or keeps the data in place under an
40
+ * annotation that does not match (`mismatch`). `unread`: a slot could not be
41
+ * read, so the pair could not be checked.
42
+ */
43
+ problem?: "conflict" | "moved" | "mismatch" | "unread";
44
+ }
45
+ export interface NamespaceCheck {
46
+ /**
47
+ * `old`'s layout with each paired namespace under its name in `<new>`, for
48
+ * `diffLayouts`: OpenZeppelin then compares the structs of the same storage.
49
+ * An unpaired namespace keeps its name, so one that is really gone is still
50
+ * reported as deleted.
51
+ */
52
+ layout: StorageLayout;
53
+ /** Every namespace of either side, old and new paired when they are the same storage. */
54
+ pairs: NamespacePair[];
55
+ /** `fail` when any pair has a problem other than `unread`; `unread` when a slot could not be read. */
56
+ result: "pass" | "fail" | "unread";
57
+ }
58
+ /**
59
+ * Checks ERC-7201 namespaces by the slot the code uses, not the annotation
60
+ * OpenZeppelin goes by. An old and a new namespace are the same storage when
61
+ * the code uses the same slot (under the same annotation first), failing that
62
+ * when they share an annotation, and failing that when the new annotation
63
+ * names the slot where the old data is.
64
+ * Pass `layout` to `diffLayouts` in place of `old.layout`.
65
+ */
66
+ export declare function checkNamespaces(old: {
67
+ layout: StorageLayout;
68
+ namespaces: NamespaceSlot[];
69
+ }, next: {
70
+ namespaces: NamespaceSlot[];
71
+ }): NamespaceCheck;
72
+ //# sourceMappingURL=erc7201.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erc7201.d.ts","sourceRoot":"","sources":["../src/erc7201.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAW7E,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;CACtD;AAQD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAAE,GACnB,aAAa,EAAE,CA6FjB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;CACxD;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB,yFAAyF;IACzF,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,sGAAsG;IACtG,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,UAAU,EAAE,aAAa,EAAE,CAAA;CAAE,EAC3D,IAAI,EAAE;IAAE,UAAU,EAAE,aAAa,EAAE,CAAA;CAAE,GACpC,cAAc,CAmDhB"}