@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/LICENSE +21 -0
- package/README.md +276 -0
- package/dist/index.d.ts +353 -0
- package/dist/index.js +442 -0
- package/package.json +56 -0
- package/src/abi.ts +72 -0
- package/src/constants.ts +42 -0
- package/src/decode.ts +103 -0
- package/src/detect.ts +60 -0
- package/src/enrich.ts +36 -0
- package/src/errors.ts +33 -0
- package/src/index.ts +122 -0
- package/src/patterns/diamond.ts +77 -0
- package/src/patterns/eip1167.ts +45 -0
- package/src/patterns/eip1822.ts +34 -0
- package/src/patterns/eip1967-beacon.ts +43 -0
- package/src/patterns/eip1967.ts +44 -0
- package/src/patterns/eip897.ts +35 -0
- package/src/patterns/safe.ts +37 -0
- package/src/rpc.ts +93 -0
- package/src/selector.ts +37 -0
- package/src/types.ts +131 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 1001.digital
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# @1001-digital/proxies
|
|
2
|
+
|
|
3
|
+
Ethereum proxy pattern detection primitives for TypeScript — ERC-2535 diamonds, EIP-1967, EIP-1167, beacon, Safe, EIP-1822, EIP-897.
|
|
4
|
+
|
|
5
|
+
Given a contract address, resolve where the real code lives: one implementation (plain proxy) or N facets (diamond). Uniform detect → enrich → compose pipeline across all patterns.
|
|
6
|
+
|
|
7
|
+
Narrow on purpose: this package knows proxy conventions, selectors, and ABIs. Anything richer — Sourcify, NatSpec, repository metadata — is the consumer's concern. Bring your own enricher.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @1001-digital/proxies
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Detect + fetch a proxy
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createProxies } from '@1001-digital/proxies'
|
|
21
|
+
|
|
22
|
+
const proxies = createProxies()
|
|
23
|
+
|
|
24
|
+
const result = await proxies.fetch(
|
|
25
|
+
'https://eth.llamarpc.com',
|
|
26
|
+
'0xProxyAddress…',
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if (result) {
|
|
30
|
+
console.log(result.pattern) // 'eip-1967' | 'eip-2535-diamond' | …
|
|
31
|
+
console.log(result.targets) // [{ address, selectors?, abi? }, …]
|
|
32
|
+
console.log(result.compositeAbi) // undefined — no enricher configured
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Enrich with Sourcify (or anything else)
|
|
37
|
+
|
|
38
|
+
The enricher returns each target's ABI. Anything richer (sources, bytecode,
|
|
39
|
+
documentation, …) is the consumer's concern — see *Detect and do your own
|
|
40
|
+
enrichment* below.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createProxies } from '@1001-digital/proxies'
|
|
44
|
+
|
|
45
|
+
async function sourcifyAbi(address: string) {
|
|
46
|
+
const res = await fetch(
|
|
47
|
+
`https://sourcify.dev/server/v2/contract/1/${address}?fields=abi`,
|
|
48
|
+
)
|
|
49
|
+
if (!res.ok) return null
|
|
50
|
+
const { abi } = await res.json()
|
|
51
|
+
return { abi }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const proxies = createProxies({ enrich: sourcifyAbi })
|
|
55
|
+
|
|
56
|
+
const result = await proxies.fetch(
|
|
57
|
+
'https://eth.llamarpc.com',
|
|
58
|
+
'0xProxyAddress…',
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if (result) {
|
|
62
|
+
console.log(result.targets[0].abi) // ABI (filtered to selectors for diamonds, full for plain proxies)
|
|
63
|
+
console.log(result.compositeAbi) // all target ABIs deduped by selector
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Detect and do your own enrichment
|
|
68
|
+
|
|
69
|
+
For richer per-target metadata, use `detect` and own the enrichment step
|
|
70
|
+
end-to-end. `filterAbiBySelectors` and `buildCompositeAbi` remain useful
|
|
71
|
+
primitives.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import {
|
|
75
|
+
createProxies,
|
|
76
|
+
filterAbiBySelectors,
|
|
77
|
+
buildCompositeAbi,
|
|
78
|
+
} from '@1001-digital/proxies'
|
|
79
|
+
|
|
80
|
+
const proxies = createProxies()
|
|
81
|
+
const raw = await proxies.detect(rpc, address)
|
|
82
|
+
|
|
83
|
+
if (raw) {
|
|
84
|
+
const enriched = await Promise.all(raw.targets.map(async t => {
|
|
85
|
+
const src = await mySource(t.address)
|
|
86
|
+
return {
|
|
87
|
+
...t,
|
|
88
|
+
abi: src?.abi && t.selectors
|
|
89
|
+
? filterAbiBySelectors(src.abi, t.selectors)
|
|
90
|
+
: src?.abi,
|
|
91
|
+
metadata: src?.metadata,
|
|
92
|
+
}
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
const compositeAbi = buildCompositeAbi(
|
|
96
|
+
enriched.map(t => t.abi).filter((a): a is unknown[] => !!a),
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Standalone primitives
|
|
102
|
+
|
|
103
|
+
All low-level utilities are exported directly — use them without the factory:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import {
|
|
107
|
+
detectProxy,
|
|
108
|
+
detectDiamond,
|
|
109
|
+
detectEip1967,
|
|
110
|
+
decodeFacets,
|
|
111
|
+
computeSelector,
|
|
112
|
+
canonicalSignature,
|
|
113
|
+
filterAbiBySelectors,
|
|
114
|
+
buildCompositeAbi,
|
|
115
|
+
enrichTargets,
|
|
116
|
+
} from '@1001-digital/proxies'
|
|
117
|
+
|
|
118
|
+
decodeFacets('0x…') // parse a facets() return value
|
|
119
|
+
computeSelector('transfer(address,uint256)') // '0xa9059cbb'
|
|
120
|
+
canonicalSignature({ type: 'function', name: 'transfer', inputs: [/*…*/] })
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
### `createProxies(config?)`
|
|
126
|
+
|
|
127
|
+
Creates a proxies client.
|
|
128
|
+
|
|
129
|
+
**Config options:**
|
|
130
|
+
|
|
131
|
+
| Option | Type | Default | Description |
|
|
132
|
+
|--------|------|---------|-------------|
|
|
133
|
+
| `enrich` | `TargetEnricher` | — | Default per-target enricher. Called with each target address; return `{ abi? }` or `null`. Errors are swallowed per-target. |
|
|
134
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch function. |
|
|
135
|
+
|
|
136
|
+
**Returns** a `ProxiesClient` with:
|
|
137
|
+
|
|
138
|
+
- **`detect(rpc, address)`** — `Promise<RawProxy | null>`. On-chain probe only.
|
|
139
|
+
- **`fetch(rpc, address, options?)`** — `Promise<Proxy | null>`. Detect, enrich, and compose.
|
|
140
|
+
- `options.enrich` — per-call enricher (overrides config-level)
|
|
141
|
+
- `options.enrich = false` — skip enrichment for this call
|
|
142
|
+
|
|
143
|
+
### Detection
|
|
144
|
+
|
|
145
|
+
- **`detectProxy(rpc, address, fetchFn)`** — tries all patterns in priority order (`diamond → 1967 → 1967-beacon → 1822 → 1167 → safe → 897`), returns the first match.
|
|
146
|
+
- **`detectDiamond`** — ERC-165 probe, then `facets()` fallback.
|
|
147
|
+
- **`detectEip1967`** — reads impl slot `0x3608…3bc3`; optionally admin slot.
|
|
148
|
+
- **`detectEip1967Beacon`** — reads beacon slot `0xa3f0…750d`, then `implementation()` on the beacon.
|
|
149
|
+
- **`detectEip1822`** — reads PROXIABLE slot `0xc5f1…f8e2`.
|
|
150
|
+
- **`detectEip1167`** — `eth_getCode`, matches minimal proxy bytecode (`363d3d…bf3`).
|
|
151
|
+
- **`detectGnosisSafe`** — reads storage slot 0.
|
|
152
|
+
- **`detectEip897`** — calls `implementation()` as a last resort.
|
|
153
|
+
|
|
154
|
+
Each detector returns `null` if the pattern doesn't match; otherwise a `RawProxy`.
|
|
155
|
+
|
|
156
|
+
### Composition
|
|
157
|
+
|
|
158
|
+
- **`enrichTargets(targets, enricher | null)`** — applies the enricher to each target; ABIs are filtered to live selectors for diamonds, passed through for plain proxies.
|
|
159
|
+
- **`buildCompositeAbi(abis)`** — pure; dedupes functions/events/errors across ABIs (first-wins).
|
|
160
|
+
|
|
161
|
+
### Utilities
|
|
162
|
+
|
|
163
|
+
- **`decodeFacets(hex)`** — pure; decodes `(address, bytes4[])[]` loupe return.
|
|
164
|
+
- **`computeSelector(signature)`** — pure; keccak256-based 4-byte selector.
|
|
165
|
+
- **`canonicalSignature(abiEntry)`** — pure; normalizes tuples/arrays into a signature string.
|
|
166
|
+
- **`filterAbiBySelectors(abi, selectors)`** — pure; keeps non-functions, filters functions by selector.
|
|
167
|
+
- **`ethCall`**, **`ethGetStorageAt`**, **`ethGetCode`** — minimal JSON-RPC helpers.
|
|
168
|
+
|
|
169
|
+
### Constants
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
SUPPORTS_INTERFACE_SELECTOR // '0x01ffc9a7'
|
|
173
|
+
DIAMOND_LOUPE_INTERFACE_ID // '0x48e2b093'
|
|
174
|
+
FACETS_SELECTOR // '0x7a0ed627'
|
|
175
|
+
IMPLEMENTATION_SELECTOR // '0x5c60da1b'
|
|
176
|
+
EIP1967_IMPL_SLOT
|
|
177
|
+
EIP1967_BEACON_SLOT
|
|
178
|
+
EIP1967_ADMIN_SLOT
|
|
179
|
+
EIP1822_PROXIABLE_SLOT
|
|
180
|
+
EIP1167_BYTECODE_PREFIX
|
|
181
|
+
EIP1167_BYTECODE_SUFFIX
|
|
182
|
+
ZERO_ADDRESS
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Errors
|
|
186
|
+
|
|
187
|
+
- **`ProxiesError`** — base class.
|
|
188
|
+
- **`ProxiesDecodeError`** — malformed `facets()` return.
|
|
189
|
+
- **`ProxiesFetchError`** — JSON-RPC transport error.
|
|
190
|
+
|
|
191
|
+
The client's `detect` and `fetch` methods swallow RPC-layer errors and return
|
|
192
|
+
`null`; only `decodeFacets` (called directly) can throw `ProxiesDecodeError`
|
|
193
|
+
on malformed input.
|
|
194
|
+
|
|
195
|
+
## Shapes
|
|
196
|
+
|
|
197
|
+
### `ProxyPattern`
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
type ProxyPattern =
|
|
201
|
+
| 'eip-2535-diamond'
|
|
202
|
+
| 'eip-1967'
|
|
203
|
+
| 'eip-1967-beacon'
|
|
204
|
+
| 'eip-1822'
|
|
205
|
+
| 'eip-1167'
|
|
206
|
+
| 'gnosis-safe'
|
|
207
|
+
| 'eip-897'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### `ResolvedTarget`
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
{
|
|
214
|
+
address: string
|
|
215
|
+
// undefined = all selectors route here (plain proxy)
|
|
216
|
+
// defined = diamond facet selector scope
|
|
217
|
+
selectors?: string[]
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### `RawProxy`
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
{
|
|
225
|
+
pattern: ProxyPattern
|
|
226
|
+
targets: ResolvedTarget[] // 1 entry except for diamonds
|
|
227
|
+
beacon?: string // only for eip-1967-beacon
|
|
228
|
+
admin?: string // only for eip-1967 when admin slot is set
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `EnrichedTarget`
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
{
|
|
236
|
+
address: string
|
|
237
|
+
selectors?: string[]
|
|
238
|
+
abi?: unknown[]
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### `Proxy`
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
{
|
|
246
|
+
pattern: ProxyPattern
|
|
247
|
+
targets: EnrichedTarget[]
|
|
248
|
+
beacon?: string
|
|
249
|
+
admin?: string
|
|
250
|
+
compositeAbi?: unknown[] // deduped by selector
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### `TargetEnrichment`
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
{ abi?: unknown[] }
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### `TargetEnricher`
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
type TargetEnricher = (address: string) => Promise<TargetEnrichment | null>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Design notes
|
|
267
|
+
|
|
268
|
+
- **Narrow scope** — detects patterns and composes ABIs; no opinion on documentation formats or richer per-target metadata.
|
|
269
|
+
- **Dependency-injected enrichment** — the factory wires I/O when you ask; the primitives work offline.
|
|
270
|
+
- **First-wins ABI dedup** — pass the most authoritative ABI first (e.g. main contract → impl, or main diamond → facets).
|
|
271
|
+
- **Single-hop resolution** — if a resolved implementation is itself a proxy, `detectProxy` does not recurse. Beacon stays supported as a defined two-step pattern.
|
|
272
|
+
- **Minimal runtime deps** — only `@noble/hashes` for keccak256.
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
export declare interface AbiFunctionLike {
|
|
2
|
+
type: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
inputs?: AbiParam[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export declare interface AbiParam {
|
|
8
|
+
type: string;
|
|
9
|
+
components?: AbiParam[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Combine multiple ABIs into a single composite. First-occurrence wins:
|
|
14
|
+
* - functions deduped by selector
|
|
15
|
+
* - events and errors deduped by `type:name(canonicalInputs)`
|
|
16
|
+
* - other entries (constructor, fallback, receive) kept as-is
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCompositeAbi(abis: unknown[][]): unknown[];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build the canonical signature for an ABI function/event/error entry,
|
|
22
|
+
* recursively expanding `tuple` into `(innerTypes)` while preserving any
|
|
23
|
+
* `[]` or `[N]` array suffix. This is the form hashed to produce selectors.
|
|
24
|
+
*/
|
|
25
|
+
export declare function canonicalSignature(fn: AbiFunctionLike): string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Compute the 4-byte selector for a canonical function signature.
|
|
29
|
+
* Example: `computeSelector('transfer(address,uint256)')` → `'0xa9059cbb'`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function computeSelector(signature: string): string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a proxy inspection client.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* const proxies = createProxies()
|
|
38
|
+
* const result = await proxies.fetch('https://rpc…', '0x…')
|
|
39
|
+
* if (result) console.log(result.pattern, result.targets)
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* Pass `config.enrich` to populate each target's ABI from any source
|
|
43
|
+
* (Sourcify, Etherscan, a local cache). Omit it to get raw targets with
|
|
44
|
+
* address + (optional) selectors only.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createProxies(config?: ProxiesConfig): ProxiesClient;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Literal on-chain facet tuple (`(address, bytes4[])`) — the shape returned by
|
|
50
|
+
* the ERC-2535 loupe `facets()` call. Use {@link decodeFacets} to parse it.
|
|
51
|
+
*/
|
|
52
|
+
export declare interface DecodedFacet {
|
|
53
|
+
facetAddress: string;
|
|
54
|
+
functionSelectors: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Decode the return value of `facets()` — type `(address, bytes4[])[]`.
|
|
59
|
+
* Throws {@link ProxiesDecodeError} on malformed input.
|
|
60
|
+
*/
|
|
61
|
+
export declare function decodeFacets(hex: string): DecodedFacet[];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Detect whether a contract implements ERC-2535 (Diamond) and return its
|
|
65
|
+
* facets as a {@link RawProxy}. Returns `null` if not a diamond.
|
|
66
|
+
*
|
|
67
|
+
* Strategy:
|
|
68
|
+
* 1. Try ERC-165 `supportsInterface(0x48e2b093)`.
|
|
69
|
+
* - Valid bool `true` → fetch and return facets
|
|
70
|
+
* - Valid bool `false` → definitively not a diamond (null)
|
|
71
|
+
* - Malformed / error → fall through to step 2
|
|
72
|
+
* 2. Probe `facets()` directly. If it returns a non-empty decoded array,
|
|
73
|
+
* treat the contract as a diamond.
|
|
74
|
+
*
|
|
75
|
+
* Zero-address facets (deleted selectors) are filtered out of the result.
|
|
76
|
+
*/
|
|
77
|
+
export declare function detectDiamond(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Detect an EIP-1167 minimal proxy (clone) from runtime bytecode.
|
|
81
|
+
*
|
|
82
|
+
* Matches the canonical 45-byte runtime:
|
|
83
|
+
* `363d3d373d3d3d363d73<20-byte impl>5af43d82803e903d91602b57fd5bf3`
|
|
84
|
+
*
|
|
85
|
+
* Does NOT match the "optimized" variants (e.g. push-based relays) that some
|
|
86
|
+
* tooling emits — this is the standards-compliant shape per EIP-1167.
|
|
87
|
+
*
|
|
88
|
+
* Returns `null` when the bytecode does not match.
|
|
89
|
+
*/
|
|
90
|
+
export declare function detectEip1167(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Detect an EIP-1822 UUPS proxy.
|
|
94
|
+
*
|
|
95
|
+
* Reads the PROXIABLE slot (`keccak256('PROXIABLE')`). Returns `null` when the
|
|
96
|
+
* slot is empty or malformed.
|
|
97
|
+
*
|
|
98
|
+
* Note: most modern UUPS proxies use the EIP-1967 slot instead — this detector
|
|
99
|
+
* only catches the older EIP-1822 convention.
|
|
100
|
+
*/
|
|
101
|
+
export declare function detectEip1822(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Detect an EIP-1967 transparent or UUPS proxy.
|
|
105
|
+
*
|
|
106
|
+
* Reads the implementation slot (`0x3608…3bc3`). If non-zero, reads the admin
|
|
107
|
+
* slot in parallel and attaches it when present.
|
|
108
|
+
*
|
|
109
|
+
* Returns `null` when the implementation slot is empty or malformed.
|
|
110
|
+
*/
|
|
111
|
+
export declare function detectEip1967(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Detect an EIP-1967 beacon proxy.
|
|
115
|
+
*
|
|
116
|
+
* Reads the beacon slot (`0xa3f0…750d`); if non-zero, calls `implementation()`
|
|
117
|
+
* on the beacon contract. Returns `null` when the slot is empty or the beacon
|
|
118
|
+
* call fails.
|
|
119
|
+
*/
|
|
120
|
+
export declare function detectEip1967Beacon(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Detect an EIP-897 delegate proxy by calling `implementation()` as a view
|
|
124
|
+
* function.
|
|
125
|
+
*
|
|
126
|
+
* Last-resort detector — any contract with a public `implementation()` view
|
|
127
|
+
* returning a non-zero address will match. Only reached when all other
|
|
128
|
+
* pattern detectors have returned `null`.
|
|
129
|
+
*
|
|
130
|
+
* Returns `null` when the call reverts or returns zero.
|
|
131
|
+
*/
|
|
132
|
+
export declare function detectEip897(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Detect a Gnosis Safe proxy — the singleton (implementation) address is
|
|
136
|
+
* stored at storage slot 0.
|
|
137
|
+
*
|
|
138
|
+
* Returns `null` when slot 0 is empty or not address-shaped.
|
|
139
|
+
*
|
|
140
|
+
* Cheap, but has a higher false-positive surface than the standard slots
|
|
141
|
+
* because any contract may use storage slot 0. Detector priority puts this
|
|
142
|
+
* after all EIP-standard patterns to keep the match rate clean.
|
|
143
|
+
*/
|
|
144
|
+
export declare function detectGnosisSafe(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Detect any supported proxy pattern at `address`.
|
|
148
|
+
*
|
|
149
|
+
* Tries the built-in detectors in priority order (`eip-2535-diamond → eip-1967
|
|
150
|
+
* → eip-1967-beacon → eip-1822 → eip-1167 → gnosis-safe → eip-897`). Returns
|
|
151
|
+
* the first match, or `null` when no pattern matches.
|
|
152
|
+
*
|
|
153
|
+
* Each detector runs with error isolation — an RPC failure in one pattern does
|
|
154
|
+
* not poison the probe for subsequent ones.
|
|
155
|
+
*
|
|
156
|
+
* Single-hop only: if the resolved implementation is itself a proxy, the
|
|
157
|
+
* result describes the direct pattern only. Re-run detection on the resolved
|
|
158
|
+
* implementation to chain manually.
|
|
159
|
+
*/
|
|
160
|
+
export declare function detectProxy(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<RawProxy | null>;
|
|
161
|
+
|
|
162
|
+
/** ERC-2535 Diamond Loupe `IDiamondLoupe` interface ID. */
|
|
163
|
+
export declare const DIAMOND_LOUPE_INTERFACE_ID = "0x48e2b093";
|
|
164
|
+
|
|
165
|
+
/** Prefix preceding the 20-byte implementation address in an EIP-1167 minimal proxy. */
|
|
166
|
+
export declare const EIP1167_BYTECODE_PREFIX = "363d3d373d3d3d363d73";
|
|
167
|
+
|
|
168
|
+
/** Suffix following the 20-byte implementation address in an EIP-1167 minimal proxy. */
|
|
169
|
+
export declare const EIP1167_BYTECODE_SUFFIX = "5af43d82803e903d91602b57fd5bf3";
|
|
170
|
+
|
|
171
|
+
/** EIP-1822 UUPS PROXIABLE slot: `keccak256('PROXIABLE')`. */
|
|
172
|
+
export declare const EIP1822_PROXIABLE_SLOT = "0xc5f16f0fcc7e328891200cdca4c1c57b2b360c12265401510d42209b5829f8e2";
|
|
173
|
+
|
|
174
|
+
/** EIP-1967 admin slot: `keccak256('eip1967.proxy.admin') - 1`. */
|
|
175
|
+
export declare const EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
|
|
176
|
+
|
|
177
|
+
/** EIP-1967 beacon slot: `keccak256('eip1967.proxy.beacon') - 1`. */
|
|
178
|
+
export declare const EIP1967_BEACON_SLOT = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50";
|
|
179
|
+
|
|
180
|
+
/** EIP-1967 implementation slot: `keccak256('eip1967.proxy.implementation') - 1`. */
|
|
181
|
+
export declare const EIP1967_IMPL_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
182
|
+
|
|
183
|
+
export declare interface EnrichedTarget {
|
|
184
|
+
address: string;
|
|
185
|
+
/** Selector scope for diamond facets; undefined for single-impl proxies (all selectors). */
|
|
186
|
+
selectors?: string[];
|
|
187
|
+
/**
|
|
188
|
+
* ABI for this target. Filtered to `selectors` for diamonds; full implementation
|
|
189
|
+
* ABI for single-impl proxies.
|
|
190
|
+
*/
|
|
191
|
+
abi?: unknown[];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Apply an enricher to each resolved target, producing an `EnrichedTarget[]`.
|
|
196
|
+
*
|
|
197
|
+
* Enricher errors are swallowed per-target — one bad fetch does not fail the
|
|
198
|
+
* whole resolution. Pass `null` to skip enrichment entirely (targets will
|
|
199
|
+
* carry only `address` + `selectors`).
|
|
200
|
+
*
|
|
201
|
+
* When a target has `selectors` defined (diamond facets), the returned ABI is
|
|
202
|
+
* filtered to those selectors. When `selectors` is undefined (any single-impl
|
|
203
|
+
* proxy pattern), the full implementation ABI is passed through untouched.
|
|
204
|
+
*/
|
|
205
|
+
export declare function enrichTargets(targets: ResolvedTarget[], enrich: TargetEnricher | null): Promise<EnrichedTarget[]>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Minimal JSON-RPC `eth_call` client. Invokes a view call on the given address
|
|
209
|
+
* at the latest block and returns the raw hex result.
|
|
210
|
+
*/
|
|
211
|
+
export declare function ethCall(rpc: string, to: string, data: string, fetchFn: typeof globalThis.fetch): Promise<string>;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Minimal JSON-RPC `eth_getCode` client. Returns the deployed runtime bytecode
|
|
215
|
+
* at the latest block.
|
|
216
|
+
*/
|
|
217
|
+
export declare function ethGetCode(rpc: string, address: string, fetchFn: typeof globalThis.fetch): Promise<string>;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Minimal JSON-RPC `eth_getStorageAt` client. Reads a single 32-byte storage
|
|
221
|
+
* slot at the latest block.
|
|
222
|
+
*/
|
|
223
|
+
export declare function ethGetStorageAt(rpc: string, address: string, slot: string, fetchFn: typeof globalThis.fetch): Promise<string>;
|
|
224
|
+
|
|
225
|
+
/** ERC-2535 `facets()` — returns `(address, bytes4[])[]`. */
|
|
226
|
+
export declare const FACETS_SELECTOR = "0x7a0ed627";
|
|
227
|
+
|
|
228
|
+
export declare interface FetchProxyOptions {
|
|
229
|
+
/**
|
|
230
|
+
* Override the client's default enricher for this call. Pass `false` to
|
|
231
|
+
* disable enrichment entirely (address + selectors only).
|
|
232
|
+
*/
|
|
233
|
+
enrich?: TargetEnricher | false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Keep all non-function ABI entries; keep function entries whose computed
|
|
238
|
+
* selector is in the provided selector set. Verified facet contracts may
|
|
239
|
+
* declare extra functions not actually mounted on the diamond — this trims them.
|
|
240
|
+
*/
|
|
241
|
+
export declare function filterAbiBySelectors(abi: unknown[], selectors: string[]): unknown[];
|
|
242
|
+
|
|
243
|
+
/** `implementation()` — used by EIP-897 proxies and EIP-1967 beacons. */
|
|
244
|
+
export declare const IMPLEMENTATION_SELECTOR = "0x5c60da1b";
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Parse a 32-byte ABI-encoded address (right-padded). Returns the lowercase
|
|
248
|
+
* `0x` address, or `null` if the payload is not a well-formed address (wrong
|
|
249
|
+
* length, or non-zero high bytes).
|
|
250
|
+
*/
|
|
251
|
+
export declare function parseAddress(hex: string): string | null;
|
|
252
|
+
|
|
253
|
+
export declare interface ProxiesClient {
|
|
254
|
+
/**
|
|
255
|
+
* Detect the proxy pattern and resolve its target(s). Returns `null` if the
|
|
256
|
+
* contract is not a recognised proxy.
|
|
257
|
+
*/
|
|
258
|
+
detect: (rpc: string, address: string) => Promise<RawProxy | null>;
|
|
259
|
+
/**
|
|
260
|
+
* Detect + enrich. Returns `null` when the contract is not a recognised proxy.
|
|
261
|
+
* Uses the config-level enricher by default; override per-call via `options.enrich`.
|
|
262
|
+
*/
|
|
263
|
+
fetch: (rpc: string, address: string, options?: FetchProxyOptions) => Promise<Proxy_2 | null>;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export declare interface ProxiesConfig {
|
|
267
|
+
/**
|
|
268
|
+
* Default enricher used by `fetch` when no per-call enricher is passed.
|
|
269
|
+
* Receives each target address and returns whatever ABI the caller can find.
|
|
270
|
+
* Leave unset to return targets with address + selectors only.
|
|
271
|
+
*/
|
|
272
|
+
enrich?: TargetEnricher;
|
|
273
|
+
/** Custom fetch function. Default: `globalThis.fetch`. */
|
|
274
|
+
fetch?: typeof globalThis.fetch;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Raised when the on-chain `facets()` return value cannot be decoded. */
|
|
278
|
+
export declare class ProxiesDecodeError extends ProxiesError {
|
|
279
|
+
constructor(message: string, options?: ErrorOptions);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export declare class ProxiesError extends Error {
|
|
283
|
+
constructor(message: string, options?: ErrorOptions);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Raised when a JSON-RPC request fails at the transport level. */
|
|
287
|
+
export declare class ProxiesFetchError extends ProxiesError {
|
|
288
|
+
readonly status: number;
|
|
289
|
+
constructor(message: string, details: {
|
|
290
|
+
status: number;
|
|
291
|
+
}, options?: ErrorOptions);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
declare interface Proxy_2 {
|
|
295
|
+
pattern: ProxyPattern;
|
|
296
|
+
targets: EnrichedTarget[];
|
|
297
|
+
beacon?: string;
|
|
298
|
+
admin?: string;
|
|
299
|
+
/** Composite ABI across targets, deduped by selector (first-wins). */
|
|
300
|
+
compositeAbi?: unknown[];
|
|
301
|
+
}
|
|
302
|
+
export { Proxy_2 as Proxy }
|
|
303
|
+
|
|
304
|
+
/** Discriminator for all supported proxy patterns. */
|
|
305
|
+
export declare type ProxyPattern = 'eip-2535-diamond' | 'eip-1967' | 'eip-1967-beacon' | 'eip-1822' | 'eip-1167' | 'gnosis-safe' | 'eip-897';
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Raw on-chain detection result. Contains a pattern discriminator and the
|
|
309
|
+
* resolved target(s). Single-impl proxies carry exactly one target; diamonds
|
|
310
|
+
* carry one per facet.
|
|
311
|
+
*/
|
|
312
|
+
export declare interface RawProxy {
|
|
313
|
+
pattern: ProxyPattern;
|
|
314
|
+
targets: ResolvedTarget[];
|
|
315
|
+
/** EIP-1967 beacon address (only set for `eip-1967-beacon`). */
|
|
316
|
+
beacon?: string;
|
|
317
|
+
/** EIP-1967 admin address (only set for `eip-1967` when the admin slot is non-zero). */
|
|
318
|
+
admin?: string;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* A resolved target behind a proxy — an implementation address, with optional
|
|
323
|
+
* selector scope.
|
|
324
|
+
*
|
|
325
|
+
* - `selectors: string[]` — this target serves exactly these selectors (diamond facets).
|
|
326
|
+
* - `selectors: undefined` — this target receives all calls (every other proxy pattern).
|
|
327
|
+
*/
|
|
328
|
+
export declare interface ResolvedTarget {
|
|
329
|
+
address: string;
|
|
330
|
+
selectors?: string[];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** ERC-165 `supportsInterface(bytes4)`. */
|
|
334
|
+
export declare const SUPPORTS_INTERFACE_SELECTOR = "0x01ffc9a7";
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* User-supplied per-target enricher. Receives a target's address and returns
|
|
338
|
+
* its ABI. Return `null` for unknown targets — errors are swallowed per-target
|
|
339
|
+
* so one bad fetch does not fail the whole resolution.
|
|
340
|
+
*
|
|
341
|
+
* The package is intentionally narrow here: it only composes ABIs. For
|
|
342
|
+
* richer per-target metadata, use {@link ProxiesClient.detect} and enrich
|
|
343
|
+
* the raw targets yourself.
|
|
344
|
+
*/
|
|
345
|
+
export declare type TargetEnricher = (address: string) => Promise<TargetEnrichment | null>;
|
|
346
|
+
|
|
347
|
+
export declare interface TargetEnrichment {
|
|
348
|
+
abi?: unknown[];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
352
|
+
|
|
353
|
+
export { }
|