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

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/common.ts CHANGED
@@ -1,40 +1,25 @@
1
- import { Schema } from "@effect/schema";
1
+ import type { Codec, CodecType } from "@apibara/protocol/codec";
2
+ import type * as proto from "./proto";
2
3
 
3
- const _FieldElement = Schema.TemplateLiteral(
4
- Schema.Literal("0x"),
5
- Schema.String,
6
- );
4
+ const MAX_U64 = 0xffffffffffffffffn;
7
5
 
8
- /** Wire representation of `FieldElement`. */
9
- export const FieldElementProto = Schema.Struct({
10
- x0: Schema.BigIntFromSelf,
11
- x1: Schema.BigIntFromSelf,
12
- x2: Schema.BigIntFromSelf,
13
- x3: Schema.BigIntFromSelf,
14
- });
15
-
16
- /** Field element. */
17
- export const FieldElement = Schema.transform(FieldElementProto, _FieldElement, {
18
- decode(value) {
19
- const x0 = value.x0.toString(16).padStart(16, "0");
20
- const x1 = value.x1.toString(16).padStart(16, "0");
21
- const x2 = value.x2.toString(16).padStart(16, "0");
22
- const x3 = value.x3.toString(16).padStart(16, "0");
23
- return `0x${x0}${x1}${x2}${x3}` as `0x${string}`;
24
- },
25
- encode(value) {
26
- const bn = BigInt(value);
27
- const hex = bn.toString(16).padStart(64, "0");
28
- const s = hex.length;
29
- const x3 = BigInt(`0x${hex.slice(s - 16, s)}`);
30
- const x2 = BigInt(`0x${hex.slice(s - 32, s - 16)}`);
31
- const x1 = BigInt(`0x${hex.slice(s - 48, s - 32)}`);
32
- const x0 = BigInt(`0x${hex.slice(s - 64, s - 48)}`);
6
+ export const FieldElement: Codec<`0x${string}`, proto.common.FieldElement> = {
7
+ encode(x) {
8
+ const bn = BigInt(x);
9
+ const x3 = bn & MAX_U64;
10
+ const x2 = (bn >> 64n) & MAX_U64;
11
+ const x1 = (bn >> 128n) & MAX_U64;
12
+ const x0 = (bn >> 192n) & MAX_U64;
33
13
  return { x0, x1, x2, x3 };
34
14
  },
35
- });
36
-
37
- export type FieldElement = Schema.Schema.Type<typeof FieldElement>;
15
+ decode(p) {
16
+ const x0 = p.x0 ?? 0n;
17
+ const x1 = p.x1 ?? 0n;
18
+ const x2 = p.x2 ?? 0n;
19
+ const x3 = p.x3 ?? 0n;
20
+ const bn = x3 + (x2 << 64n) + (x1 << 128n) + (x0 << 192n);
21
+ return `0x${bn.toString(16).padStart(64, "0")}` as `0x${string}`;
22
+ },
23
+ };
38
24
 
39
- export const feltToProto = Schema.encodeSync(FieldElement);
40
- export const feltFromProto = Schema.decodeSync(FieldElement);
25
+ export type FieldElement = CodecType<typeof FieldElement>;
package/src/event.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import type { Abi } from "abi-wan-kanabi";
2
2
  import type {
3
3
  AbiEventMember,
4
- EventToPrimitiveType,
5
4
  ExtractAbiEventNames,
6
5
  } from "abi-wan-kanabi/kanabi";
7
6
  import {
@@ -11,17 +10,31 @@ import {
11
10
  getOptionType,
12
11
  getSpanType,
13
12
  isArrayType,
13
+ isByteArray,
14
14
  isEmptyType,
15
15
  isOptionType,
16
16
  isPrimitiveType,
17
17
  isSpanType,
18
18
  } from "./abi";
19
+ import {
20
+ type AbiEnum,
21
+ type AbiEvent,
22
+ type AbiEventEnum,
23
+ type AbiEventStruct,
24
+ type AbiMember,
25
+ type EventToPrimitiveType,
26
+ isEnumEventAbi,
27
+ isEventAbi,
28
+ isStructEventAbi,
29
+ } from "./abi-wan-helpers";
19
30
  import type { Event } from "./block";
20
31
  import {
21
32
  ParseError,
22
33
  type Parser,
23
34
  parseArray,
35
+ parseByteArray,
24
36
  parseEmpty,
37
+ parseEnum,
25
38
  parseOption,
26
39
  parseSpan,
27
40
  parseStruct,
@@ -78,61 +91,184 @@ export function decodeEvent<
78
91
  (item) => item.name === eventName && item.type === "event",
79
92
  );
80
93
 
81
- if (!eventAbi || eventAbi.type !== "event") {
94
+ if (!eventAbi || !isEventAbi(eventAbi)) {
82
95
  if (strict) {
83
96
  throw new DecodeEventError(`Event ${eventName} not found in ABI`);
84
97
  }
85
-
86
98
  return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
87
99
  }
88
100
 
89
- if (eventAbi.kind === "enum") {
90
- throw new DecodeEventError("enum: not implemented");
101
+ try {
102
+ if (isStructEventAbi(eventAbi)) {
103
+ return decodeStructEvent(abi, eventAbi, event, eventName);
104
+ }
105
+
106
+ if (isEnumEventAbi(eventAbi)) {
107
+ return decodeEnumEvent(abi, eventAbi, event, eventName);
108
+ }
109
+
110
+ throw new DecodeEventError(
111
+ `Unsupported event kind: ${(eventAbi as AbiEvent)?.kind}`,
112
+ );
113
+ } catch (error) {
114
+ if (
115
+ (error instanceof DecodeEventError || error instanceof ParseError) &&
116
+ !strict
117
+ ) {
118
+ return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
119
+ }
120
+
121
+ throw error;
91
122
  }
123
+ }
92
124
 
125
+ function decodeStructEvent<
126
+ TAbi extends Abi = Abi,
127
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
128
+ >(
129
+ abi: TAbi,
130
+ eventAbi: AbiEventStruct,
131
+ event: Event,
132
+ eventName: TEventName,
133
+ ): DecodedEvent<TAbi, TEventName> {
93
134
  const selector = BigInt(getEventSelector(eventName));
94
135
  if ((event.keys && selector !== BigInt(event.keys[0])) || !event.keys) {
95
- if (strict) {
96
- throw new DecodeEventError(
97
- `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`,
98
- );
99
- }
100
-
101
- return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
136
+ throw new DecodeEventError(
137
+ `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`,
138
+ );
102
139
  }
103
140
 
104
141
  const keysAbi = eventAbi.members.filter((m) => m.kind === "key");
105
142
  const dataAbi = eventAbi.members.filter((m) => m.kind === "data");
106
143
 
107
- try {
108
- const keysParser = compileEventMembers(abi, keysAbi);
109
- const dataParser = compileEventMembers(abi, dataAbi);
110
-
111
- const keysWithoutSelector = event.keys?.slice(1) ?? [];
112
- const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
113
- const { out: decodedData } = dataParser(event.data ?? [], 0);
114
-
115
- const decoded = {
116
- ...decodedKeys,
117
- ...decodedData,
118
- } as EventToPrimitiveType<TAbi, TEventName>;
119
-
120
- return {
121
- ...event,
122
- eventName,
123
- args: decoded,
124
- } as DecodedEvent<TAbi, TEventName>;
125
- } catch (error) {
126
- if (error instanceof DecodeEventError && !strict) {
127
- return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
128
- }
144
+ const keysParser = compileEventMembers(abi, keysAbi);
145
+ const dataParser = compileEventMembers(abi, dataAbi);
129
146
 
130
- if (error instanceof ParseError && !strict) {
131
- return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
147
+ const keysWithoutSelector = event.keys?.slice(1) ?? [];
148
+
149
+ const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
150
+ const { out: decodedData } = dataParser(event.data ?? [], 0);
151
+
152
+ const decoded = {
153
+ ...decodedKeys,
154
+ ...decodedData,
155
+ } as EventToPrimitiveType<TAbi, TEventName>;
156
+
157
+ return {
158
+ ...event,
159
+ eventName,
160
+ args: decoded,
161
+ } as DecodedEvent<TAbi, TEventName>;
162
+ }
163
+
164
+ function decodeEnumEvent<
165
+ TAbi extends Abi = Abi,
166
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
167
+ >(
168
+ abi: TAbi,
169
+ eventAbi: AbiEventEnum,
170
+ event: Event,
171
+ eventName: TEventName,
172
+ ): DecodedEvent<TAbi, TEventName> {
173
+ if (!event.keys || event.keys.length === 0) {
174
+ throw new DecodeEventError(
175
+ "Event has no keys; cannot determine variant selector",
176
+ );
177
+ }
178
+
179
+ const variants = eventAbi.variants;
180
+
181
+ const variantSelector = event.keys[0];
182
+
183
+ // Create a map of all possible selectors to their variant paths
184
+ const selectorToVariant = buildVariantSelectorMap(abi, variants);
185
+
186
+ // Find the matching variant and path
187
+ const matchingVariant = selectorToVariant[variantSelector];
188
+
189
+ if (!matchingVariant) {
190
+ throw new DecodeEventError(
191
+ `No matching variant found for selector: ${variantSelector}`,
192
+ );
193
+ }
194
+
195
+ const structEventAbi = abi.find(
196
+ (item) =>
197
+ item.name === matchingVariant.variant.type && item.type === "event",
198
+ );
199
+
200
+ if (!structEventAbi || !isStructEventAbi(structEventAbi)) {
201
+ throw new DecodeEventError(
202
+ `Nested event type not found or not a struct: ${matchingVariant.variant.type}`,
203
+ );
204
+ }
205
+
206
+ const decodedStruct = decodeStructEvent(
207
+ abi,
208
+ structEventAbi,
209
+ event,
210
+ matchingVariant.variant.name,
211
+ );
212
+
213
+ return {
214
+ ...event,
215
+ eventName,
216
+ args: {
217
+ _tag: matchingVariant.variant.name,
218
+ [matchingVariant.variant.name]: decodedStruct.args,
219
+ },
220
+ } as DecodedEvent<TAbi, TEventName>;
221
+ }
222
+
223
+ type EnumFlatVariantMap = Record<
224
+ string,
225
+ { variant: AbiEventMember; path: string[] }
226
+ >;
227
+
228
+ // Helper to build a map of all possible selectors to their variant paths
229
+ function buildVariantSelectorMap(
230
+ abi: Abi,
231
+ variants: AbiEventMember[],
232
+ ): EnumFlatVariantMap {
233
+ const selectorMap: EnumFlatVariantMap = {};
234
+
235
+ for (const variant of variants) {
236
+ // For nested events, just map the variant's own selector
237
+ if (variant.kind === "nested") {
238
+ const selector = getEventSelector(variant.name);
239
+ selectorMap[selector] = { variant, path: [variant.name] };
132
240
  }
241
+ // For flat events, recursively map all possible selectors from the event hierarchy
242
+ else if (variant.kind === "flat") {
243
+ const flatEventName = variant.type;
244
+ const flatEventAbi = abi.find(
245
+ (item) => item.name === flatEventName && item.type === "event",
246
+ );
133
247
 
134
- throw error;
248
+ // Skip if the flat event type is not found
249
+ if (!flatEventAbi) {
250
+ continue;
251
+ }
252
+
253
+ if (isEnumEventAbi(flatEventAbi)) {
254
+ // For enum events, recursively map all their variants
255
+ const nestedMap = buildVariantSelectorMap(abi, flatEventAbi.variants);
256
+
257
+ // Add this variant to the path for all nested selectors
258
+ for (const [
259
+ nestedSelector,
260
+ { variant: nestedVariant, path: nestedPath },
261
+ ] of Object.entries(nestedMap)) {
262
+ selectorMap[nestedSelector] = {
263
+ variant: nestedVariant,
264
+ path: [variant.name, ...nestedPath],
265
+ };
266
+ }
267
+ }
268
+ }
135
269
  }
270
+
271
+ return selectorMap;
136
272
  }
137
273
 
138
274
  function compileEventMembers<T extends Record<string, unknown>>(
@@ -166,6 +302,10 @@ function compileTypeParser(abi: Abi, type: string): Parser<unknown> {
166
302
  return parseEmpty;
167
303
  }
168
304
 
305
+ if (isByteArray(type)) {
306
+ return parseByteArray;
307
+ }
308
+
169
309
  // Not a well-known type. Look it up in the ABI.
170
310
  const typeAbi = abi.find((item) => item.name === type);
171
311
  if (!typeAbi) {
@@ -176,18 +316,14 @@ function compileTypeParser(abi: Abi, type: string): Parser<unknown> {
176
316
  case "struct": {
177
317
  return compileStructParser(abi, typeAbi.members);
178
318
  }
179
- case "enum":
180
- throw new DecodeEventError("enum: not implemented");
319
+ case "enum": {
320
+ return compileEnumParser(abi, typeAbi);
321
+ }
181
322
  default:
182
323
  throw new DecodeEventError(`Invalid type ${typeAbi.type}`);
183
324
  }
184
325
  }
185
326
 
186
- type AbiMember = {
187
- name: string;
188
- type: string;
189
- };
190
-
191
327
  function compileStructParser(
192
328
  abi: Abi,
193
329
  members: readonly AbiMember[],
@@ -202,3 +338,15 @@ function compileStructParser(
202
338
  }
203
339
  return parseStruct(parsers);
204
340
  }
341
+
342
+ function compileEnumParser(abi: Abi, enumAbi: AbiEnum): Parser<unknown> {
343
+ const parsers: Record<string, { index: number; parser: Parser<unknown> }> =
344
+ {};
345
+ for (const [index, variant] of enumAbi.variants.entries()) {
346
+ parsers[variant.name] = {
347
+ index,
348
+ parser: compileTypeParser(abi, variant.type),
349
+ };
350
+ }
351
+ return parseEnum(parsers);
352
+ }