@callcium/sdk 0.11.1 → 0.13.0
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 +2 -2
- package/dist/index.d.ts +683 -622
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2297 -2051
- package/dist/index.js.map +1 -1
- package/package.json +13 -10
- package/src/bytes.ts +9 -25
- package/src/calldata-reader.ts +3 -3
- package/src/constants.ts +156 -82
- package/src/constraint.ts +61 -33
- package/src/descriptor-coder.ts +139 -125
- package/src/descriptor.ts +82 -292
- package/src/errors.ts +38 -30
- package/src/hint.ts +237 -0
- package/src/index.ts +30 -12
- package/src/operators.ts +173 -242
- package/src/path.ts +7 -32
- package/src/policy-builder.ts +205 -190
- package/src/policy-coder.ts +1160 -536
- package/src/policy-enforcer.ts +499 -488
- package/src/policy-validator.ts +563 -1045
- package/src/signature.ts +7 -11
- package/src/type-desc.ts +12 -12
- package/src/types.ts +232 -136
- package/src/validation-issue.ts +78 -465
package/src/descriptor-coder.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { concatBytes } from "@noble/hashes/utils.js";
|
|
2
|
+
|
|
1
3
|
import { readU16 } from "./bytes";
|
|
2
|
-
import { DescriptorFormat as DF
|
|
4
|
+
import { DescriptorFormat as DF } from "./constants";
|
|
3
5
|
import { Descriptor, readNode } from "./descriptor";
|
|
4
6
|
import { CallciumError } from "./errors";
|
|
5
|
-
import {
|
|
7
|
+
import { lookupTypeCode } from "./operators";
|
|
6
8
|
import { address, array, bool, bytes, bytesN, function_, intN, string_, tuple, uintN } from "./type-desc";
|
|
7
9
|
|
|
8
|
-
import type {
|
|
10
|
+
import type { DecodedDescriptor, DecodedNode } from "./types";
|
|
9
11
|
|
|
10
12
|
///////////////////////////////////////////////////////////////////////////
|
|
11
13
|
// Splitting helpers
|
|
12
14
|
///////////////////////////////////////////////////////////////////////////
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
|
-
* Collect the positions of depth-0 commas in input[start..end).
|
|
16
|
-
* Commas inside `()` or `[]` are skipped.
|
|
17
|
+
* Collect the positions of depth-0 commas in input[start..end). Commas inside `()` or `[]` are skipped.
|
|
17
18
|
*/
|
|
18
19
|
function commaPositions(input: string, start: number, end: number): number[] {
|
|
19
20
|
const positions: number[] = [];
|
|
@@ -47,8 +48,8 @@ const MAX_TYPE_STRING_NUMBER = 256 ** DF.ARRAY_LENGTH_SIZE - 1;
|
|
|
47
48
|
/**
|
|
48
49
|
* Parse a decimal integer from input[start..end).
|
|
49
50
|
*
|
|
50
|
-
* Rejects an empty span, a non-digit, a leading zero, and a value too wide for the fields a type
|
|
51
|
-
*
|
|
51
|
+
* Rejects an empty span, a non-digit, a leading zero, and a value too wide for the fields a type string feeds. Width
|
|
52
|
+
* and length validity are out of scope here.
|
|
52
53
|
*/
|
|
53
54
|
function parseUint(input: string, start: number, end: number): number {
|
|
54
55
|
if (start >= end) {
|
|
@@ -77,8 +78,7 @@ function parseUint(input: string, start: number, end: number): number {
|
|
|
77
78
|
///////////////////////////////////////////////////////////////////////////
|
|
78
79
|
|
|
79
80
|
/**
|
|
80
|
-
* Parse a tuple literal `(field0,field1,...)`
|
|
81
|
-
* `start` and the substring to parse ends before `end`.
|
|
81
|
+
* Parse a tuple literal `(field0,field1,...)` whose opening `(` is at `start` and whose content ends before `end`.
|
|
82
82
|
*/
|
|
83
83
|
function parseTuple(input: string, start: number, end: number): Uint8Array {
|
|
84
84
|
if (input[start] !== "(") {
|
|
@@ -137,13 +137,20 @@ function parseBaseType(input: string, start: number, end: number): Uint8Array {
|
|
|
137
137
|
if (segment === "string") return string_();
|
|
138
138
|
if (segment.startsWith("(")) return parseTuple(input, start, end);
|
|
139
139
|
|
|
140
|
-
// A prefix with no width after it spells no type, so it falls through to the unknown-type report
|
|
141
|
-
//
|
|
142
|
-
if (segment.length > 4 && segment.startsWith("uint"))
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
// A prefix with no width after it spells no type, so it falls through to the unknown-type report rather than being
|
|
141
|
+
// read as a width that is not there. A width outside its type's range names no type either.
|
|
142
|
+
if (segment.length > 4 && segment.startsWith("uint")) {
|
|
143
|
+
const bits = parseUint(input, start + 4, end);
|
|
144
|
+
if (bits >= 8 && bits <= 256 && bits % 8 === 0) return uintN(bits);
|
|
145
|
+
} else if (segment.length > 3 && segment.startsWith("int")) {
|
|
146
|
+
const bits = parseUint(input, start + 3, end);
|
|
147
|
+
if (bits >= 8 && bits <= 256 && bits % 8 === 0) return intN(bits);
|
|
148
|
+
} else if (segment.length > 5 && segment.startsWith("bytes")) {
|
|
149
|
+
const length = parseUint(input, start + 5, end);
|
|
150
|
+
if (length >= 1 && length <= 32) return bytesN(length);
|
|
151
|
+
}
|
|
145
152
|
|
|
146
|
-
throw new CallciumError("UNKNOWN_TYPE", `
|
|
153
|
+
throw new CallciumError("UNKNOWN_TYPE", `Unknown type '${segment}'`);
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
///////////////////////////////////////////////////////////////////////////
|
|
@@ -153,16 +160,16 @@ function parseBaseType(input: string, start: number, end: number): Uint8Array {
|
|
|
153
160
|
/**
|
|
154
161
|
* Parse a type segment from input[start..end).
|
|
155
162
|
*
|
|
156
|
-
* Scans backward from end to collect all `[...]` suffixes, identifies the
|
|
157
|
-
*
|
|
163
|
+
* Scans backward from end to collect all `[...]` suffixes, identifies the base type extent, parses the base, then
|
|
164
|
+
* applies suffixes left-to-right.
|
|
158
165
|
*/
|
|
159
166
|
function parseType(input: string, start: number, end: number): Uint8Array {
|
|
160
167
|
if (start >= end) {
|
|
161
168
|
throw new CallciumError("MALFORMED_TYPE_STRING", "Empty type segment");
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
// Locate array suffixes by scanning backward, reading no length yet: the base names the type, so
|
|
165
|
-
//
|
|
171
|
+
// Locate array suffixes by scanning backward, reading no length yet: the base names the type, so an unrecognised one
|
|
172
|
+
// is reported before a suffix it carries is parsed.
|
|
166
173
|
const spans: Array<[number, number]> = [];
|
|
167
174
|
let baseEnd = end;
|
|
168
175
|
|
|
@@ -182,8 +189,7 @@ function parseType(input: string, start: number, end: number): Uint8Array {
|
|
|
182
189
|
|
|
183
190
|
let desc = parseBaseType(input, start, baseEnd);
|
|
184
191
|
|
|
185
|
-
// Apply suffixes left-to-right: the leftmost suffix is the
|
|
186
|
-
// A dynamic array spells no length at all.
|
|
192
|
+
// Apply suffixes left-to-right: the leftmost suffix is the innermost array. A dynamic array spells no length at all.
|
|
187
193
|
for (const [innerStart, innerEnd] of spans) {
|
|
188
194
|
desc = array(desc, innerStart === innerEnd ? undefined : parseUint(input, innerStart, innerEnd));
|
|
189
195
|
}
|
|
@@ -198,7 +204,7 @@ function parseType(input: string, start: number, end: number): Uint8Array {
|
|
|
198
204
|
/**
|
|
199
205
|
* Encode a comma-separated list of ABI type strings into a binary descriptor.
|
|
200
206
|
*
|
|
201
|
-
* @param typesCsv - Comma-separated ABI type strings,
|
|
207
|
+
* @param typesCsv - Comma-separated ABI type strings, for example `"address,uint256,(bool,bytes32)[],string"`.
|
|
202
208
|
* @returns Binary descriptor bytes starting with the version+paramCount header.
|
|
203
209
|
* @throws {CallciumError} With code `MALFORMED_TYPE_STRING` for malformed input.
|
|
204
210
|
* @throws {CallciumError} With code `UNKNOWN_TYPE` for unrecognised type names.
|
|
@@ -223,90 +229,30 @@ function fromTypes(typesCsv: string): Uint8Array {
|
|
|
223
229
|
if (paramDescs.length > DF.MAX_PARAMS) {
|
|
224
230
|
throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DF.MAX_PARAMS}`);
|
|
225
231
|
}
|
|
226
|
-
const
|
|
227
|
-
const result = new Uint8Array(DF.HEADER_SIZE + totalBytes);
|
|
228
|
-
result[0] = DF.VERSION;
|
|
229
|
-
result[1] = paramDescs.length;
|
|
230
|
-
let offset = DF.HEADER_SIZE;
|
|
231
|
-
for (const desc of paramDescs) {
|
|
232
|
-
result.set(desc, offset);
|
|
233
|
-
offset += desc.length;
|
|
234
|
-
}
|
|
232
|
+
const result = concatBytes(Uint8Array.of(DF.VERSION, paramDescs.length), ...paramDescs);
|
|
235
233
|
// Nesting depth is a property of the assembled descriptor, not of any single node.
|
|
236
234
|
decodeDescriptor(result);
|
|
237
235
|
return result;
|
|
238
236
|
}
|
|
239
237
|
|
|
240
|
-
///////////////////////////////////////////////////////////////////////////
|
|
241
|
-
// Inverse: descriptor bytes → type string
|
|
242
|
-
///////////////////////////////////////////////////////////////////////////
|
|
243
|
-
|
|
244
|
-
/** Reconstruct an ABI type string from a descriptor node at the given offset. */
|
|
245
|
-
function nodeToTypeString(desc: Uint8Array, offset: number): string {
|
|
246
|
-
const { typeCode } = Descriptor.inspect(desc, offset);
|
|
247
|
-
|
|
248
|
-
if (typeCode === TypeCode.TUPLE) {
|
|
249
|
-
const fieldCount = Descriptor.tupleFieldCount(desc, offset);
|
|
250
|
-
const fieldTypes: string[] = [];
|
|
251
|
-
let fieldOffset = offset + DF.TUPLE_HEADER_SIZE;
|
|
252
|
-
for (let i = 0; i < fieldCount; i++) {
|
|
253
|
-
fieldTypes.push(nodeToTypeString(desc, fieldOffset));
|
|
254
|
-
fieldOffset += Descriptor.nodeLength(desc, fieldOffset);
|
|
255
|
-
}
|
|
256
|
-
return `(${fieldTypes.join(",")})`;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (typeCode === TypeCode.STATIC_ARRAY) {
|
|
260
|
-
const length = Descriptor.staticArrayLength(desc, offset);
|
|
261
|
-
const elemType = nodeToTypeString(desc, Descriptor.arrayElementOffset(offset));
|
|
262
|
-
return `${elemType}[${length}]`;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (typeCode === TypeCode.DYNAMIC_ARRAY) {
|
|
266
|
-
const elemType = nodeToTypeString(desc, Descriptor.arrayElementOffset(offset));
|
|
267
|
-
return `${elemType}[]`;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return lookupTypeCode(typeCode).label;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Reconstruct a comma-separated list of ABI type strings from a binary descriptor.
|
|
275
|
-
*
|
|
276
|
-
* Inverse of `fromTypes`: `toTypes(fromTypes(s))` returns `s` for any valid input.
|
|
277
|
-
*
|
|
278
|
-
* @param desc - Binary descriptor bytes (with version+paramCount header).
|
|
279
|
-
* @returns Comma-separated ABI type strings, e.g. `"address,uint256,(bool,bytes32)[]"`.
|
|
280
|
-
* @throws {CallciumError} If the descriptor is malformed.
|
|
281
|
-
*/
|
|
282
|
-
function toTypes(desc: Uint8Array): string {
|
|
283
|
-
const count = Descriptor.paramCount(desc);
|
|
284
|
-
const types: string[] = [];
|
|
285
|
-
let offset = DF.HEADER_SIZE;
|
|
286
|
-
for (let i = 0; i < count; i++) {
|
|
287
|
-
types.push(nodeToTypeString(desc, offset));
|
|
288
|
-
offset += Descriptor.nodeLength(desc, offset);
|
|
289
|
-
}
|
|
290
|
-
return types.join(",");
|
|
291
|
-
}
|
|
292
|
-
|
|
293
238
|
///////////////////////////////////////////////////////////////////////////
|
|
294
239
|
// Descriptor binary decoder
|
|
295
240
|
///////////////////////////////////////////////////////////////////////////
|
|
296
241
|
|
|
297
|
-
/**
|
|
298
|
-
type ParseResult = {
|
|
242
|
+
/** A parsed descriptor node and the offset just past it. */
|
|
243
|
+
type ParseResult = { node: DecodedNode; next: number };
|
|
299
244
|
|
|
300
|
-
/** Recursively parse a single descriptor node starting at offset
|
|
301
|
-
function parseNode(data: Uint8Array, offset: number, depth: number): ParseResult {
|
|
245
|
+
/** Recursively parse a single descriptor node starting at offset, its span shifted by `base`. */
|
|
246
|
+
function parseNode(data: Uint8Array, offset: number, depth: number, base: number): ParseResult {
|
|
302
247
|
const { typeCode: code, staticWords, nodeLength } = readNode(data, offset);
|
|
303
|
-
const info =
|
|
248
|
+
const info = lookupTypeCode(code);
|
|
304
249
|
const isDynamic = staticWords === 0;
|
|
305
250
|
const staticSize = isDynamic ? 0 : staticWords * 32;
|
|
306
251
|
const nodeEnd = offset + nodeLength;
|
|
252
|
+
const span = { start: base + offset, end: base + nodeEnd };
|
|
307
253
|
|
|
308
254
|
if (info.typeClass === "elementary") {
|
|
309
|
-
return { typeCode: code, isDynamic, staticSize, next: nodeEnd };
|
|
255
|
+
return { node: { typeClass: "elementary", typeCode: code, isDynamic, staticSize, span }, next: nodeEnd };
|
|
310
256
|
}
|
|
311
257
|
|
|
312
258
|
// Only composites nest; a leaf below the deepest allowed composite is fine.
|
|
@@ -320,7 +266,7 @@ function parseNode(data: Uint8Array, offset: number, depth: number): ParseResult
|
|
|
320
266
|
|
|
321
267
|
if (info.typeClass === "tuple") {
|
|
322
268
|
const fieldCountOffset = offset + DF.TYPECODE_SIZE + DF.COMPOSITE_META_SIZE;
|
|
323
|
-
if (fieldCountOffset +
|
|
269
|
+
if (fieldCountOffset + DF.TUPLE_FIELDCOUNT_SIZE > data.length) {
|
|
324
270
|
throw new CallciumError("UNEXPECTED_END", "Incomplete tuple header", offset);
|
|
325
271
|
}
|
|
326
272
|
const fieldCount = readU16(data, fieldCountOffset);
|
|
@@ -335,17 +281,21 @@ function parseNode(data: Uint8Array, offset: number, depth: number): ParseResult
|
|
|
335
281
|
);
|
|
336
282
|
}
|
|
337
283
|
|
|
284
|
+
const fields: DecodedNode[] = [];
|
|
338
285
|
let cursor = offset + DF.TUPLE_HEADER_SIZE;
|
|
339
286
|
for (let i = 0; i < fieldCount; i++) {
|
|
340
|
-
|
|
287
|
+
const field = parseNode(data, cursor, depth + 1, base);
|
|
288
|
+
fields.push(field.node);
|
|
289
|
+
cursor = field.next;
|
|
341
290
|
}
|
|
342
291
|
|
|
343
|
-
return { typeCode: code, isDynamic, staticSize, next: nodeEnd };
|
|
292
|
+
return { node: { typeClass: "tuple", typeCode: code, isDynamic, staticSize, span, fields }, next: nodeEnd };
|
|
344
293
|
}
|
|
345
294
|
|
|
295
|
+
const element = parseNode(data, Descriptor.arrayElementOffset(offset), depth + 1, base);
|
|
296
|
+
|
|
346
297
|
if (info.typeClass === "staticArray") {
|
|
347
|
-
const
|
|
348
|
-
const lengthOffset = elemResult.next;
|
|
298
|
+
const lengthOffset = element.next;
|
|
349
299
|
if (lengthOffset + DF.ARRAY_LENGTH_SIZE > data.length) {
|
|
350
300
|
throw new CallciumError("UNEXPECTED_END", "Missing static array length suffix", offset);
|
|
351
301
|
}
|
|
@@ -361,25 +311,36 @@ function parseNode(data: Uint8Array, offset: number, depth: number): ParseResult
|
|
|
361
311
|
);
|
|
362
312
|
}
|
|
363
313
|
|
|
364
|
-
|
|
314
|
+
const node: DecodedNode = {
|
|
315
|
+
typeClass: "staticArray",
|
|
316
|
+
typeCode: code,
|
|
317
|
+
isDynamic,
|
|
318
|
+
staticSize,
|
|
319
|
+
span,
|
|
320
|
+
length,
|
|
321
|
+
element: element.node,
|
|
322
|
+
};
|
|
323
|
+
return { node, next: nodeEnd };
|
|
365
324
|
}
|
|
366
325
|
|
|
367
|
-
|
|
368
|
-
|
|
326
|
+
const node: DecodedNode = {
|
|
327
|
+
typeClass: "dynamicArray",
|
|
328
|
+
typeCode: code,
|
|
329
|
+
isDynamic: true,
|
|
330
|
+
staticSize: 0,
|
|
331
|
+
span,
|
|
332
|
+
element: element.node,
|
|
333
|
+
};
|
|
334
|
+
return { node, next: nodeEnd };
|
|
369
335
|
}
|
|
370
336
|
|
|
371
|
-
/**
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
params: DecodedParam[];
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
/** Decode a binary descriptor blob, returning its structural representation. */
|
|
382
|
-
export function decodeDescriptor(data: Uint8Array): { descriptor: DecodedDescriptor } {
|
|
337
|
+
/**
|
|
338
|
+
* Decode a binary descriptor blob, returning its structural representation.
|
|
339
|
+
* @param data - The descriptor bytes.
|
|
340
|
+
* @param base - Byte offset of the descriptor in the blob its spans locate.
|
|
341
|
+
* @throws {CallciumError} When the descriptor breaks a well-formedness invariant.
|
|
342
|
+
*/
|
|
343
|
+
export function decodeDescriptor(data: Uint8Array, base = 0): DecodedDescriptor {
|
|
383
344
|
if (data.length < 1) {
|
|
384
345
|
throw new CallciumError("MALFORMED_HEADER", "Descriptor is empty");
|
|
385
346
|
}
|
|
@@ -393,29 +354,21 @@ export function decodeDescriptor(data: Uint8Array): { descriptor: DecodedDescrip
|
|
|
393
354
|
throw new CallciumError("MALFORMED_HEADER", "Descriptor too short for header");
|
|
394
355
|
}
|
|
395
356
|
|
|
396
|
-
const params:
|
|
357
|
+
const params: DecodedNode[] = [];
|
|
397
358
|
let cursor: number = DF.HEADER_SIZE;
|
|
398
359
|
|
|
399
360
|
while (cursor < data.length) {
|
|
400
|
-
const {
|
|
361
|
+
const { node, next } = parseNode(data, cursor, 1, base);
|
|
401
362
|
|
|
402
363
|
if (params.length === DF.MAX_PARAMS) {
|
|
403
364
|
throw new CallciumError("TOO_MANY_PARAMS", `Descriptor exceeds the maximum of ${DF.MAX_PARAMS} top-level params`);
|
|
404
365
|
}
|
|
405
366
|
|
|
406
|
-
params.push(
|
|
407
|
-
index: params.length,
|
|
408
|
-
typeCode,
|
|
409
|
-
isDynamic,
|
|
410
|
-
staticSize,
|
|
411
|
-
path: indexToPath(params.length),
|
|
412
|
-
span: { start: cursor, end: next },
|
|
413
|
-
});
|
|
414
|
-
|
|
367
|
+
params.push(node);
|
|
415
368
|
cursor = next;
|
|
416
369
|
}
|
|
417
370
|
|
|
418
|
-
const declaredCount = data[
|
|
371
|
+
const declaredCount = data[DF.HEADER_PARAMCOUNT_OFFSET]!;
|
|
419
372
|
if (params.length !== declaredCount) {
|
|
420
373
|
throw new CallciumError(
|
|
421
374
|
"PARAM_COUNT_MISMATCH",
|
|
@@ -423,8 +376,69 @@ export function decodeDescriptor(data: Uint8Array): { descriptor: DecodedDescrip
|
|
|
423
376
|
);
|
|
424
377
|
}
|
|
425
378
|
|
|
426
|
-
return {
|
|
379
|
+
return {
|
|
380
|
+
version: { value: version, span: { start: base, end: base + DF.HEADER_PARAMCOUNT_OFFSET } },
|
|
381
|
+
paramCount: {
|
|
382
|
+
value: declaredCount,
|
|
383
|
+
span: { start: base + DF.HEADER_PARAMCOUNT_OFFSET, end: base + DF.HEADER_SIZE },
|
|
384
|
+
},
|
|
385
|
+
params,
|
|
386
|
+
span: { start: base, end: base + data.length },
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
///////////////////////////////////////////////////////////////////////////
|
|
391
|
+
// Descriptor inspection
|
|
392
|
+
///////////////////////////////////////////////////////////////////////////
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Decode a binary descriptor and check every composite node against its children.
|
|
396
|
+
* @param desc - The descriptor bytes.
|
|
397
|
+
* @param base - Byte offset of the descriptor in the blob its spans locate.
|
|
398
|
+
* @throws {CallciumError} If the descriptor is malformed, or a composite node disagrees with its children.
|
|
399
|
+
*/
|
|
400
|
+
export function inspectDescriptor(desc: Uint8Array, base = 0): DecodedDescriptor {
|
|
401
|
+
const decoded = decodeDescriptor(desc, base);
|
|
402
|
+
Descriptor.validateMeta(desc);
|
|
403
|
+
return decoded;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Inspect a binary descriptor: its header fields and its parameters as a tree of nodes, each with the bytes it spans.
|
|
408
|
+
* @param desc - Binary descriptor bytes (with version+paramCount header).
|
|
409
|
+
* @returns The decoded descriptor.
|
|
410
|
+
* @throws {CallciumError} If the descriptor is malformed, or a composite node disagrees with its children.
|
|
411
|
+
*/
|
|
412
|
+
function inspect(desc: Uint8Array): DecodedDescriptor {
|
|
413
|
+
return inspectDescriptor(desc);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Reconstruct the ABI type string of a decoded node. */
|
|
417
|
+
function typeString(node: DecodedNode): string {
|
|
418
|
+
switch (node.typeClass) {
|
|
419
|
+
case "elementary":
|
|
420
|
+
return lookupTypeCode(node.typeCode).label;
|
|
421
|
+
case "tuple":
|
|
422
|
+
return `(${node.fields.map(typeString).join(",")})`;
|
|
423
|
+
case "staticArray":
|
|
424
|
+
return `${typeString(node.element)}[${node.length}]`;
|
|
425
|
+
case "dynamicArray":
|
|
426
|
+
return `${typeString(node.element)}[]`;
|
|
427
|
+
default:
|
|
428
|
+
return node satisfies never;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Reconstruct a comma-separated list of ABI type strings from a binary descriptor. `fromTypes(toTypes(desc))` returns
|
|
434
|
+
* `desc` for every descriptor `inspect` accepts.
|
|
435
|
+
* @param desc - Binary descriptor bytes (with version+paramCount header).
|
|
436
|
+
* @returns Comma-separated ABI type strings, for example `"address,uint256,(bool,bytes32)[]"`.
|
|
437
|
+
* @throws {CallciumError} If the descriptor is malformed, or a composite node disagrees with its children.
|
|
438
|
+
*/
|
|
439
|
+
function toTypes(desc: Uint8Array): string {
|
|
440
|
+
return inspect(desc).params.map(typeString).join(",");
|
|
427
441
|
}
|
|
428
442
|
|
|
429
|
-
/** Encode and
|
|
430
|
-
export const DescriptorCoder = { fromTypes, toTypes };
|
|
443
|
+
/** Encode descriptors from type strings, inspect them and read their type strings back. */
|
|
444
|
+
export const DescriptorCoder = { fromTypes, inspect, toTypes };
|