@volga-sh/evm-ghostcall 0.0.1 → 0.0.2
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/README.md +47 -284
- package/dist/sdk/index.d.ts +129 -68
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +216 -45
- package/dist/sdk/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
# ghostcall
|
|
2
2
|
|
|
3
|
-
`ghostcall`
|
|
3
|
+
`ghostcall` batches EVM blockchain reads without deployment dependencies.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
appended payload. The EVM executes that initcode exactly as if it were deploying a contract, but
|
|
7
|
-
because the transport is `eth_call`, nothing is persisted. Whatever the initcode `RETURN`s comes
|
|
8
|
-
back as the RPC result.
|
|
5
|
+
## Documentation
|
|
9
6
|
|
|
10
|
-
The
|
|
7
|
+
The docs live at [ghostcall.volga.sh](https://ghostcall.volga.sh).
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
Start there for installation, examples, the API reference, protocol details, and endpoint limit notes.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @volga-sh/evm-ghostcall
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
This example uses viem for the EIP-1193-compatible client and ABI helpers. Install it with `npm install viem` if your app does not already use it.
|
|
13
20
|
|
|
14
21
|
```ts
|
|
15
|
-
import {
|
|
22
|
+
import { aggregateDecodedCalls } from "@volga-sh/evm-ghostcall";
|
|
16
23
|
import {
|
|
17
24
|
createPublicClient,
|
|
18
25
|
decodeFunctionResult,
|
|
@@ -22,299 +29,55 @@ import {
|
|
|
22
29
|
} from "viem";
|
|
23
30
|
import { mainnet } from "viem/chains";
|
|
24
31
|
|
|
25
|
-
const erc20Abi = parseAbi([
|
|
26
|
-
"function balanceOf(address account) view returns (uint256)",
|
|
27
|
-
"function allowance(address owner, address spender) view returns (uint256)",
|
|
28
|
-
]);
|
|
29
|
-
|
|
30
|
-
const token = "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
|
|
31
|
-
const owner = "0x1111111111111111111111111111111111111111";
|
|
32
|
-
const spender = "0x2222222222222222222222222222222222222222";
|
|
33
|
-
|
|
34
32
|
const client = createPublicClient({
|
|
35
33
|
chain: mainnet,
|
|
36
34
|
transport: http(),
|
|
37
35
|
});
|
|
38
36
|
|
|
39
|
-
const [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
decodeFunctionResult({
|
|
51
|
-
abi: erc20Abi,
|
|
52
|
-
functionName: "balanceOf",
|
|
53
|
-
data,
|
|
54
|
-
}),
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
to: token,
|
|
58
|
-
data: encodeFunctionData({
|
|
37
|
+
const erc20Abi = parseAbi(["function totalSupply() view returns (uint256)"]);
|
|
38
|
+
|
|
39
|
+
const [totalSupply] = await aggregateDecodedCalls(client, [
|
|
40
|
+
{
|
|
41
|
+
to: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
42
|
+
data: encodeFunctionData({
|
|
43
|
+
abi: erc20Abi,
|
|
44
|
+
functionName: "totalSupply",
|
|
45
|
+
}),
|
|
46
|
+
decodeResult: (returnData) =>
|
|
47
|
+
decodeFunctionResult({
|
|
59
48
|
abi: erc20Abi,
|
|
60
|
-
functionName: "
|
|
61
|
-
|
|
49
|
+
functionName: "totalSupply",
|
|
50
|
+
data: returnData,
|
|
62
51
|
}),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
abi: erc20Abi,
|
|
66
|
-
functionName: "allowance",
|
|
67
|
-
data,
|
|
68
|
-
}),
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
{ results: "decoded" },
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
console.log({
|
|
75
|
-
balance,
|
|
76
|
-
allowance,
|
|
77
|
-
});
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Why this works
|
|
81
|
-
|
|
82
|
-
- `eth_call` without a `to` field executes the supplied `data` as CREATE initcode.
|
|
83
|
-
- Initcode can read caller-appended bytes from its own code using `CODECOPY`.
|
|
84
|
-
- Initcode can perform ordinary external calls, pack the returned bytes into memory, and `RETURN` them.
|
|
85
|
-
- Returned bytes are still subject to CREATE limits because the client treats them as would-be
|
|
86
|
-
runtime bytecode.
|
|
87
|
-
|
|
88
|
-
## Development stack
|
|
89
|
-
|
|
90
|
-
The repository now uses a minimal TypeScript-based test stack:
|
|
91
|
-
|
|
92
|
-
- Foundry for contract compilation and `anvil`
|
|
93
|
-
- Node's built-in [`node:test`](https://nodejs.org/api/test.html) runner
|
|
94
|
-
- Node's built-in TypeScript stripping for test execution
|
|
95
|
-
- [`ox`](https://www.npmjs.com/package/ox) for JSON-RPC, ABI, hex, and byte utilities
|
|
96
|
-
- [`@safe-global/mock-contract`](https://www.npmjs.com/package/@safe-global/mock-contract) for configurable mock-call behavior
|
|
97
|
-
|
|
98
|
-
That keeps the dependency footprint small while giving us a stable place to grow ABI-heavy tests.
|
|
99
|
-
|
|
100
|
-
## TypeScript SDK
|
|
101
|
-
|
|
102
|
-
Install the SDK from npm:
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
npm install @volga-sh/evm-ghostcall
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
The repository also includes a minimal internal-first TypeScript SDK in
|
|
109
|
-
[`src/sdk/index.ts`](src/sdk/index.ts).
|
|
110
|
-
|
|
111
|
-
It intentionally exposes only the small protocol surface:
|
|
112
|
-
|
|
113
|
-
- `encodeCalls(calls)` bundles the canonical Ghostcall initcode and returns the full CREATE-style `eth_call` data blob.
|
|
114
|
-
- `decodeResults(data)` parses the packed Ghostcall response format into `{ success, returnData }` entries.
|
|
115
|
-
- `aggregateCalls(provider, calls, options?)` sends the CREATE-style `eth_call` through an EIP-1193 `request` provider, decodes the packed response, and optionally runs each call's `decodeResult` callback.
|
|
116
|
-
|
|
117
|
-
`encodeCalls` fails fast if any subcall exceeds the `uint16` calldata limit or if the full
|
|
118
|
-
encoded CREATE payload would exceed the EVM initcode size ceiling.
|
|
119
|
-
|
|
120
|
-
`aggregateCalls` treats `allowFailure` as an SDK-side policy. Failed subcalls reject by default,
|
|
121
|
-
matching Multicall3-style strict batches, while calls marked `allowFailure: true` are returned as
|
|
122
|
-
ordinary `{ success: false, returnData }` entries.
|
|
123
|
-
|
|
124
|
-
The SDK has no ABI helpers and no runtime artifact reads. To ABI-decode successful entries, pass
|
|
125
|
-
`decodeResult` callbacks that call the ABI library already used by the application. By default,
|
|
126
|
-
`aggregateCalls` returns result entries. Pass `{ results: "decoded" }` to return decoded values
|
|
127
|
-
directly.
|
|
128
|
-
|
|
129
|
-
## Current scope
|
|
130
|
-
|
|
131
|
-
This implementation is intentionally focused on the smallest SDK-first variant:
|
|
132
|
-
|
|
133
|
-
- zero-value `CALL` for subcalls
|
|
134
|
-
- packed binary input instead of ABI encoding
|
|
135
|
-
- packed binary output instead of ABI encoding
|
|
136
|
-
- always-return result entries for every subcall
|
|
137
|
-
- SDK-enforced strict failure policy instead of engine-enforced batch reverts
|
|
138
|
-
|
|
139
|
-
That keeps the initcode small, auditable, and easy to extend.
|
|
140
|
-
|
|
141
|
-
## Why not a naive Solidity constructor
|
|
142
|
-
|
|
143
|
-
A straightforward deployless design is to write a Solidity constructor that:
|
|
144
|
-
|
|
145
|
-
- accepts an ABI-encoded array of calls,
|
|
146
|
-
- executes them in the constructor, and
|
|
147
|
-
- rewrites constructor memory so the returned bytes look like a normal ABI-encoded multicall result.
|
|
148
|
-
|
|
149
|
-
That approach works, but this project intentionally uses a lower-level Yul program instead.
|
|
150
|
-
|
|
151
|
-
Advantages of the current design:
|
|
152
|
-
|
|
153
|
-
- smaller base program, because it avoids Solidity's constructor scaffolding and generic ABI decoding,
|
|
154
|
-
- a tighter wire format, because both requests and responses use a compact custom binary layout instead of full ABI encoding,
|
|
155
|
-
- less compiler coupling, because the batching logic does not depend on Solidity memory-layout assumptions inside constructor-generated code.
|
|
156
|
-
|
|
157
|
-
In practice, this means less initcode to ship on every request, fewer bytes on the wire, and a design that is easier to reason about at the EVM level.
|
|
158
|
-
|
|
159
|
-
## Input format
|
|
160
|
-
|
|
161
|
-
The caller sends:
|
|
162
|
-
|
|
163
|
-
```text
|
|
164
|
-
<compiled ghostcall initcode><payload>
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Payload layout:
|
|
168
|
-
|
|
169
|
-
```text
|
|
170
|
-
N bytes repeated call entries
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Each call entry:
|
|
174
|
-
|
|
175
|
-
```text
|
|
176
|
-
2 bytes calldata length (big-endian uint16)
|
|
177
|
-
20 bytes target
|
|
178
|
-
N bytes calldata
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Notes:
|
|
182
|
-
|
|
183
|
-
- Payload bytes are not normal calldata. They are appended after the compiled initcode and read via
|
|
184
|
-
`CODECOPY`.
|
|
185
|
-
- The length comes first on purpose. Ghostcall copies the 22-byte fixed header into scratch memory
|
|
186
|
-
at offset `0x0a`, so one `mload(0x00)` exposes the length in the high 2 non-zero bytes and the
|
|
187
|
-
target address in the low 20 bytes used by `CALL`.
|
|
188
|
-
- An empty payload is valid and returns an empty result blob.
|
|
189
|
-
- Per-call calldata is limited to `65535` bytes because the format uses `uint16`.
|
|
190
|
-
- The whole CREATE payload is still limited by the network/client initcode size ceiling.
|
|
191
|
-
|
|
192
|
-
## Output format
|
|
193
|
-
|
|
194
|
-
The program returns:
|
|
195
|
-
|
|
196
|
-
```text
|
|
197
|
-
N bytes repeated result entries
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Each result entry:
|
|
201
|
-
|
|
202
|
-
```text
|
|
203
|
-
2 bytes packed header
|
|
204
|
-
bit 15 = success flag
|
|
205
|
-
bits 0-14 = returndata length (big-endian uint15)
|
|
206
|
-
N bytes returndata
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Subcall failures are returned inline as ordinary result entries with `success = 0`.
|
|
210
|
-
|
|
211
|
-
The engine only reverts for malformed payloads or per-entry return-size violations, and those
|
|
212
|
-
top-level reverts are intentionally empty. The SDK is expected to validate payloads up front and
|
|
213
|
-
impose any higher-level "fail the whole batch" policy for callers that want it.
|
|
214
|
-
|
|
215
|
-
The packed result header can represent up to `32767` bytes of returndata per entry. On
|
|
216
|
-
Ethereum, EIP-170's returned-code limit is usually the stricter bound: CREATE-style execution
|
|
217
|
-
limits the whole response to `24,576` bytes, including the 2-byte header on each entry.
|
|
218
|
-
|
|
219
|
-
## Limits
|
|
220
|
-
|
|
221
|
-
The aggregate response is returned through CREATE-style execution, so clients still treat it as
|
|
222
|
-
would-be runtime code. Ghostcall does not impose its own aggregate response cap; the effective
|
|
223
|
-
ceiling comes from the chain, client, RPC provider, gas setting, and request-size policy.
|
|
224
|
-
|
|
225
|
-
Common reference points:
|
|
226
|
-
|
|
227
|
-
- Ethereum's EIP-170 returned-code limit is `24,576` bytes.
|
|
228
|
-
- Ethereum's EIP-3860 initcode limit is `49,152` bytes.
|
|
229
|
-
- Other chains may set different values. For example, Monad documents larger contract-code and
|
|
230
|
-
initcode limits.
|
|
231
|
-
|
|
232
|
-
Measure the endpoint you plan to use instead of assuming a consensus value. Provider-side request
|
|
233
|
-
limits can be lower than the chain limit.
|
|
234
|
-
|
|
235
|
-
## Benchmark limits
|
|
236
|
-
|
|
237
|
-
The repository includes a TypeScript benchmark for rough endpoint-specific measurements:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
npm run benchmark:limits -- --rpc-url "$RPC_URL" --mode raw
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
241
54
|
```
|
|
242
55
|
|
|
243
|
-
|
|
244
|
-
uses a realistic ERC-20 balance workload:
|
|
56
|
+
See the [Getting Started guide](https://ghostcall.volga.sh/getting-started/) for a complete viem example with ABI encoding and decoding.
|
|
245
57
|
|
|
246
|
-
|
|
247
|
-
npm run benchmark:limits -- \
|
|
248
|
-
--rpc-url "$RPC_URL" \
|
|
249
|
-
--mode balances \
|
|
250
|
-
--token "$TOKEN_ADDRESS" \
|
|
251
|
-
--owner "$OWNER_ADDRESS"
|
|
252
|
-
```
|
|
58
|
+
## API
|
|
253
59
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
60
|
+
- `aggregateDecodedCalls()` sends a strict batch and returns decoded values.
|
|
61
|
+
- `aggregateCalls()` sends a batch and returns raw `{ success, returnData }` entries.
|
|
62
|
+
- `encodeCalls()` builds the CREATE-style `eth_call` data payload.
|
|
63
|
+
- `decodeResults()` parses the packed ghostcall response.
|
|
258
64
|
|
|
259
|
-
|
|
65
|
+
Full reference: [API docs](https://ghostcall.volga.sh/api/).
|
|
260
66
|
|
|
261
|
-
|
|
262
|
-
- `--token` and `--owner`, repeatable or comma-separated
|
|
263
|
-
- `--block`, `--from`, `--gas`, and `--timeout-ms`
|
|
264
|
-
- `--max-calls`, `--max-initcode-bytes`, and `--max-runtime-bytes`
|
|
265
|
-
- `--json` for machine-readable output
|
|
67
|
+
## Development
|
|
266
68
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
```bash
|
|
69
|
+
```sh
|
|
270
70
|
npm install
|
|
71
|
+
npm run build:sdk
|
|
72
|
+
npm run test
|
|
73
|
+
npm run check
|
|
271
74
|
```
|
|
272
75
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
```bash
|
|
276
|
-
npm run build:contracts
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
The compiled artifacts are emitted into the standard Foundry artifact tree under `out/`.
|
|
280
|
-
That build step also refreshes the generated SDK initcode file at
|
|
281
|
-
[`src/sdk/generated/initcode.ts`](src/sdk/generated/initcode.ts).
|
|
282
|
-
|
|
283
|
-
## Test
|
|
76
|
+
Docs are built with Astro Starlight:
|
|
284
77
|
|
|
285
|
-
```
|
|
286
|
-
npm
|
|
78
|
+
```sh
|
|
79
|
+
npm run docs:dev
|
|
80
|
+
npm run docs:build
|
|
287
81
|
```
|
|
288
82
|
|
|
289
|
-
The
|
|
290
|
-
|
|
291
|
-
- compiles the contracts with Foundry,
|
|
292
|
-
- starts an ephemeral `anvil` instance automatically,
|
|
293
|
-
- deploys and configures `MockContract` from Foundry artifacts,
|
|
294
|
-
- encodes function calldata with `ox`,
|
|
295
|
-
- executes a CREATE-style `eth_call` against Ghostcall,
|
|
296
|
-
- dogfoods the provider-facing SDK aggregation helper,
|
|
297
|
-
- decodes both function return data and revert data with `ox`,
|
|
298
|
-
- verifies configurable success paths, calldata-vs-method precedence, inline failure entries, the empty-batch case, the CREATE request-size boundary, the CREATE return-size boundary, and top-level malformed-payload handling.
|
|
299
|
-
|
|
300
|
-
For static TypeScript checking:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
npm run typecheck
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## Design notes
|
|
307
|
-
|
|
308
|
-
The implementation chooses Yul over raw bytecode because it keeps the control flow legible while
|
|
309
|
-
still mapping one-to-one onto the EVM concepts that matter here:
|
|
310
|
-
|
|
311
|
-
- `dataoffset(...)` anchors the appended payload boundary
|
|
312
|
-
- `codecopy` streams headers and calldata directly from the appended payload
|
|
313
|
-
- the len-first header plus a `0x0a` scratch offset lets one `mload(0x00)` yield both calldata
|
|
314
|
-
length and the `CALL` address word without extra masking
|
|
315
|
-
- `call` executes each subcall with zero value
|
|
316
|
-
- `returndatacopy` packs the aggregate response into a compact binary format
|
|
317
|
-
- `return` hands the batch result back to RPC
|
|
318
|
-
|
|
319
|
-
That gives you a maintainable base version first, with a straightforward path to hand-optimizing
|
|
320
|
-
hot spots later if initcode size becomes the bottleneck.
|
|
83
|
+
The repository is hosted at [github.com/volga-sh/ghostcall](https://github.com/volga-sh/ghostcall).
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* SDK does not accept byte arrays or ABI fragments.
|
|
6
6
|
*/
|
|
7
7
|
type Hex = `0x${string}`;
|
|
8
|
+
/**
|
|
9
|
+
* Hex-encoded RPC quantity prefixed with `0x`.
|
|
10
|
+
*/
|
|
11
|
+
type HexQuantity = `0x${string}`;
|
|
12
|
+
/**
|
|
13
|
+
* Block reference accepted by the outer `eth_call`.
|
|
14
|
+
*/
|
|
15
|
+
type GhostcallBlockReference = string | number | bigint;
|
|
8
16
|
/**
|
|
9
17
|
* One Ghostcall subcall entry.
|
|
10
18
|
*/
|
|
@@ -35,39 +43,46 @@ type GhostcallAggregateCall = GhostcallCall & {
|
|
|
35
43
|
* a call does not explicitly opt into failure.
|
|
36
44
|
*/
|
|
37
45
|
allowFailure?: boolean;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* One Ghostcall subcall entry for decoded aggregate results.
|
|
49
|
+
*/
|
|
50
|
+
type GhostcallDecodedCall<TResult = unknown> = GhostcallCall & {
|
|
38
51
|
/**
|
|
39
|
-
*
|
|
52
|
+
* Decodes this call's successful return data.
|
|
40
53
|
*
|
|
41
54
|
* This is intentionally a caller-provided function so the SDK stays independent
|
|
42
55
|
* from ABI libraries while still letting callers plug in helpers such as
|
|
43
56
|
* `decodeFunctionResult` from viem or ox.
|
|
44
57
|
*/
|
|
45
|
-
decodeResult
|
|
58
|
+
decodeResult: GhostcallResultDecoder<TResult>;
|
|
46
59
|
};
|
|
47
60
|
/**
|
|
48
|
-
* One Ghostcall
|
|
61
|
+
* One successful Ghostcall result entry.
|
|
49
62
|
*/
|
|
50
|
-
type
|
|
63
|
+
type GhostcallSuccessResult = {
|
|
51
64
|
/**
|
|
52
|
-
*
|
|
65
|
+
* Indicates whether the underlying EVM `CALL` returned successfully.
|
|
66
|
+
*
|
|
67
|
+
* A `true` value means the target call returned successfully.
|
|
53
68
|
*/
|
|
54
|
-
|
|
69
|
+
success: true;
|
|
55
70
|
/**
|
|
56
|
-
*
|
|
71
|
+
* Raw return data produced by the target call.
|
|
57
72
|
*/
|
|
58
|
-
|
|
73
|
+
returnData: Hex;
|
|
59
74
|
};
|
|
60
75
|
/**
|
|
61
|
-
* One
|
|
76
|
+
* One failed Ghostcall result entry.
|
|
62
77
|
*/
|
|
63
|
-
type
|
|
78
|
+
type GhostcallFailedResult = {
|
|
64
79
|
/**
|
|
65
80
|
* Indicates whether the underlying EVM `CALL` returned successfully.
|
|
66
81
|
*
|
|
67
82
|
* A `false` value means the target call reverted or otherwise failed, but the
|
|
68
83
|
* Ghostcall batch itself still completed successfully.
|
|
69
84
|
*/
|
|
70
|
-
success:
|
|
85
|
+
success: false;
|
|
71
86
|
/**
|
|
72
87
|
* Raw return data produced by the target call.
|
|
73
88
|
*
|
|
@@ -76,46 +91,60 @@ type GhostcallResult = {
|
|
|
76
91
|
*/
|
|
77
92
|
returnData: Hex;
|
|
78
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* One decoded Ghostcall result entry.
|
|
96
|
+
*/
|
|
97
|
+
type GhostcallResult = GhostcallSuccessResult | GhostcallFailedResult;
|
|
79
98
|
/**
|
|
80
99
|
* Function used by {@link aggregateCalls} to turn raw successful return data into
|
|
81
100
|
* a caller-chosen value.
|
|
82
101
|
*/
|
|
83
|
-
type GhostcallResultDecoder<TResult> = (returnData: Hex, entry:
|
|
102
|
+
type GhostcallResultDecoder<TResult> = (returnData: Hex, entry: GhostcallSuccessResult, index: number) => TResult;
|
|
84
103
|
/**
|
|
85
|
-
*
|
|
104
|
+
* Error thrown when a strict Ghostcall batch encounters a failed subcall.
|
|
86
105
|
*/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
} ? TCall extends {
|
|
95
|
-
allowFailure: true;
|
|
96
|
-
} ? GhostcallDecodedResult<TResult> | GhostcallResult : GhostcallDecodedResult<TResult> : GhostcallResult;
|
|
97
|
-
type GhostcallAggregateResults<TCalls extends readonly GhostcallAggregateCall[]> = {
|
|
98
|
-
-readonly [Index in keyof TCalls]: GhostcallAggregateResult<TCalls[Index]>;
|
|
99
|
-
};
|
|
100
|
-
type GhostcallDecodedResults<TCalls extends readonly GhostcallDecodedAggregateCall[]> = {
|
|
106
|
+
declare class GhostcallSubcallError extends Error {
|
|
107
|
+
readonly index: number;
|
|
108
|
+
readonly call: GhostcallAggregateCall;
|
|
109
|
+
readonly result: GhostcallFailedResult;
|
|
110
|
+
constructor(index: number, call: GhostcallAggregateCall, result: GhostcallFailedResult);
|
|
111
|
+
}
|
|
112
|
+
type GhostcallDecodedResults<TCalls extends readonly GhostcallDecodedCall[]> = {
|
|
101
113
|
-readonly [Index in keyof TCalls]: TCalls[Index] extends {
|
|
102
114
|
decodeResult: GhostcallResultDecoder<infer TResult>;
|
|
103
115
|
} ? TResult : never;
|
|
104
116
|
};
|
|
105
|
-
type
|
|
117
|
+
type GhostcallEncodeOptions = {
|
|
106
118
|
/**
|
|
107
|
-
*
|
|
119
|
+
* Maximum allowed CREATE initcode size in bytes.
|
|
108
120
|
*
|
|
109
|
-
* Defaults to
|
|
110
|
-
* to return each call's decoded value directly.
|
|
121
|
+
* Defaults to Ethereum's EIP-3860 limit of `49,152` bytes.
|
|
111
122
|
*/
|
|
112
|
-
|
|
123
|
+
maxInitcodeBytes?: number;
|
|
113
124
|
};
|
|
114
|
-
type
|
|
125
|
+
type GhostcallEthCallOptions = {
|
|
126
|
+
/**
|
|
127
|
+
* Optional `from` address for the outer `eth_call`.
|
|
128
|
+
*/
|
|
129
|
+
from?: Hex;
|
|
115
130
|
/**
|
|
116
|
-
*
|
|
131
|
+
* Optional gas limit for the outer `eth_call`.
|
|
117
132
|
*/
|
|
118
|
-
|
|
133
|
+
gas?: HexQuantity;
|
|
134
|
+
/**
|
|
135
|
+
* Optional block tag, hex quantity, or block number for the outer `eth_call`.
|
|
136
|
+
*
|
|
137
|
+
* Decimal strings, numbers, and bigints are normalized to hex quantities.
|
|
138
|
+
* Defaults to `latest`.
|
|
139
|
+
*/
|
|
140
|
+
blockTag?: GhostcallBlockReference;
|
|
141
|
+
};
|
|
142
|
+
type GhostcallAggregateOptions = GhostcallEncodeOptions & {
|
|
143
|
+
/**
|
|
144
|
+
* Optional outer `eth_call` controls shared by {@link aggregateCalls} and
|
|
145
|
+
* {@link aggregateDecodedCalls}.
|
|
146
|
+
*/
|
|
147
|
+
ethCall?: GhostcallEthCallOptions;
|
|
119
148
|
};
|
|
120
149
|
/**
|
|
121
150
|
* Minimal EIP-1193 provider shape used by the SDK.
|
|
@@ -137,6 +166,7 @@ type EIP1193ProviderWithRequestFn = {
|
|
|
137
166
|
*
|
|
138
167
|
* @param calls - Ordered list of subcalls to execute. Each entry becomes one
|
|
139
168
|
* Ghostcall payload segment in the same order it appears here.
|
|
169
|
+
* @param options - Optional encoding controls.
|
|
140
170
|
*
|
|
141
171
|
* @returns Full CREATE payload consisting of the bundled Ghostcall initcode plus
|
|
142
172
|
* the encoded call list.
|
|
@@ -144,16 +174,18 @@ type EIP1193ProviderWithRequestFn = {
|
|
|
144
174
|
* @throws {TypeError} If any call address or calldata value is not valid hex.
|
|
145
175
|
* @throws {RangeError} If any call data exceeds the protocol `uint16` length limit
|
|
146
176
|
* or if the full encoded CREATE payload would exceed the
|
|
147
|
-
*
|
|
177
|
+
* configured initcode size limit.
|
|
148
178
|
*
|
|
149
179
|
* @example
|
|
150
180
|
* const data = encodeCalls([
|
|
151
181
|
* {
|
|
152
|
-
*
|
|
153
|
-
*
|
|
182
|
+
* // USDC on Ethereum mainnet: balanceOf(Binance 14)
|
|
183
|
+
* to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
184
|
+
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
154
185
|
* },
|
|
155
186
|
* {
|
|
156
|
-
*
|
|
187
|
+
* // WETH9 on Ethereum mainnet: totalSupply()
|
|
188
|
+
* to: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
157
189
|
* data: "0x18160ddd",
|
|
158
190
|
* },
|
|
159
191
|
* ]);
|
|
@@ -161,67 +193,96 @@ type EIP1193ProviderWithRequestFn = {
|
|
|
161
193
|
* // Later:
|
|
162
194
|
* // provider.request({ method: "eth_call", params: [{ data }, "latest"] })
|
|
163
195
|
*/
|
|
164
|
-
declare function encodeCalls(calls: readonly GhostcallCall[]): Hex;
|
|
196
|
+
declare function encodeCalls(calls: readonly GhostcallCall[], options?: GhostcallEncodeOptions): Hex;
|
|
165
197
|
/**
|
|
166
198
|
* Sends a Ghostcall batch with a CREATE-style `eth_call` and decodes the result.
|
|
167
199
|
*
|
|
168
200
|
* This is the provider-facing counterpart to {@link encodeCalls} and
|
|
169
201
|
* {@link decodeResults}. It sends the bundled Ghostcall initcode as the `data`
|
|
170
|
-
* field of `eth_call` without a `to` address, then returns
|
|
202
|
+
* field of `eth_call` without a `to` address, then returns raw result entries
|
|
171
203
|
* in the same order as the input calls.
|
|
172
204
|
*
|
|
173
205
|
* By default, any failed subcall makes this method reject. Set
|
|
174
206
|
* `allowFailure: true` on a call to receive that failed entry in the returned
|
|
175
|
-
* results instead.
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* decoded values.
|
|
207
|
+
* results instead. Use {@link aggregateDecodedCalls} when you want a strict batch
|
|
208
|
+
* that returns decoded values directly. Use `options.ethCall` to forward `from`,
|
|
209
|
+
* `gas`, or `blockTag` to the outer `eth_call`.
|
|
179
210
|
*
|
|
180
211
|
* @param provider - EIP-1193-compatible provider with a `request` method.
|
|
181
212
|
* @param calls - Ordered list of subcalls to execute.
|
|
182
|
-
* @param options - Optional
|
|
213
|
+
* @param options - Optional outer call and initcode controls.
|
|
183
214
|
*
|
|
184
|
-
* @returns Ordered
|
|
215
|
+
* @returns Ordered Ghostcall result entries.
|
|
185
216
|
*
|
|
186
217
|
* @throws {TypeError} If inputs are not valid Ghostcall call entries or if the
|
|
187
218
|
* provider returns a non-hex `eth_call` result.
|
|
188
|
-
* @throws {RangeError} If the encoded CREATE payload exceeds protocol or
|
|
189
|
-
*
|
|
190
|
-
* @throws {
|
|
191
|
-
*
|
|
219
|
+
* @throws {RangeError} If the encoded CREATE payload exceeds protocol or the
|
|
220
|
+
* configured CREATE initcode ceiling.
|
|
221
|
+
* @throws {GhostcallSubcallError} If a subcall fails without `allowFailure: true`.
|
|
222
|
+
* @throws {Error} If the response entry count does not match the request entry count.
|
|
192
223
|
*
|
|
193
224
|
* @example
|
|
194
225
|
* const results = await aggregateCalls(provider, [
|
|
195
226
|
* {
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* abi: erc20Abi,
|
|
200
|
-
* functionName: "balanceOf",
|
|
201
|
-
* data: returnData,
|
|
202
|
-
* }),
|
|
227
|
+
* // USDC on Ethereum mainnet: balanceOf(Binance 14)
|
|
228
|
+
* to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
229
|
+
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
203
230
|
* },
|
|
204
231
|
* {
|
|
205
|
-
*
|
|
232
|
+
* // WETH9 on Ethereum mainnet: totalSupply()
|
|
233
|
+
* to: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
206
234
|
* data: "0x18160ddd",
|
|
207
|
-
* allowFailure: true,
|
|
208
235
|
* },
|
|
209
236
|
* ]);
|
|
237
|
+
*/
|
|
238
|
+
declare function aggregateCalls(provider: EIP1193ProviderWithRequestFn, calls: readonly GhostcallAggregateCall[], options?: GhostcallAggregateOptions): Promise<GhostcallResult[]>;
|
|
239
|
+
/**
|
|
240
|
+
* Sends a strict Ghostcall batch and decodes each successful result entry.
|
|
210
241
|
*
|
|
211
|
-
*
|
|
242
|
+
* This is the decoded counterpart to {@link aggregateCalls}. It sends the bundled
|
|
243
|
+
* Ghostcall initcode as the `data` field of `eth_call` without a `to` address,
|
|
244
|
+
* then runs each call's `decodeResult` callback over the successful return data in
|
|
245
|
+
* the same order as the input calls.
|
|
246
|
+
*
|
|
247
|
+
* `aggregateDecodedCalls` is always strict. Its TypeScript input shape requires a
|
|
248
|
+
* `decodeResult` callback on every call and does not accept `allowFailure`.
|
|
249
|
+
* Any failed subcall rejects with {@link GhostcallSubcallError}. Use
|
|
250
|
+
* {@link aggregateCalls} if you need raw failed entries. Use `options.ethCall`
|
|
251
|
+
* to forward `from`, `gas`, or `blockTag` to the outer `eth_call`.
|
|
252
|
+
*
|
|
253
|
+
* @param provider - EIP-1193-compatible provider with a `request` method.
|
|
254
|
+
* @param calls - Ordered list of strict decoded subcalls to execute.
|
|
255
|
+
* @param options - Optional outer call and initcode controls.
|
|
256
|
+
*
|
|
257
|
+
* @returns Ordered list of decoded values.
|
|
258
|
+
*
|
|
259
|
+
* @throws {TypeError} If inputs are not valid Ghostcall call entries or if the
|
|
260
|
+
* provider returns a non-hex `eth_call` result.
|
|
261
|
+
* @throws {RangeError} If the encoded CREATE payload exceeds protocol or the
|
|
262
|
+
* configured CREATE initcode ceiling.
|
|
263
|
+
* @throws {GhostcallSubcallError} If any subcall fails.
|
|
264
|
+
* @throws {Error} If the response entry count does not match the request entry count.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* const erc20Abi = parseAbi([
|
|
268
|
+
* "function balanceOf(address account) view returns (uint256)",
|
|
269
|
+
* ]);
|
|
270
|
+
* const usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
|
|
271
|
+
* const owner = "0x28C6c06298d514Db089934071355E5743bf21d60";
|
|
272
|
+
*
|
|
273
|
+
* const [balance] = await aggregateDecodedCalls(provider, [
|
|
212
274
|
* {
|
|
213
|
-
* to:
|
|
214
|
-
* data: "
|
|
275
|
+
* to: usdc,
|
|
276
|
+
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
215
277
|
* decodeResult: (returnData) => decodeFunctionResult({
|
|
216
278
|
* abi: erc20Abi,
|
|
217
279
|
* functionName: "balanceOf",
|
|
218
280
|
* data: returnData,
|
|
219
281
|
* }),
|
|
220
282
|
* },
|
|
221
|
-
* ]
|
|
283
|
+
* ]);
|
|
222
284
|
*/
|
|
223
|
-
declare function
|
|
224
|
-
declare function aggregateCalls<const TCalls extends readonly GhostcallDecodedAggregateCall[]>(provider: EIP1193ProviderWithRequestFn, calls: TCalls, options: GhostcallDecodedAggregateOptions): Promise<GhostcallDecodedResults<TCalls>>;
|
|
285
|
+
declare function aggregateDecodedCalls<const TCalls extends readonly GhostcallDecodedCall<unknown>[]>(provider: EIP1193ProviderWithRequestFn, calls: TCalls, options?: GhostcallAggregateOptions): Promise<GhostcallDecodedResults<TCalls>>;
|
|
225
286
|
/**
|
|
226
287
|
* Decodes the packed result blob returned by Ghostcall.
|
|
227
288
|
*
|
|
@@ -249,6 +310,6 @@ declare function aggregateCalls<const TCalls extends readonly GhostcallDecodedAg
|
|
|
249
310
|
* // ]
|
|
250
311
|
*/
|
|
251
312
|
declare function decodeResults(data: Hex): GhostcallResult[];
|
|
252
|
-
export type { EIP1193ProviderWithRequestFn, GhostcallAggregateCall, GhostcallAggregateOptions,
|
|
253
|
-
export { aggregateCalls, decodeResults, encodeCalls };
|
|
313
|
+
export type { EIP1193ProviderWithRequestFn, GhostcallAggregateCall, GhostcallAggregateOptions, GhostcallBlockReference, GhostcallCall, GhostcallDecodedCall, GhostcallDecodedResults, GhostcallEncodeOptions, GhostcallEthCallOptions, GhostcallFailedResult, GhostcallResult, GhostcallResultDecoder, GhostcallSuccessResult, Hex, HexQuantity, };
|
|
314
|
+
export { aggregateCalls, aggregateDecodedCalls, decodeResults, encodeCalls, GhostcallSubcallError, };
|
|
254
315
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/sdk/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,KAAK,GAAG,GAAG,KAAK,MAAM,EAAE,CAAC;AAEzB;;GAEG;AACH,KAAK,aAAa,GAAG;IACpB;;OAEG;IACH,EAAE,EAAE,GAAG,CAAC;IAER;;;;;OAKG;IACH,IAAI,EAAE,GAAG,CAAC;CACV,CAAC;AAEF;;;;;GAKG;AACH,KAAK,sBAAsB,GAAG,aAAa,GAAG;IAC7C;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"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"}
|
package/dist/sdk/index.js
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { ghostcallInitcode } from "./generated/initcode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when a strict Ghostcall batch encounters a failed subcall.
|
|
4
|
+
*/
|
|
5
|
+
class GhostcallSubcallError extends Error {
|
|
6
|
+
index;
|
|
7
|
+
call;
|
|
8
|
+
result;
|
|
9
|
+
constructor(index, call, result) {
|
|
10
|
+
super(`Ghostcall subcall ${index} failed`);
|
|
11
|
+
this.name = "GhostcallSubcallError";
|
|
12
|
+
this.index = index;
|
|
13
|
+
this.call = call;
|
|
14
|
+
this.result = result;
|
|
15
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
2
18
|
const addressHexLength = 40;
|
|
3
19
|
const encodedHeaderHexLength = 4;
|
|
4
20
|
const maxCalldataSize = 0xffff;
|
|
5
21
|
const encodedCallHeaderSize = 0x16;
|
|
6
|
-
const
|
|
22
|
+
const defaultMaxCreateInitcodeSize = 0xc000;
|
|
7
23
|
const successFlagMask = 0x8000;
|
|
8
24
|
const returnDataLengthMask = 0x7fff;
|
|
9
25
|
const bundledInitcodeSize = byteLength(ghostcallInitcode);
|
|
@@ -18,6 +34,7 @@ const bundledInitcodeSize = byteLength(ghostcallInitcode);
|
|
|
18
34
|
*
|
|
19
35
|
* @param calls - Ordered list of subcalls to execute. Each entry becomes one
|
|
20
36
|
* Ghostcall payload segment in the same order it appears here.
|
|
37
|
+
* @param options - Optional encoding controls.
|
|
21
38
|
*
|
|
22
39
|
* @returns Full CREATE payload consisting of the bundled Ghostcall initcode plus
|
|
23
40
|
* the encoded call list.
|
|
@@ -25,16 +42,18 @@ const bundledInitcodeSize = byteLength(ghostcallInitcode);
|
|
|
25
42
|
* @throws {TypeError} If any call address or calldata value is not valid hex.
|
|
26
43
|
* @throws {RangeError} If any call data exceeds the protocol `uint16` length limit
|
|
27
44
|
* or if the full encoded CREATE payload would exceed the
|
|
28
|
-
*
|
|
45
|
+
* configured initcode size limit.
|
|
29
46
|
*
|
|
30
47
|
* @example
|
|
31
48
|
* const data = encodeCalls([
|
|
32
49
|
* {
|
|
33
|
-
*
|
|
34
|
-
*
|
|
50
|
+
* // USDC on Ethereum mainnet: balanceOf(Binance 14)
|
|
51
|
+
* to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
52
|
+
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
35
53
|
* },
|
|
36
54
|
* {
|
|
37
|
-
*
|
|
55
|
+
* // WETH9 on Ethereum mainnet: totalSupply()
|
|
56
|
+
* to: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
38
57
|
* data: "0x18160ddd",
|
|
39
58
|
* },
|
|
40
59
|
* ]);
|
|
@@ -42,9 +61,13 @@ const bundledInitcodeSize = byteLength(ghostcallInitcode);
|
|
|
42
61
|
* // Later:
|
|
43
62
|
* // provider.request({ method: "eth_call", params: [{ data }, "latest"] })
|
|
44
63
|
*/
|
|
45
|
-
function encodeCalls(calls) {
|
|
64
|
+
function encodeCalls(calls, options = {}) {
|
|
46
65
|
const encodedParts = [ghostcallInitcode.slice(2)];
|
|
66
|
+
const maxInitcodeBytes = resolveMaxInitcodeBytes(options.maxInitcodeBytes);
|
|
47
67
|
let totalEncodedSize = bundledInitcodeSize;
|
|
68
|
+
if (totalEncodedSize > maxInitcodeBytes) {
|
|
69
|
+
throw new RangeError(`encoded Ghostcall initcode exceeds the ${maxInitcodeBytes}-byte CREATE initcode limit`);
|
|
70
|
+
}
|
|
48
71
|
for (const [index, call] of calls.entries()) {
|
|
49
72
|
assertAddress(call.to, `calls[${index}].to`);
|
|
50
73
|
const calldata = assertHex(call.data, `calls[${index}].data`);
|
|
@@ -53,8 +76,8 @@ function encodeCalls(calls) {
|
|
|
53
76
|
throw new RangeError(`calls[${index}].data exceeds the ${maxCalldataSize}-byte calldata limit`);
|
|
54
77
|
}
|
|
55
78
|
totalEncodedSize += encodedCallHeaderSize + calldataSize;
|
|
56
|
-
if (totalEncodedSize >
|
|
57
|
-
throw new RangeError(`encoded Ghostcall initcode exceeds the ${
|
|
79
|
+
if (totalEncodedSize > maxInitcodeBytes) {
|
|
80
|
+
throw new RangeError(`encoded Ghostcall initcode exceeds the ${maxInitcodeBytes}-byte CREATE initcode limit`);
|
|
58
81
|
}
|
|
59
82
|
encodedParts.push(calldataSize.toString(16).padStart(4, "0"));
|
|
60
83
|
encodedParts.push(call.to.slice(2));
|
|
@@ -62,54 +85,128 @@ function encodeCalls(calls) {
|
|
|
62
85
|
}
|
|
63
86
|
return `0x${encodedParts.join("")}`;
|
|
64
87
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Sends a Ghostcall batch with a CREATE-style `eth_call` and decodes the result.
|
|
90
|
+
*
|
|
91
|
+
* This is the provider-facing counterpart to {@link encodeCalls} and
|
|
92
|
+
* {@link decodeResults}. It sends the bundled Ghostcall initcode as the `data`
|
|
93
|
+
* field of `eth_call` without a `to` address, then returns raw result entries
|
|
94
|
+
* in the same order as the input calls.
|
|
95
|
+
*
|
|
96
|
+
* By default, any failed subcall makes this method reject. Set
|
|
97
|
+
* `allowFailure: true` on a call to receive that failed entry in the returned
|
|
98
|
+
* results instead. Use {@link aggregateDecodedCalls} when you want a strict batch
|
|
99
|
+
* that returns decoded values directly. Use `options.ethCall` to forward `from`,
|
|
100
|
+
* `gas`, or `blockTag` to the outer `eth_call`.
|
|
101
|
+
*
|
|
102
|
+
* @param provider - EIP-1193-compatible provider with a `request` method.
|
|
103
|
+
* @param calls - Ordered list of subcalls to execute.
|
|
104
|
+
* @param options - Optional outer call and initcode controls.
|
|
105
|
+
*
|
|
106
|
+
* @returns Ordered Ghostcall result entries.
|
|
107
|
+
*
|
|
108
|
+
* @throws {TypeError} If inputs are not valid Ghostcall call entries or if the
|
|
109
|
+
* provider returns a non-hex `eth_call` result.
|
|
110
|
+
* @throws {RangeError} If the encoded CREATE payload exceeds protocol or the
|
|
111
|
+
* configured CREATE initcode ceiling.
|
|
112
|
+
* @throws {GhostcallSubcallError} If a subcall fails without `allowFailure: true`.
|
|
113
|
+
* @throws {Error} If the response entry count does not match the request entry count.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* const results = await aggregateCalls(provider, [
|
|
117
|
+
* {
|
|
118
|
+
* // USDC on Ethereum mainnet: balanceOf(Binance 14)
|
|
119
|
+
* to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
120
|
+
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
121
|
+
* },
|
|
122
|
+
* {
|
|
123
|
+
* // WETH9 on Ethereum mainnet: totalSupply()
|
|
124
|
+
* to: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
125
|
+
* data: "0x18160ddd",
|
|
126
|
+
* },
|
|
127
|
+
* ]);
|
|
128
|
+
*/
|
|
129
|
+
async function aggregateCalls(provider, calls, options) {
|
|
130
|
+
const resolvedOptions = options ?? {};
|
|
131
|
+
const data = encodeCalls(calls, resolvedOptions);
|
|
132
|
+
const ethCall = { data };
|
|
133
|
+
const blockTag = normalizeBlockTag(resolvedOptions.ethCall?.blockTag ?? "latest", "options.ethCall.blockTag");
|
|
134
|
+
if (resolvedOptions.ethCall?.from !== undefined) {
|
|
135
|
+
assertAddress(resolvedOptions.ethCall.from, "options.ethCall.from");
|
|
136
|
+
ethCall.from = resolvedOptions.ethCall.from;
|
|
137
|
+
}
|
|
138
|
+
if (resolvedOptions.ethCall?.gas !== undefined) {
|
|
139
|
+
ethCall.gas = assertHexQuantity(resolvedOptions.ethCall.gas, "options.ethCall.gas");
|
|
77
140
|
}
|
|
78
|
-
const data = encodeCalls(calls);
|
|
79
141
|
const result = await provider.request({
|
|
80
142
|
method: "eth_call",
|
|
81
|
-
params: [
|
|
143
|
+
params: [ethCall, blockTag],
|
|
82
144
|
});
|
|
83
145
|
const entries = decodeResults(assertHex(result, "eth_call result"));
|
|
84
146
|
if (entries.length !== calls.length) {
|
|
85
147
|
throw new Error(`Ghostcall returned ${entries.length} result entries for ${calls.length} calls`);
|
|
86
148
|
}
|
|
87
149
|
for (const [index, entry] of entries.entries()) {
|
|
88
|
-
|
|
89
|
-
|
|
150
|
+
const call = calls[index];
|
|
151
|
+
if (!entry.success && call.allowFailure !== true) {
|
|
152
|
+
throw new GhostcallSubcallError(index, call, entry);
|
|
90
153
|
}
|
|
91
154
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
155
|
+
return entries;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Sends a strict Ghostcall batch and decodes each successful result entry.
|
|
159
|
+
*
|
|
160
|
+
* This is the decoded counterpart to {@link aggregateCalls}. It sends the bundled
|
|
161
|
+
* Ghostcall initcode as the `data` field of `eth_call` without a `to` address,
|
|
162
|
+
* then runs each call's `decodeResult` callback over the successful return data in
|
|
163
|
+
* the same order as the input calls.
|
|
164
|
+
*
|
|
165
|
+
* `aggregateDecodedCalls` is always strict. Its TypeScript input shape requires a
|
|
166
|
+
* `decodeResult` callback on every call and does not accept `allowFailure`.
|
|
167
|
+
* Any failed subcall rejects with {@link GhostcallSubcallError}. Use
|
|
168
|
+
* {@link aggregateCalls} if you need raw failed entries. Use `options.ethCall`
|
|
169
|
+
* to forward `from`, `gas`, or `blockTag` to the outer `eth_call`.
|
|
170
|
+
*
|
|
171
|
+
* @param provider - EIP-1193-compatible provider with a `request` method.
|
|
172
|
+
* @param calls - Ordered list of strict decoded subcalls to execute.
|
|
173
|
+
* @param options - Optional outer call and initcode controls.
|
|
174
|
+
*
|
|
175
|
+
* @returns Ordered list of decoded values.
|
|
176
|
+
*
|
|
177
|
+
* @throws {TypeError} If inputs are not valid Ghostcall call entries or if the
|
|
178
|
+
* provider returns a non-hex `eth_call` result.
|
|
179
|
+
* @throws {RangeError} If the encoded CREATE payload exceeds protocol or the
|
|
180
|
+
* configured CREATE initcode ceiling.
|
|
181
|
+
* @throws {GhostcallSubcallError} If any subcall fails.
|
|
182
|
+
* @throws {Error} If the response entry count does not match the request entry count.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* const erc20Abi = parseAbi([
|
|
186
|
+
* "function balanceOf(address account) view returns (uint256)",
|
|
187
|
+
* ]);
|
|
188
|
+
* const usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
|
|
189
|
+
* const owner = "0x28C6c06298d514Db089934071355E5743bf21d60";
|
|
190
|
+
*
|
|
191
|
+
* const [balance] = await aggregateDecodedCalls(provider, [
|
|
192
|
+
* {
|
|
193
|
+
* to: usdc,
|
|
194
|
+
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
195
|
+
* decodeResult: (returnData) => decodeFunctionResult({
|
|
196
|
+
* abi: erc20Abi,
|
|
197
|
+
* functionName: "balanceOf",
|
|
198
|
+
* data: returnData,
|
|
199
|
+
* }),
|
|
200
|
+
* },
|
|
201
|
+
* ]);
|
|
202
|
+
*/
|
|
203
|
+
async function aggregateDecodedCalls(provider, calls, options) {
|
|
204
|
+
const entries = await aggregateCalls(provider, calls, options);
|
|
205
|
+
return entries.map((entry, index) => {
|
|
206
|
+
const call = calls[index];
|
|
207
|
+
const successEntry = entry;
|
|
208
|
+
return call.decodeResult(successEntry.returnData, successEntry, index);
|
|
111
209
|
});
|
|
112
|
-
return decodedEntries;
|
|
113
210
|
}
|
|
114
211
|
/**
|
|
115
212
|
* Decodes the packed result blob returned by Ghostcall.
|
|
@@ -211,6 +308,80 @@ function assertHex(value, label) {
|
|
|
211
308
|
}
|
|
212
309
|
return value;
|
|
213
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Validates that a value is an RPC hex quantity.
|
|
313
|
+
*
|
|
314
|
+
* @param value - Unknown input to validate.
|
|
315
|
+
* @param label - Field name used in thrown error messages.
|
|
316
|
+
* @returns The validated value narrowed to {@link HexQuantity}.
|
|
317
|
+
* @throws {TypeError} If the value is not a valid `0x`-prefixed quantity.
|
|
318
|
+
*
|
|
319
|
+
* @internal
|
|
320
|
+
*/
|
|
321
|
+
function assertHexQuantity(value, label) {
|
|
322
|
+
if (typeof value !== "string") {
|
|
323
|
+
throw new TypeError(`${label} must be a hex quantity string`);
|
|
324
|
+
}
|
|
325
|
+
if (!/^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$/.test(value)) {
|
|
326
|
+
throw new TypeError(`${label} must be a 0x-prefixed hex quantity`);
|
|
327
|
+
}
|
|
328
|
+
return value;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Normalizes a block reference into the RPC shape expected by `eth_call`.
|
|
332
|
+
*
|
|
333
|
+
* @param value - Block reference to normalize.
|
|
334
|
+
* @param label - Field name used in thrown error messages.
|
|
335
|
+
* @returns Normalized block reference.
|
|
336
|
+
* @throws {TypeError} If the value is not a supported block reference.
|
|
337
|
+
*
|
|
338
|
+
* @internal
|
|
339
|
+
*/
|
|
340
|
+
function normalizeBlockTag(value, label) {
|
|
341
|
+
if (typeof value === "number") {
|
|
342
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
343
|
+
throw new TypeError(`${label} must be a non-negative safe integer, bigint, or non-empty string`);
|
|
344
|
+
}
|
|
345
|
+
return `0x${value.toString(16)}`;
|
|
346
|
+
}
|
|
347
|
+
if (typeof value === "bigint") {
|
|
348
|
+
if (value < 0n) {
|
|
349
|
+
throw new TypeError(`${label} must be a non-negative safe integer, bigint, or non-empty string`);
|
|
350
|
+
}
|
|
351
|
+
return `0x${value.toString(16)}`;
|
|
352
|
+
}
|
|
353
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
354
|
+
throw new TypeError(`${label} must be a non-negative safe integer, bigint, or non-empty string`);
|
|
355
|
+
}
|
|
356
|
+
if (/^-?[0-9]+$/.test(value)) {
|
|
357
|
+
if (value.startsWith("-")) {
|
|
358
|
+
throw new TypeError(`${label} must be a non-negative safe integer, bigint, or non-empty string`);
|
|
359
|
+
}
|
|
360
|
+
return `0x${BigInt(value).toString(16)}`;
|
|
361
|
+
}
|
|
362
|
+
if (value.startsWith("0x") || value.startsWith("0X")) {
|
|
363
|
+
return assertHexQuantity(`0x${value.slice(2)}`, label);
|
|
364
|
+
}
|
|
365
|
+
return value;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Resolves the active CREATE initcode ceiling.
|
|
369
|
+
*
|
|
370
|
+
* @param value - Optional caller override.
|
|
371
|
+
* @returns Active initcode ceiling in bytes.
|
|
372
|
+
* @throws {TypeError} If the override is not a non-negative safe integer.
|
|
373
|
+
*
|
|
374
|
+
* @internal
|
|
375
|
+
*/
|
|
376
|
+
function resolveMaxInitcodeBytes(value) {
|
|
377
|
+
if (value === undefined) {
|
|
378
|
+
return defaultMaxCreateInitcodeSize;
|
|
379
|
+
}
|
|
380
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
381
|
+
throw new TypeError("options.maxInitcodeBytes must be a non-negative safe integer");
|
|
382
|
+
}
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
214
385
|
/**
|
|
215
386
|
* Returns the byte length of a validated hex string.
|
|
216
387
|
*
|
|
@@ -222,5 +393,5 @@ function assertHex(value, label) {
|
|
|
222
393
|
function byteLength(value) {
|
|
223
394
|
return (value.length - 2) / 2;
|
|
224
395
|
}
|
|
225
|
-
export { aggregateCalls, decodeResults, encodeCalls };
|
|
396
|
+
export { aggregateCalls, aggregateDecodedCalls, decodeResults, encodeCalls, GhostcallSubcallError, };
|
|
226
397
|
//# sourceMappingURL=index.js.map
|
package/dist/sdk/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volga-sh/evm-ghostcall",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Batch EVM blockchain reads without deployment dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/volga-sh/ghostcall/issues"
|
|
13
13
|
},
|
|
14
|
-
"homepage": "https://
|
|
14
|
+
"homepage": "https://ghostcall.volga.sh",
|
|
15
15
|
"main": "./dist/sdk/index.js",
|
|
16
16
|
"types": "./dist/sdk/index.d.ts",
|
|
17
17
|
"exports": {
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
"build:sdk": "npm run build:contracts && tsc --project tsconfig.build.json",
|
|
35
35
|
"check": "biome check .",
|
|
36
36
|
"check:fix": "biome check --write .",
|
|
37
|
+
"docs:build": "npm run build --prefix docs",
|
|
38
|
+
"docs:dev": "npm run dev --prefix docs",
|
|
39
|
+
"docs:preview": "npm run preview --prefix docs",
|
|
37
40
|
"check:sdk:initcode": "git diff --exit-code -- src/sdk/generated/initcode.ts",
|
|
38
41
|
"format": "biome format --write .",
|
|
39
42
|
"format:check": "biome format .",
|