@volga-sh/evm-ghostcall 0.0.2 → 0.0.4
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 +51 -28
- package/dist/sdk/abi.d.ts +39 -0
- package/dist/sdk/abi.d.ts.map +1 -0
- package/dist/sdk/abi.js +20 -0
- package/dist/sdk/abi.js.map +1 -0
- package/dist/sdk/generated/initcode.d.ts +1 -1
- package/dist/sdk/generated/initcode.d.ts.map +1 -1
- package/dist/sdk/generated/initcode.js +1 -1
- package/dist/sdk/generated/initcode.js.map +1 -1
- package/dist/sdk/index.d.ts +55 -282
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +70 -308
- package/dist/sdk/index.js.map +1 -1
- package/package.json +5 -4
- package/src/Ghostcall.yul +28 -131
package/dist/sdk/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { validate as isAddress } from "ox/Address";
|
|
2
|
+
import { size as hexSize, validate as isHex } from "ox/Hex";
|
|
3
|
+
import { prepareAbiCall, } from "./abi.js";
|
|
1
4
|
import { ghostcallInitcode } from "./generated/initcode.js";
|
|
2
|
-
/**
|
|
3
|
-
* Error thrown when a strict Ghostcall batch encounters a failed subcall.
|
|
4
|
-
*/
|
|
5
|
+
/** A disallowed failure, with its zero-based index, executed call, and revert data. */
|
|
5
6
|
class GhostcallSubcallError extends Error {
|
|
6
7
|
index;
|
|
7
8
|
call;
|
|
@@ -12,135 +13,60 @@ class GhostcallSubcallError extends Error {
|
|
|
12
13
|
this.index = index;
|
|
13
14
|
this.call = call;
|
|
14
15
|
this.result = result;
|
|
15
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
const
|
|
18
|
+
const encodedCallHeaderSize = 22;
|
|
19
19
|
const encodedHeaderHexLength = 4;
|
|
20
20
|
const maxCalldataSize = 0xffff;
|
|
21
|
-
const encodedCallHeaderSize = 0x16;
|
|
22
21
|
const defaultMaxCreateInitcodeSize = 0xc000;
|
|
23
22
|
const successFlagMask = 0x8000;
|
|
24
23
|
const returnDataLengthMask = 0x7fff;
|
|
25
|
-
const bundledInitcodeSize =
|
|
24
|
+
const bundledInitcodeSize = hexSize(ghostcallInitcode);
|
|
26
25
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* The returned hex string already includes the bundled Ghostcall initcode followed
|
|
31
|
-
* by the compact binary payload for each subcall, so callers can pass it directly
|
|
32
|
-
* as the `data` field of an `eth_call` request without supplying a `to` address.
|
|
33
|
-
* Each encoded subcall entry uses the compact layout `[len(2)][target(20)][data]`.
|
|
34
|
-
*
|
|
35
|
-
* @param calls - Ordered list of subcalls to execute. Each entry becomes one
|
|
36
|
-
* Ghostcall payload segment in the same order it appears here.
|
|
37
|
-
* @param options - Optional encoding controls.
|
|
38
|
-
*
|
|
39
|
-
* @returns Full CREATE payload consisting of the bundled Ghostcall initcode plus
|
|
40
|
-
* the encoded call list.
|
|
41
|
-
*
|
|
42
|
-
* @throws {TypeError} If any call address or calldata value is not valid hex.
|
|
43
|
-
* @throws {RangeError} If any call data exceeds the protocol `uint16` length limit
|
|
44
|
-
* or if the full encoded CREATE payload would exceed the
|
|
45
|
-
* configured initcode size limit.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* const data = encodeCalls([
|
|
49
|
-
* {
|
|
50
|
-
* // USDC on Ethereum mainnet: balanceOf(Binance 14)
|
|
51
|
-
* to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
52
|
-
* data: "0x70a0823100000000000000000000000028c6c06298d514db089934071355e5743bf21d60",
|
|
53
|
-
* },
|
|
54
|
-
* {
|
|
55
|
-
* // WETH9 on Ethereum mainnet: totalSupply()
|
|
56
|
-
* to: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
57
|
-
* data: "0x18160ddd",
|
|
58
|
-
* },
|
|
59
|
-
* ]);
|
|
60
|
-
*
|
|
61
|
-
* // Later:
|
|
62
|
-
* // provider.request({ method: "eth_call", params: [{ data }, "latest"] })
|
|
26
|
+
* Build CREATE-style eth_call data: initcode followed by [length (2)][target (20)][data].
|
|
27
|
+
* Send without a `to` address. Invalid inputs throw TypeError; size limits throw RangeError.
|
|
63
28
|
*/
|
|
64
|
-
function encodeCalls(calls,
|
|
29
|
+
function encodeCalls(calls, { maxInitcodeBytes = defaultMaxCreateInitcodeSize, } = {}) {
|
|
30
|
+
if (!Number.isSafeInteger(maxInitcodeBytes) || maxInitcodeBytes < 0) {
|
|
31
|
+
throw new TypeError("options.maxInitcodeBytes must be a non-negative safe integer");
|
|
32
|
+
}
|
|
65
33
|
const encodedParts = [ghostcallInitcode.slice(2)];
|
|
66
|
-
const maxInitcodeBytes = resolveMaxInitcodeBytes(options.maxInitcodeBytes);
|
|
67
34
|
let totalEncodedSize = bundledInitcodeSize;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
35
|
+
const sizeError = `encoded Ghostcall initcode exceeds the ${maxInitcodeBytes}-byte CREATE initcode limit`;
|
|
36
|
+
if (totalEncodedSize > maxInitcodeBytes)
|
|
37
|
+
throw new RangeError(sizeError);
|
|
71
38
|
for (const [index, call] of calls.entries()) {
|
|
72
39
|
assertAddress(call.to, `calls[${index}].to`);
|
|
73
40
|
const calldata = assertHex(call.data, `calls[${index}].data`);
|
|
74
|
-
const calldataSize =
|
|
41
|
+
const calldataSize = hexSize(calldata);
|
|
75
42
|
if (calldataSize > maxCalldataSize) {
|
|
76
43
|
throw new RangeError(`calls[${index}].data exceeds the ${maxCalldataSize}-byte calldata limit`);
|
|
77
44
|
}
|
|
78
45
|
totalEncodedSize += encodedCallHeaderSize + calldataSize;
|
|
79
|
-
if (totalEncodedSize > maxInitcodeBytes)
|
|
80
|
-
throw new RangeError(
|
|
81
|
-
|
|
82
|
-
encodedParts.push(calldataSize.toString(16).padStart(4, "0"));
|
|
83
|
-
encodedParts.push(call.to.slice(2));
|
|
84
|
-
encodedParts.push(calldata.slice(2));
|
|
46
|
+
if (totalEncodedSize > maxInitcodeBytes)
|
|
47
|
+
throw new RangeError(sizeError);
|
|
48
|
+
encodedParts.push(calldataSize.toString(16).padStart(4, "0"), call.to.slice(2), calldata.slice(2));
|
|
85
49
|
}
|
|
86
50
|
return `0x${encodedParts.join("")}`;
|
|
87
51
|
}
|
|
88
52
|
/**
|
|
89
|
-
*
|
|
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
|
-
* ]);
|
|
53
|
+
* Execute a raw batch in order. Failed calls throw GhostcallSubcallError unless
|
|
54
|
+
* their entry sets allowFailure. Provider errors pass through unchanged.
|
|
128
55
|
*/
|
|
129
56
|
async function aggregateCalls(provider, calls, options) {
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
assertAddress(
|
|
136
|
-
ethCall.from =
|
|
137
|
-
}
|
|
138
|
-
if (
|
|
139
|
-
ethCall.gas = assertHexQuantity(
|
|
140
|
-
}
|
|
57
|
+
const { from, gas, blockTag } = options?.ethCall ?? {};
|
|
58
|
+
const ethCall = {
|
|
59
|
+
data: encodeCalls(calls, options ?? {}),
|
|
60
|
+
};
|
|
61
|
+
if (from !== undefined) {
|
|
62
|
+
assertAddress(from, "options.ethCall.from");
|
|
63
|
+
ethCall.from = from;
|
|
64
|
+
}
|
|
65
|
+
if (gas !== undefined)
|
|
66
|
+
ethCall.gas = assertHexQuantity(gas, "options.ethCall.gas");
|
|
141
67
|
const result = await provider.request({
|
|
142
68
|
method: "eth_call",
|
|
143
|
-
params: [ethCall, blockTag],
|
|
69
|
+
params: [ethCall, normalizeBlockTag(blockTag ?? "latest")],
|
|
144
70
|
});
|
|
145
71
|
const entries = decodeResults(assertHex(result, "eth_call result"));
|
|
146
72
|
if (entries.length !== calls.length) {
|
|
@@ -155,243 +81,79 @@ async function aggregateCalls(provider, calls, options) {
|
|
|
155
81
|
return entries;
|
|
156
82
|
}
|
|
157
83
|
/**
|
|
158
|
-
*
|
|
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
|
-
* ]);
|
|
84
|
+
* Execute ABI-described calls or raw calls with custom decoders. Each tuple position
|
|
85
|
+
* retains its result type. Any failed call throws; encoding/decoding errors pass through.
|
|
202
86
|
*/
|
|
203
87
|
async function aggregateDecodedCalls(provider, calls, options) {
|
|
204
|
-
const
|
|
88
|
+
const preparedCalls = calls.map((call) => call.abi === undefined ? call : prepareAbiCall(call));
|
|
89
|
+
const entries = await aggregateCalls(provider, preparedCalls, options);
|
|
205
90
|
return entries.map((entry, index) => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
91
|
+
// aggregateCalls has checked the count. Hidden allowFailure fields still cannot
|
|
92
|
+
// bypass this guard and send failed returndata to a success-only decoder.
|
|
93
|
+
const call = preparedCalls[index];
|
|
94
|
+
if (!entry.success)
|
|
95
|
+
throw new GhostcallSubcallError(index, call, entry);
|
|
96
|
+
return call.decodeResult(entry.returnData, entry, index);
|
|
209
97
|
});
|
|
210
98
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Decodes the packed result blob returned by Ghostcall.
|
|
213
|
-
*
|
|
214
|
-
* Each decoded entry corresponds to exactly one subcall in the original batch and
|
|
215
|
-
* preserves the original ordering. The SDK intentionally returns raw result bytes
|
|
216
|
-
* rather than ABI-decoding them so higher-level callers can apply their own
|
|
217
|
-
* decoding and failure policy.
|
|
218
|
-
*
|
|
219
|
-
* @param data - Raw bytes returned by Ghostcall, typically the direct result of a
|
|
220
|
-
* CREATE-style `eth_call`.
|
|
221
|
-
*
|
|
222
|
-
* @returns Ordered list of decoded Ghostcall result entries. Returns an empty
|
|
223
|
-
* array for `0x`.
|
|
224
|
-
*
|
|
225
|
-
* @throws {TypeError} If the provided data is not valid hex, if a result header is
|
|
226
|
-
* truncated, or if an entry body is shorter than advertised.
|
|
227
|
-
*
|
|
228
|
-
* @example
|
|
229
|
-
* const results = decodeResults("0x8002cafe0004deadbeef");
|
|
230
|
-
*
|
|
231
|
-
* console.log(results);
|
|
232
|
-
* // [
|
|
233
|
-
* // { success: true, returnData: "0xcafe" },
|
|
234
|
-
* // { success: false, returnData: "0xdeadbeef" }
|
|
235
|
-
* // ]
|
|
236
|
-
*/
|
|
99
|
+
/** Decode ordered [success bit | uint15 length][returndata] entries. Reject malformed data. */
|
|
237
100
|
function decodeResults(data) {
|
|
238
|
-
const
|
|
239
|
-
if (normalizedData === "0x") {
|
|
240
|
-
return [];
|
|
241
|
-
}
|
|
101
|
+
const encodedData = assertHex(data, "data").slice(2);
|
|
242
102
|
const results = [];
|
|
243
|
-
const encodedData = normalizedData.slice(2);
|
|
244
103
|
let cursor = 0;
|
|
245
104
|
while (cursor < encodedData.length) {
|
|
246
105
|
if (cursor + encodedHeaderHexLength > encodedData.length) {
|
|
247
106
|
throw new TypeError("Truncated Ghostcall response header");
|
|
248
107
|
}
|
|
249
108
|
const header = Number.parseInt(encodedData.slice(cursor, cursor + encodedHeaderHexLength), 16);
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const nextCursor = cursor + encodedHeaderHexLength;
|
|
253
|
-
const returnDataEnd = nextCursor + returnDataSize * 2;
|
|
109
|
+
cursor += encodedHeaderHexLength;
|
|
110
|
+
const returnDataEnd = cursor + (header & returnDataLengthMask) * 2;
|
|
254
111
|
if (returnDataEnd > encodedData.length) {
|
|
255
112
|
throw new TypeError("Truncated Ghostcall response body");
|
|
256
113
|
}
|
|
257
114
|
results.push({
|
|
258
|
-
success,
|
|
259
|
-
returnData: `0x${encodedData.slice(
|
|
115
|
+
success: (header & successFlagMask) !== 0,
|
|
116
|
+
returnData: `0x${encodedData.slice(cursor, returnDataEnd)}`,
|
|
260
117
|
});
|
|
261
118
|
cursor = returnDataEnd;
|
|
262
119
|
}
|
|
263
120
|
return results;
|
|
264
121
|
}
|
|
265
|
-
/**
|
|
266
|
-
* Validates that a value is a canonical 20-byte hex address.
|
|
267
|
-
*
|
|
268
|
-
* @param value - Unknown input to validate.
|
|
269
|
-
* @param label - Field name used in thrown error messages.
|
|
270
|
-
*
|
|
271
|
-
* @throws {TypeError} If the value is not valid `0x`-prefixed hex or is not
|
|
272
|
-
* exactly 20 bytes long.
|
|
273
|
-
*
|
|
274
|
-
* @internal
|
|
275
|
-
*/
|
|
276
122
|
function assertAddress(value, label) {
|
|
277
|
-
|
|
278
|
-
if (normalizedValue.length !== addressHexLength + 2) {
|
|
123
|
+
if (typeof value !== "string" || !isAddress(value, { strict: false })) {
|
|
279
124
|
throw new TypeError(`${label} must be a 20-byte hex string`);
|
|
280
125
|
}
|
|
281
126
|
}
|
|
282
|
-
/**
|
|
283
|
-
* Validates that a value is an even-length `0x`-prefixed hex string.
|
|
284
|
-
*
|
|
285
|
-
* @param value - Unknown input to validate.
|
|
286
|
-
* @param label - Field name used in thrown error messages.
|
|
287
|
-
*
|
|
288
|
-
* @returns The validated value narrowed to {@link Hex}.
|
|
289
|
-
*
|
|
290
|
-
* @throws {TypeError} If the value is not a string, lacks the `0x` prefix, has an
|
|
291
|
-
* odd number of hex characters, or contains non-hex digits.
|
|
292
|
-
*
|
|
293
|
-
* @internal
|
|
294
|
-
*/
|
|
295
127
|
function assertHex(value, label) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (!value.startsWith("0x")) {
|
|
300
|
-
throw new TypeError(`${label} must start with 0x`);
|
|
301
|
-
}
|
|
302
|
-
const rawValue = value.slice(2);
|
|
303
|
-
if (rawValue.length % 2 !== 0) {
|
|
304
|
-
throw new TypeError(`${label} must have an even number of hex characters`);
|
|
305
|
-
}
|
|
306
|
-
if (!/^[0-9a-fA-F]*$/.test(rawValue)) {
|
|
307
|
-
throw new TypeError(`${label} must contain only hexadecimal characters`);
|
|
128
|
+
// ox checks prefix/digits; the wire format additionally requires whole bytes.
|
|
129
|
+
if (!isHex(value, { strict: true }) || value.length % 2 !== 0) {
|
|
130
|
+
throw new TypeError(`${label} must be an even-length 0x-prefixed hex string`);
|
|
308
131
|
}
|
|
309
132
|
return value;
|
|
310
133
|
}
|
|
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
134
|
function assertHexQuantity(value, label) {
|
|
322
|
-
if (typeof value !== "string"
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
if (!/^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$/.test(value)) {
|
|
135
|
+
if (typeof value !== "string" ||
|
|
136
|
+
!/^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$/.test(value)) {
|
|
326
137
|
throw new TypeError(`${label} must be a 0x-prefixed hex quantity`);
|
|
327
138
|
}
|
|
328
139
|
return value;
|
|
329
140
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Returns the byte length of a validated hex string.
|
|
387
|
-
*
|
|
388
|
-
* @param value - Validated hex string.
|
|
389
|
-
* @returns Number of bytes represented by {@link value}.
|
|
390
|
-
*
|
|
391
|
-
* @internal
|
|
392
|
-
*/
|
|
393
|
-
function byteLength(value) {
|
|
394
|
-
return (value.length - 2) / 2;
|
|
141
|
+
function normalizeBlockTag(value) {
|
|
142
|
+
if (typeof value === "bigint" ||
|
|
143
|
+
(typeof value === "number" && Number.isSafeInteger(value))) {
|
|
144
|
+
if (value >= 0)
|
|
145
|
+
return `0x${value.toString(16)}`;
|
|
146
|
+
}
|
|
147
|
+
else if (typeof value === "string" &&
|
|
148
|
+
value.length > 0 &&
|
|
149
|
+
!/^-\d+$/.test(value)) {
|
|
150
|
+
if (/^\d+$/.test(value))
|
|
151
|
+
return `0x${BigInt(value).toString(16)}`;
|
|
152
|
+
return /^0x/i.test(value)
|
|
153
|
+
? assertHexQuantity(`0x${value.slice(2)}`, "options.ethCall.blockTag")
|
|
154
|
+
: value;
|
|
155
|
+
}
|
|
156
|
+
throw new TypeError("options.ethCall.blockTag must be a non-negative safe integer, bigint, or non-empty string");
|
|
395
157
|
}
|
|
396
158
|
export { aggregateCalls, aggregateDecodedCalls, decodeResults, encodeCalls, GhostcallSubcallError, };
|
|
397
159
|
//# 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": "Batch EVM
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Batch EVM contract reads without deploying a Multicall contract.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -48,11 +48,12 @@
|
|
|
48
48
|
"test:watch": "node --disable-warning=ExperimentalWarning --experimental-strip-types --test --watch test/*.test.ts",
|
|
49
49
|
"typecheck": "tsc --project tsconfig.json"
|
|
50
50
|
},
|
|
51
|
-
"dependencies": {
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"ox": "0.9.6"
|
|
53
|
+
},
|
|
52
54
|
"devDependencies": {
|
|
53
55
|
"@biomejs/biome": "2.4.12",
|
|
54
56
|
"@safe-global/mock-contract": "4.1.0",
|
|
55
|
-
"ox": "0.9.6",
|
|
56
57
|
"@types/node": "24.3.1",
|
|
57
58
|
"typescript": "5.9.2"
|
|
58
59
|
}
|