@1001-digital/proxies 0.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,442 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { keccak_256 } from "@noble/hashes/sha3";
5
+ const encoder = new TextEncoder();
6
+ function computeSelector(signature) {
7
+ const hash = keccak_256(encoder.encode(signature));
8
+ let hex = "0x";
9
+ for (let i = 0; i < 4; i++) {
10
+ hex += hash[i].toString(16).padStart(2, "0");
11
+ }
12
+ return hex;
13
+ }
14
+ function canonicalSignature(fn) {
15
+ if (!fn.name) throw new Error("Cannot build signature: entry has no name");
16
+ const types = (fn.inputs ?? []).map(canonicalType).join(",");
17
+ return `${fn.name}(${types})`;
18
+ }
19
+ function canonicalType(p) {
20
+ if (p.type.startsWith("tuple")) {
21
+ const suffix = p.type.slice("tuple".length);
22
+ const inner = (p.components ?? []).map(canonicalType).join(",");
23
+ return `(${inner})${suffix}`;
24
+ }
25
+ return p.type;
26
+ }
27
+ function filterAbiBySelectors(abi, selectors) {
28
+ const set = new Set(selectors.map((s) => s.toLowerCase()));
29
+ return abi.filter((item) => {
30
+ if (!isAbiFunction(item)) return true;
31
+ try {
32
+ const sel = computeSelector(canonicalSignature(item)).toLowerCase();
33
+ return set.has(sel);
34
+ } catch {
35
+ return false;
36
+ }
37
+ });
38
+ }
39
+ function buildCompositeAbi(abis) {
40
+ const seenSelectors = /* @__PURE__ */ new Set();
41
+ const seenEventErrors = /* @__PURE__ */ new Set();
42
+ const composite = [];
43
+ for (const abi of abis) {
44
+ for (const item of abi) {
45
+ if (isAbiFunction(item)) {
46
+ try {
47
+ const sel = computeSelector(canonicalSignature(item)).toLowerCase();
48
+ if (seenSelectors.has(sel)) continue;
49
+ seenSelectors.add(sel);
50
+ } catch {
51
+ }
52
+ } else if (isEventOrError(item)) {
53
+ try {
54
+ const key = `${item.type}:${canonicalSignature(item)}`;
55
+ if (seenEventErrors.has(key)) continue;
56
+ seenEventErrors.add(key);
57
+ } catch {
58
+ }
59
+ }
60
+ composite.push(item);
61
+ }
62
+ }
63
+ return composite;
64
+ }
65
+ function isAbiFunction(item) {
66
+ return typeof item === "object" && item !== null && item.type === "function" && typeof item.name === "string";
67
+ }
68
+ function isEventOrError(item) {
69
+ if (typeof item !== "object" || item === null) return false;
70
+ const t = item.type;
71
+ return (t === "event" || t === "error") && typeof item.name === "string";
72
+ }
73
+ const SUPPORTS_INTERFACE_SELECTOR = "0x01ffc9a7";
74
+ const DIAMOND_LOUPE_INTERFACE_ID = "0x48e2b093";
75
+ const FACETS_SELECTOR = "0x7a0ed627";
76
+ const IMPLEMENTATION_SELECTOR = "0x5c60da1b";
77
+ const EIP1967_IMPL_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
78
+ const EIP1967_BEACON_SLOT = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50";
79
+ const EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
80
+ const EIP1822_PROXIABLE_SLOT = "0xc5f16f0fcc7e328891200cdca4c1c57b2b360c12265401510d42209b5829f8e2";
81
+ const EIP1167_BYTECODE_PREFIX = "363d3d373d3d3d363d73";
82
+ const EIP1167_BYTECODE_SUFFIX = "5af43d82803e903d91602b57fd5bf3";
83
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
84
+ function errorMessage(error) {
85
+ return error instanceof Error ? error.message : String(error);
86
+ }
87
+ class ProxiesError extends Error {
88
+ constructor(message, options) {
89
+ super(message, options);
90
+ this.name = "ProxiesError";
91
+ }
92
+ }
93
+ class ProxiesDecodeError extends ProxiesError {
94
+ constructor(message, options) {
95
+ super(message, options);
96
+ this.name = "ProxiesDecodeError";
97
+ }
98
+ }
99
+ class ProxiesFetchError extends ProxiesError {
100
+ constructor(message, details, options) {
101
+ super(message, options);
102
+ __publicField(this, "status");
103
+ this.name = "ProxiesFetchError";
104
+ this.status = details.status;
105
+ }
106
+ }
107
+ const MAX_FACETS = 200;
108
+ const MAX_SELECTORS_PER_FACET = 1e3;
109
+ function decodeFacets(hex) {
110
+ const h = hex.startsWith("0x") ? hex.slice(2) : hex;
111
+ const W = 64;
112
+ const readWord = (pos) => {
113
+ if (pos < 0 || pos + W > h.length) {
114
+ throw new ProxiesDecodeError("malformed facets() return: out of bounds");
115
+ }
116
+ return h.slice(pos, pos + W);
117
+ };
118
+ const readUint = (pos) => {
119
+ const word = readWord(pos);
120
+ if (!/^0{48}/.test(word)) {
121
+ throw new ProxiesDecodeError("malformed facets() return: value too large");
122
+ }
123
+ return parseInt(word, 16);
124
+ };
125
+ const outerOff = readUint(0) * 2;
126
+ const n = readUint(outerOff);
127
+ if (n > MAX_FACETS) {
128
+ throw new ProxiesDecodeError(`malformed facets() return: ${n} facets exceeds limit`);
129
+ }
130
+ const head = outerOff + W;
131
+ const facets = [];
132
+ for (let i = 0; i < n; i++) {
133
+ const tupleOff = readUint(head + i * W) * 2;
134
+ const tx = head + tupleOff;
135
+ const addrWord = readWord(tx);
136
+ if (!/^0{24}/.test(addrWord)) {
137
+ throw new ProxiesDecodeError("malformed facets() return: invalid address");
138
+ }
139
+ const facetAddress = "0x" + addrWord.slice(24).toLowerCase();
140
+ const selOff = readUint(tx + W) * 2;
141
+ const selStart = tx + selOff;
142
+ const m = readUint(selStart);
143
+ if (m > MAX_SELECTORS_PER_FACET) {
144
+ throw new ProxiesDecodeError(
145
+ `malformed facets() return: ${m} selectors exceeds limit`
146
+ );
147
+ }
148
+ const selectors = [];
149
+ for (let j = 0; j < m; j++) {
150
+ const slot = readWord(selStart + W + j * W);
151
+ selectors.push("0x" + slot.slice(0, 8).toLowerCase());
152
+ }
153
+ facets.push({ facetAddress, functionSelectors: selectors });
154
+ }
155
+ return facets;
156
+ }
157
+ function parseBool(hex) {
158
+ if (hex.length !== 66) return null;
159
+ const body = hex.slice(2).toLowerCase();
160
+ if (!/^0{63}[01]$/.test(body)) return null;
161
+ return body.slice(-1) === "1";
162
+ }
163
+ function parseAddress(hex) {
164
+ if (hex.length !== 66) return null;
165
+ const body = hex.slice(2).toLowerCase();
166
+ if (!/^0{24}[0-9a-f]{40}$/.test(body)) return null;
167
+ return "0x" + body.slice(24);
168
+ }
169
+ async function jsonRpcCall(rpc, method, params, fetchFn) {
170
+ let res;
171
+ try {
172
+ res = await fetchFn(rpc, {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/json" },
175
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
176
+ });
177
+ } catch (error) {
178
+ throw new ProxiesFetchError(
179
+ `RPC request failed: ${errorMessage(error)}`,
180
+ { status: 0 },
181
+ { cause: error }
182
+ );
183
+ }
184
+ if (!res.ok) {
185
+ throw new ProxiesFetchError(
186
+ `RPC request failed: ${res.status}`,
187
+ { status: res.status }
188
+ );
189
+ }
190
+ let json;
191
+ try {
192
+ json = await res.json();
193
+ } catch (error) {
194
+ throw new ProxiesFetchError(
195
+ `Invalid JSON from RPC: ${errorMessage(error)}`,
196
+ { status: res.status },
197
+ { cause: error }
198
+ );
199
+ }
200
+ if (json.error) {
201
+ throw new ProxiesFetchError(
202
+ `RPC error: ${json.error.message}`,
203
+ { status: 0 }
204
+ );
205
+ }
206
+ return json.result ?? "0x";
207
+ }
208
+ function ethCall(rpc, to, data, fetchFn) {
209
+ return jsonRpcCall(rpc, "eth_call", [{ to, data }, "latest"], fetchFn);
210
+ }
211
+ function ethGetStorageAt(rpc, address, slot, fetchFn) {
212
+ return jsonRpcCall(rpc, "eth_getStorageAt", [address, slot, "latest"], fetchFn);
213
+ }
214
+ function ethGetCode(rpc, address, fetchFn) {
215
+ return jsonRpcCall(rpc, "eth_getCode", [address, "latest"], fetchFn);
216
+ }
217
+ async function detectDiamond(rpc, address, fetchFn) {
218
+ const calldata = SUPPORTS_INTERFACE_SELECTOR + DIAMOND_LOUPE_INTERFACE_ID.slice(2).padEnd(64, "0");
219
+ try {
220
+ const res = await ethCall(rpc, address, calldata, fetchFn);
221
+ const bool = parseBool(res);
222
+ if (bool === true) return tryFacets(rpc, address, fetchFn);
223
+ if (bool === false) return null;
224
+ } catch {
225
+ }
226
+ return tryFacets(rpc, address, fetchFn);
227
+ }
228
+ const MIN_FACETS_PAYLOAD_LEN = 130;
229
+ async function tryFacets(rpc, address, fetchFn) {
230
+ let res;
231
+ try {
232
+ res = await ethCall(rpc, address, FACETS_SELECTOR, fetchFn);
233
+ } catch {
234
+ return null;
235
+ }
236
+ if (res === "0x" || res.length < MIN_FACETS_PAYLOAD_LEN) return null;
237
+ let facets;
238
+ try {
239
+ facets = decodeFacets(res);
240
+ } catch {
241
+ return null;
242
+ }
243
+ const targets = facets.filter((f) => f.facetAddress !== ZERO_ADDRESS).map((f) => ({ address: f.facetAddress, selectors: f.functionSelectors }));
244
+ return targets.length > 0 ? { pattern: "eip-2535-diamond", targets } : null;
245
+ }
246
+ async function detectEip1167(rpc, address, fetchFn) {
247
+ let code;
248
+ try {
249
+ code = await ethGetCode(rpc, address, fetchFn);
250
+ } catch {
251
+ return null;
252
+ }
253
+ const body = code.toLowerCase().replace(/^0x/, "");
254
+ if (body.length !== 90) return null;
255
+ if (!body.startsWith(EIP1167_BYTECODE_PREFIX)) return null;
256
+ if (!body.endsWith(EIP1167_BYTECODE_SUFFIX)) return null;
257
+ const impl = "0x" + body.slice(EIP1167_BYTECODE_PREFIX.length, EIP1167_BYTECODE_PREFIX.length + 40);
258
+ if (impl === ZERO_ADDRESS) return null;
259
+ return {
260
+ pattern: "eip-1167",
261
+ targets: [{ address: impl }]
262
+ };
263
+ }
264
+ async function detectEip1822(rpc, address, fetchFn) {
265
+ let raw;
266
+ try {
267
+ raw = await ethGetStorageAt(rpc, address, EIP1822_PROXIABLE_SLOT, fetchFn);
268
+ } catch {
269
+ return null;
270
+ }
271
+ const impl = parseAddress(raw);
272
+ if (!impl || impl === ZERO_ADDRESS) return null;
273
+ return {
274
+ pattern: "eip-1822",
275
+ targets: [{ address: impl }]
276
+ };
277
+ }
278
+ async function detectEip1967(rpc, address, fetchFn) {
279
+ let implRaw;
280
+ let adminRaw;
281
+ try {
282
+ [implRaw, adminRaw] = await Promise.all([
283
+ ethGetStorageAt(rpc, address, EIP1967_IMPL_SLOT, fetchFn),
284
+ ethGetStorageAt(rpc, address, EIP1967_ADMIN_SLOT, fetchFn).catch(() => "0x")
285
+ ]);
286
+ } catch {
287
+ return null;
288
+ }
289
+ const impl = parseAddress(implRaw);
290
+ if (!impl || impl === "0x0000000000000000000000000000000000000000") return null;
291
+ const proxy = {
292
+ pattern: "eip-1967",
293
+ targets: [{ address: impl }]
294
+ };
295
+ const admin = parseAddress(adminRaw);
296
+ if (admin && admin !== "0x0000000000000000000000000000000000000000") {
297
+ proxy.admin = admin;
298
+ }
299
+ return proxy;
300
+ }
301
+ async function detectEip1967Beacon(rpc, address, fetchFn) {
302
+ let beaconRaw;
303
+ try {
304
+ beaconRaw = await ethGetStorageAt(rpc, address, EIP1967_BEACON_SLOT, fetchFn);
305
+ } catch {
306
+ return null;
307
+ }
308
+ const beacon = parseAddress(beaconRaw);
309
+ if (!beacon || beacon === ZERO_ADDRESS) return null;
310
+ let implRaw;
311
+ try {
312
+ implRaw = await ethCall(rpc, beacon, IMPLEMENTATION_SELECTOR, fetchFn);
313
+ } catch {
314
+ return null;
315
+ }
316
+ const impl = parseAddress(implRaw);
317
+ if (!impl || impl === ZERO_ADDRESS) return null;
318
+ return {
319
+ pattern: "eip-1967-beacon",
320
+ targets: [{ address: impl }],
321
+ beacon
322
+ };
323
+ }
324
+ async function detectEip897(rpc, address, fetchFn) {
325
+ let raw;
326
+ try {
327
+ raw = await ethCall(rpc, address, IMPLEMENTATION_SELECTOR, fetchFn);
328
+ } catch {
329
+ return null;
330
+ }
331
+ const impl = parseAddress(raw);
332
+ if (!impl || impl === ZERO_ADDRESS) return null;
333
+ return {
334
+ pattern: "eip-897",
335
+ targets: [{ address: impl }]
336
+ };
337
+ }
338
+ const SAFE_SINGLETON_SLOT = "0x0000000000000000000000000000000000000000000000000000000000000000";
339
+ async function detectGnosisSafe(rpc, address, fetchFn) {
340
+ let raw;
341
+ try {
342
+ raw = await ethGetStorageAt(rpc, address, SAFE_SINGLETON_SLOT, fetchFn);
343
+ } catch {
344
+ return null;
345
+ }
346
+ const impl = parseAddress(raw);
347
+ if (!impl || impl === ZERO_ADDRESS) return null;
348
+ return {
349
+ pattern: "gnosis-safe",
350
+ targets: [{ address: impl }]
351
+ };
352
+ }
353
+ const DETECTORS = [
354
+ detectDiamond,
355
+ detectEip1967,
356
+ detectEip1967Beacon,
357
+ detectEip1822,
358
+ detectEip1167,
359
+ detectGnosisSafe,
360
+ detectEip897
361
+ ];
362
+ async function detectProxy(rpc, address, fetchFn) {
363
+ for (const detect of DETECTORS) {
364
+ try {
365
+ const match = await detect(rpc, address, fetchFn);
366
+ if (match) return match;
367
+ } catch {
368
+ }
369
+ }
370
+ return null;
371
+ }
372
+ async function enrichTargets(targets, enrich) {
373
+ const enrichments = enrich ? await Promise.all(
374
+ targets.map((t) => enrich(t.address).catch(() => null))
375
+ ) : targets.map(() => null);
376
+ return targets.map((t, i) => {
377
+ const src = enrichments[i];
378
+ const info = { address: t.address };
379
+ if (t.selectors !== void 0) info.selectors = t.selectors;
380
+ if (src == null ? void 0 : src.abi) {
381
+ info.abi = t.selectors !== void 0 ? filterAbiBySelectors(src.abi, t.selectors) : src.abi;
382
+ }
383
+ return info;
384
+ });
385
+ }
386
+ function createProxies(config = {}) {
387
+ const fetchFn = config.fetch ?? globalThis.fetch;
388
+ const defaultEnrich = config.enrich ?? null;
389
+ return {
390
+ detect(rpc, address) {
391
+ return detectProxy(rpc, address, fetchFn);
392
+ },
393
+ async fetch(rpc, address, options) {
394
+ const raw = await detectProxy(rpc, address, fetchFn);
395
+ if (!raw) return null;
396
+ const enricher = (options == null ? void 0 : options.enrich) === false ? null : (options == null ? void 0 : options.enrich) ?? defaultEnrich;
397
+ const targets = await enrichTargets(raw.targets, enricher);
398
+ const abiLayers = targets.map((t) => t.abi).filter((a) => a !== void 0);
399
+ const compositeAbi = abiLayers.length > 0 ? buildCompositeAbi(abiLayers) : void 0;
400
+ const proxy = { pattern: raw.pattern, targets };
401
+ if (raw.beacon) proxy.beacon = raw.beacon;
402
+ if (raw.admin) proxy.admin = raw.admin;
403
+ if (compositeAbi) proxy.compositeAbi = compositeAbi;
404
+ return proxy;
405
+ }
406
+ };
407
+ }
408
+ export {
409
+ DIAMOND_LOUPE_INTERFACE_ID,
410
+ EIP1167_BYTECODE_PREFIX,
411
+ EIP1167_BYTECODE_SUFFIX,
412
+ EIP1822_PROXIABLE_SLOT,
413
+ EIP1967_ADMIN_SLOT,
414
+ EIP1967_BEACON_SLOT,
415
+ EIP1967_IMPL_SLOT,
416
+ FACETS_SELECTOR,
417
+ IMPLEMENTATION_SELECTOR,
418
+ ProxiesDecodeError,
419
+ ProxiesError,
420
+ ProxiesFetchError,
421
+ SUPPORTS_INTERFACE_SELECTOR,
422
+ ZERO_ADDRESS,
423
+ buildCompositeAbi,
424
+ canonicalSignature,
425
+ computeSelector,
426
+ createProxies,
427
+ decodeFacets,
428
+ detectDiamond,
429
+ detectEip1167,
430
+ detectEip1822,
431
+ detectEip1967,
432
+ detectEip1967Beacon,
433
+ detectEip897,
434
+ detectGnosisSafe,
435
+ detectProxy,
436
+ enrichTargets,
437
+ ethCall,
438
+ ethGetCode,
439
+ ethGetStorageAt,
440
+ filterAbiBySelectors,
441
+ parseAddress
442
+ };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@1001-digital/proxies",
3
+ "version": "0.0.1",
4
+ "description": "Ethereum proxy pattern detection primitives — ERC-2535 diamonds, EIP-1967, EIP-1167, beacon, Safe, EIP-1822, EIP-897. Detect, enrich, compose.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/1001-digital/diamonds.git"
9
+ },
10
+ "keywords": [
11
+ "ethereum",
12
+ "proxy",
13
+ "erc-2535",
14
+ "diamond",
15
+ "eip-1967",
16
+ "eip-1167",
17
+ "eip-1822",
18
+ "eip-897",
19
+ "beacon",
20
+ "gnosis-safe",
21
+ "loupe",
22
+ "facets",
23
+ "abi"
24
+ ],
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ }
31
+ },
32
+ "main": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "files": [
35
+ "dist",
36
+ "src",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "dependencies": {
41
+ "@noble/hashes": "^1.7.0"
42
+ },
43
+ "devDependencies": {
44
+ "@changesets/changelog-github": "^0.6.0",
45
+ "@changesets/cli": "^2.30.0",
46
+ "vite": "^6.0.0",
47
+ "vite-plugin-dts": "^4.0.0",
48
+ "vitest": "^3.0.0"
49
+ },
50
+ "scripts": {
51
+ "build": "vite build",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
54
+ "changeset": "changeset"
55
+ }
56
+ }
package/src/abi.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { canonicalSignature, computeSelector } from './selector'
2
+ import type { AbiFunctionLike } from './types'
3
+
4
+ /**
5
+ * Keep all non-function ABI entries; keep function entries whose computed
6
+ * selector is in the provided selector set. Verified facet contracts may
7
+ * declare extra functions not actually mounted on the diamond — this trims them.
8
+ */
9
+ export function filterAbiBySelectors(abi: unknown[], selectors: string[]): unknown[] {
10
+ const set = new Set(selectors.map(s => s.toLowerCase()))
11
+
12
+ return abi.filter(item => {
13
+ if (!isAbiFunction(item)) return true
14
+ try {
15
+ const sel = computeSelector(canonicalSignature(item)).toLowerCase()
16
+ return set.has(sel)
17
+ } catch {
18
+ return false
19
+ }
20
+ })
21
+ }
22
+
23
+ /**
24
+ * Combine multiple ABIs into a single composite. First-occurrence wins:
25
+ * - functions deduped by selector
26
+ * - events and errors deduped by `type:name(canonicalInputs)`
27
+ * - other entries (constructor, fallback, receive) kept as-is
28
+ */
29
+ export function buildCompositeAbi(abis: unknown[][]): unknown[] {
30
+ const seenSelectors = new Set<string>()
31
+ const seenEventErrors = new Set<string>()
32
+ const composite: unknown[] = []
33
+
34
+ for (const abi of abis) {
35
+ for (const item of abi) {
36
+ if (isAbiFunction(item)) {
37
+ try {
38
+ const sel = computeSelector(canonicalSignature(item)).toLowerCase()
39
+ if (seenSelectors.has(sel)) continue
40
+ seenSelectors.add(sel)
41
+ } catch {
42
+ // Include items we can't key
43
+ }
44
+ } else if (isEventOrError(item)) {
45
+ try {
46
+ const key = `${item.type}:${canonicalSignature(item)}`
47
+ if (seenEventErrors.has(key)) continue
48
+ seenEventErrors.add(key)
49
+ } catch {
50
+ // Include items we can't key
51
+ }
52
+ }
53
+ composite.push(item)
54
+ }
55
+ }
56
+
57
+ return composite
58
+ }
59
+
60
+ function isAbiFunction(item: unknown): item is AbiFunctionLike {
61
+ return typeof item === 'object'
62
+ && item !== null
63
+ && (item as { type?: unknown }).type === 'function'
64
+ && typeof (item as { name?: unknown }).name === 'string'
65
+ }
66
+
67
+ function isEventOrError(item: unknown): item is AbiFunctionLike {
68
+ if (typeof item !== 'object' || item === null) return false
69
+ const t = (item as { type?: unknown }).type
70
+ return (t === 'event' || t === 'error')
71
+ && typeof (item as { name?: unknown }).name === 'string'
72
+ }
@@ -0,0 +1,42 @@
1
+ // ── ERC-165 / ERC-2535 ──
2
+
3
+ /** ERC-165 `supportsInterface(bytes4)`. */
4
+ export const SUPPORTS_INTERFACE_SELECTOR = '0x01ffc9a7'
5
+
6
+ /** ERC-2535 Diamond Loupe `IDiamondLoupe` interface ID. */
7
+ export const DIAMOND_LOUPE_INTERFACE_ID = '0x48e2b093'
8
+
9
+ /** ERC-2535 `facets()` — returns `(address, bytes4[])[]`. */
10
+ export const FACETS_SELECTOR = '0x7a0ed627'
11
+
12
+ /** `implementation()` — used by EIP-897 proxies and EIP-1967 beacons. */
13
+ export const IMPLEMENTATION_SELECTOR = '0x5c60da1b'
14
+
15
+ // ── EIP-1967 storage slots ──
16
+ // Per EIP-1967, slots are `bytes32(uint256(keccak256('eip1967.proxy.<field>')) - 1)`.
17
+
18
+ /** EIP-1967 implementation slot: `keccak256('eip1967.proxy.implementation') - 1`. */
19
+ export const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'
20
+
21
+ /** EIP-1967 beacon slot: `keccak256('eip1967.proxy.beacon') - 1`. */
22
+ export const EIP1967_BEACON_SLOT = '0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50'
23
+
24
+ /** EIP-1967 admin slot: `keccak256('eip1967.proxy.admin') - 1`. */
25
+ export const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103'
26
+
27
+ // ── EIP-1822 ──
28
+
29
+ /** EIP-1822 UUPS PROXIABLE slot: `keccak256('PROXIABLE')`. */
30
+ export const EIP1822_PROXIABLE_SLOT = '0xc5f16f0fcc7e328891200cdca4c1c57b2b360c12265401510d42209b5829f8e2'
31
+
32
+ // ── EIP-1167 minimal-proxy bytecode markers ──
33
+ // Reference: https://eips.ethereum.org/EIPS/eip-1167
34
+ // Runtime: 0x363d3d373d3d3d363d73<20-byte impl>5af43d82803e903d91602b57fd5bf3 (45 bytes = 90 hex chars)
35
+
36
+ /** Prefix preceding the 20-byte implementation address in an EIP-1167 minimal proxy. */
37
+ export const EIP1167_BYTECODE_PREFIX = '363d3d373d3d3d363d73'
38
+
39
+ /** Suffix following the 20-byte implementation address in an EIP-1167 minimal proxy. */
40
+ export const EIP1167_BYTECODE_SUFFIX = '5af43d82803e903d91602b57fd5bf3'
41
+
42
+ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'