@apibara/starknet 2.1.0-beta.4 → 2.1.0-beta.40

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/src/parser.ts CHANGED
@@ -77,7 +77,7 @@ export function parseU256(data: readonly FieldElement[], offset: number) {
77
77
  export function parseAsHex(data: readonly FieldElement[], offset: number) {
78
78
  assertInBounds(data, offset);
79
79
  return {
80
- out: String(data[offset]),
80
+ out: data[offset],
81
81
  offset: offset + 1,
82
82
  };
83
83
  }
@@ -96,7 +96,7 @@ export function parseFelt252(data: readonly FieldElement[], offset: number) {
96
96
  };
97
97
  }
98
98
 
99
- export function parseEmpty(data: readonly FieldElement[], offset: number) {
99
+ export function parseEmpty(_data: readonly FieldElement[], offset: number) {
100
100
  return { out: null, offset };
101
101
  }
102
102
 
@@ -132,13 +132,15 @@ export function parseOption<T>(type: Parser<T>) {
132
132
  };
133
133
  }
134
134
 
135
- export function parseStruct<T extends { [key: string]: unknown }>(
136
- parsers: { [K in keyof T]: { index: number; parser: Parser<T[K]> } },
137
- ) {
135
+ export function parseStruct<T extends Record<string, unknown>>(
136
+ parsers: {
137
+ [K in keyof T]: { index: number; parser: Parser<T[K]> };
138
+ },
139
+ ): Parser<{ [K in keyof T]: T[K] }> {
138
140
  const sortedParsers = Object.entries(parsers).sort(
139
141
  (a, b) => a[1].index - b[1].index,
140
142
  );
141
- return (data: readonly FieldElement[], startingOffset: number) => {
143
+ const parser = (data: readonly FieldElement[], startingOffset: number) => {
142
144
  let offset = startingOffset;
143
145
  const out: Record<string, unknown> = {};
144
146
  for (const [key, { parser }] of sortedParsers) {
@@ -148,6 +150,35 @@ export function parseStruct<T extends { [key: string]: unknown }>(
148
150
  }
149
151
  return { out, offset };
150
152
  };
153
+ return parser as Parser<{ [K in keyof T]: T[K] }>;
154
+ }
155
+
156
+ export function parseEnum<T extends Record<string, unknown>>(
157
+ parsers: {
158
+ [K in keyof T]: { index: number; parser: Parser<T[K]> };
159
+ },
160
+ ): Parser<T[keyof T]> {
161
+ return (data: readonly FieldElement[], startingOffset: number) => {
162
+ const selectorFelt = data[startingOffset];
163
+ const selector = Number(BigInt(selectorFelt));
164
+
165
+ // Find the parser by index
166
+ const parserEntry = Object.entries(parsers).find(
167
+ ([, { index }]) => index === selector,
168
+ );
169
+
170
+ if (!parserEntry) {
171
+ throw new ParseError(`Unknown enum variant selector: ${selector}`);
172
+ }
173
+
174
+ const [variantName, { parser }] = parserEntry;
175
+ const { out, offset: newOffset } = parser(data, startingOffset + 1);
176
+
177
+ return {
178
+ out: { _tag: variantName, [variantName]: out } as T[keyof T],
179
+ offset: newOffset,
180
+ };
181
+ };
151
182
  }
152
183
 
153
184
  export function parseTuple<T extends Parser<unknown>[]>(
@@ -168,3 +199,68 @@ export function parseTuple<T extends Parser<unknown>[]>(
168
199
  type UnwrapParsers<TP> = {
169
200
  [Index in keyof TP]: TP[Index] extends Parser<infer U> ? U : never;
170
201
  };
202
+
203
+ const parseByteArrayStruct = parseStruct({
204
+ data: {
205
+ index: 0,
206
+ parser: parseArray(parseBytes31),
207
+ },
208
+ pendingWord: { index: 1, parser: parseFelt252 },
209
+ pendingWordLen: { index: 2, parser: parseU32 },
210
+ });
211
+
212
+ export function parseByteArray(data: readonly FieldElement[], offset: number) {
213
+ // A ByteArray is a struct with the following abi:
214
+ //
215
+ // {
216
+ // name: "core::byte_array::ByteArray",
217
+ // type: "struct",
218
+ // members: [
219
+ // {
220
+ // name: "data",
221
+ // type: "core::array::Array::<core::bytes_31::bytes31>",
222
+ // },
223
+ // {
224
+ // name: "pending_word",
225
+ // type: "core::felt252",
226
+ // },
227
+ // {
228
+ // name: "pending_word_len",
229
+ // type: "core::integer::u32",
230
+ // },
231
+ // ],
232
+ // },
233
+ //
234
+ // We first parse it using a parser for that struct, then convert it to the output `0x${string}` type.
235
+ const { out, offset: offsetOut } = parseByteArrayStruct(data, offset);
236
+
237
+ // Remove 0x prefix from data elements and pad them to 31 bytes.
238
+ const dataBytes = out.data
239
+ .map((bytes) => bytes.slice(2).padStart(62, "0"))
240
+ .join("");
241
+
242
+ let pending = out.pendingWord.toString(16);
243
+ const pendingWordLength = Number(out.pendingWordLen);
244
+ if (pending.length < pendingWordLength * 2) {
245
+ pending = pending.padStart(pendingWordLength * 2, "0");
246
+ }
247
+
248
+ const pendingBytes = pending.slice(pending.length - 2 * pendingWordLength);
249
+ const bytes = removeLeadingZeros(dataBytes + pendingBytes);
250
+
251
+ return { out: `0x${bytes}`, offset: offsetOut };
252
+ }
253
+
254
+ function removeLeadingZeros(bytes: string): string {
255
+ for (let i = 0; i < bytes.length; i++) {
256
+ if (bytes[i] !== "0") {
257
+ let j = i;
258
+ if (i % 2 !== 0) {
259
+ j -= 1;
260
+ }
261
+ return bytes.slice(j);
262
+ }
263
+ }
264
+ // The bytes are all 0, so return something reasonable.
265
+ return "00";
266
+ }