@apibara/starknet 2.1.0-beta.24 → 2.1.0-beta.26

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/dist/index.cjs CHANGED
@@ -8369,6 +8369,16 @@ function isEmptyType(type) {
8369
8369
  return type === "()";
8370
8370
  }
8371
8371
 
8372
+ function isEventAbi(item) {
8373
+ return item.type === "event";
8374
+ }
8375
+ function isStructEventAbi(item) {
8376
+ return isEventAbi(item) && item.kind === "struct";
8377
+ }
8378
+ function isEnumEventAbi(item) {
8379
+ return isEventAbi(item) && item.kind === "enum";
8380
+ }
8381
+
8372
8382
  class DecodeEventError extends Error {
8373
8383
  constructor(message) {
8374
8384
  super(message);
@@ -8380,50 +8390,120 @@ function decodeEvent(args) {
8380
8390
  const eventAbi = abi.find(
8381
8391
  (item) => item.name === eventName && item.type === "event"
8382
8392
  );
8383
- if (!eventAbi || eventAbi.type !== "event") {
8393
+ if (!eventAbi || !isEventAbi(eventAbi)) {
8384
8394
  if (strict) {
8385
8395
  throw new DecodeEventError(`Event ${eventName} not found in ABI`);
8386
8396
  }
8387
8397
  return null;
8388
8398
  }
8389
- if (eventAbi.kind === "enum") {
8390
- throw new DecodeEventError("enum: not implemented");
8399
+ try {
8400
+ if (isStructEventAbi(eventAbi)) {
8401
+ return decodeStructEvent(abi, eventAbi, event, eventName);
8402
+ }
8403
+ if (isEnumEventAbi(eventAbi)) {
8404
+ return decodeEnumEvent(abi, eventAbi, event, eventName);
8405
+ }
8406
+ throw new DecodeEventError(
8407
+ `Unsupported event kind: ${eventAbi?.kind}`
8408
+ );
8409
+ } catch (error) {
8410
+ if ((error instanceof DecodeEventError || error instanceof parser.ParseError) && !strict) {
8411
+ return null;
8412
+ }
8413
+ throw error;
8391
8414
  }
8415
+ }
8416
+ function decodeStructEvent(abi, eventAbi, event, eventName) {
8392
8417
  const selector = BigInt(getEventSelector(eventName));
8393
8418
  if (event.keys && selector !== BigInt(event.keys[0]) || !event.keys) {
8394
- if (strict) {
8395
- throw new DecodeEventError(
8396
- `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`
8397
- );
8398
- }
8399
- return null;
8419
+ throw new DecodeEventError(
8420
+ `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`
8421
+ );
8400
8422
  }
8401
8423
  const keysAbi = eventAbi.members.filter((m) => m.kind === "key");
8402
8424
  const dataAbi = eventAbi.members.filter((m) => m.kind === "data");
8403
- try {
8404
- const keysParser = compileEventMembers(abi, keysAbi);
8405
- const dataParser = compileEventMembers(abi, dataAbi);
8406
- const keysWithoutSelector = event.keys?.slice(1) ?? [];
8407
- const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
8408
- const { out: decodedData } = dataParser(event.data ?? [], 0);
8409
- const decoded = {
8410
- ...decodedKeys,
8411
- ...decodedData
8412
- };
8413
- return {
8414
- ...event,
8415
- eventName,
8416
- args: decoded
8417
- };
8418
- } catch (error) {
8419
- if (error instanceof DecodeEventError && !strict) {
8420
- return null;
8425
+ const keysParser = compileEventMembers(abi, keysAbi);
8426
+ const dataParser = compileEventMembers(abi, dataAbi);
8427
+ const keysWithoutSelector = event.keys?.slice(1) ?? [];
8428
+ const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
8429
+ const { out: decodedData } = dataParser(event.data ?? [], 0);
8430
+ const decoded = {
8431
+ ...decodedKeys,
8432
+ ...decodedData
8433
+ };
8434
+ return {
8435
+ ...event,
8436
+ eventName,
8437
+ args: decoded
8438
+ };
8439
+ }
8440
+ function decodeEnumEvent(abi, eventAbi, event, eventName) {
8441
+ if (!event.keys || event.keys.length === 0) {
8442
+ throw new DecodeEventError(
8443
+ "Event has no keys; cannot determine variant selector"
8444
+ );
8445
+ }
8446
+ const variants = eventAbi.variants;
8447
+ const variantSelector = event.keys[0];
8448
+ const selectorToVariant = buildVariantSelectorMap(abi, variants);
8449
+ const matchingVariant = selectorToVariant[variantSelector];
8450
+ if (!matchingVariant) {
8451
+ throw new DecodeEventError(
8452
+ `No matching variant found for selector: ${variantSelector}`
8453
+ );
8454
+ }
8455
+ const structEventAbi = abi.find(
8456
+ (item) => item.name === matchingVariant.variant.type && item.type === "event"
8457
+ );
8458
+ if (!structEventAbi || !isStructEventAbi(structEventAbi)) {
8459
+ throw new DecodeEventError(
8460
+ `Nested event type not found or not a struct: ${matchingVariant.variant.type}`
8461
+ );
8462
+ }
8463
+ const decodedStruct = decodeStructEvent(
8464
+ abi,
8465
+ structEventAbi,
8466
+ event,
8467
+ matchingVariant.variant.name
8468
+ );
8469
+ return {
8470
+ ...event,
8471
+ eventName,
8472
+ args: {
8473
+ _tag: matchingVariant.variant.name,
8474
+ [matchingVariant.variant.name]: decodedStruct.args
8421
8475
  }
8422
- if (error instanceof parser.ParseError && !strict) {
8423
- return null;
8476
+ };
8477
+ }
8478
+ function buildVariantSelectorMap(abi, variants) {
8479
+ const selectorMap = {};
8480
+ for (const variant of variants) {
8481
+ if (variant.kind === "nested") {
8482
+ const selector = getEventSelector(variant.name);
8483
+ selectorMap[selector] = { variant, path: [variant.name] };
8484
+ } else if (variant.kind === "flat") {
8485
+ const flatEventName = variant.type;
8486
+ const flatEventAbi = abi.find(
8487
+ (item) => item.name === flatEventName && item.type === "event"
8488
+ );
8489
+ if (!flatEventAbi) {
8490
+ continue;
8491
+ }
8492
+ if (isEnumEventAbi(flatEventAbi)) {
8493
+ const nestedMap = buildVariantSelectorMap(abi, flatEventAbi.variants);
8494
+ for (const [
8495
+ nestedSelector,
8496
+ { variant: nestedVariant, path: nestedPath }
8497
+ ] of Object.entries(nestedMap)) {
8498
+ selectorMap[nestedSelector] = {
8499
+ variant: nestedVariant,
8500
+ path: [variant.name, ...nestedPath]
8501
+ };
8502
+ }
8503
+ }
8424
8504
  }
8425
- throw error;
8426
8505
  }
8506
+ return selectorMap;
8427
8507
  }
8428
8508
  function compileEventMembers(abi, members) {
8429
8509
  return compileStructParser(abi, members);
@@ -8455,8 +8535,9 @@ function compileTypeParser(abi, type) {
8455
8535
  case "struct": {
8456
8536
  return compileStructParser(abi, typeAbi.members);
8457
8537
  }
8458
- case "enum":
8459
- throw new DecodeEventError("enum: not implemented");
8538
+ case "enum": {
8539
+ throw new DecodeEventError(`Enum types are not supported: ${type}`);
8540
+ }
8460
8541
  default:
8461
8542
  throw new DecodeEventError(`Invalid type ${typeAbi.type}`);
8462
8543
  }
package/dist/index.d.cts CHANGED
@@ -4,7 +4,7 @@ import _m0 from 'protobufjs/minimal.js';
4
4
  import { StreamConfig } from '@apibara/protocol';
5
5
  import { Abi } from 'abi-wan-kanabi';
6
6
  export { Abi } from 'abi-wan-kanabi';
7
- import { ExtractAbiEventNames, EventToPrimitiveType } from 'abi-wan-kanabi/kanabi';
7
+ import { ExtractAbiEventNames, ExtractAbiEvent, AbiEventMember, StringToPrimitiveType } from 'abi-wan-kanabi/kanabi';
8
8
 
9
9
  declare const protobufPackage$1 = "starknet.v2";
10
10
  /** Starknet DNA definitions (data). */
@@ -5983,6 +5983,35 @@ declare function getTransaction(transactionIndex: number, params: {
5983
5983
  transactions: readonly Transaction[];
5984
5984
  } | readonly Transaction[]): Transaction | undefined;
5985
5985
 
5986
+ type ResolveNestedVariantType<TAbi extends Abi, TTypeName extends string> = EventToPrimitiveType<TAbi, TTypeName>;
5987
+ type VariantToTaggedUnion<TAbi extends Abi, TVariant extends AbiEventMember> = TVariant extends {
5988
+ kind: "nested";
5989
+ } ? // Nested: Use the helper to resolve the payload type.
5990
+ {
5991
+ _tag: TVariant["name"];
5992
+ } & {
5993
+ [K in TVariant["name"]]: ResolveNestedVariantType<TAbi, TVariant["type"]>;
5994
+ } : TVariant extends {
5995
+ kind: "flat";
5996
+ } ? EventToPrimitiveType<TAbi, TVariant["type"]> : never;
5997
+ type EventToPrimitiveType<TAbi extends Abi, TEventName extends ExtractAbiEventNames<TAbi>> = ExtractAbiEvent<TAbi, TEventName> extends infer TEventDef ? TEventDef extends {
5998
+ type: "event";
5999
+ kind: "struct";
6000
+ members: infer TMembers extends readonly AbiEventMember[];
6001
+ } ? {
6002
+ [Member in TMembers[number] as Member["name"]]: StringToPrimitiveType<TAbi, Member["type"]>;
6003
+ } : TEventDef extends {
6004
+ type: "event";
6005
+ kind: "enum";
6006
+ variants: infer TVariants extends readonly AbiEventMember[];
6007
+ } ? {
6008
+ [Idx in keyof TVariants]: VariantToTaggedUnion<TAbi, TVariants[Idx]>;
6009
+ }[number] : TEventDef extends {
6010
+ type: "event";
6011
+ kind: "enum";
6012
+ variants: [];
6013
+ } ? never : never : never;
6014
+
5986
6015
  declare class DecodeEventError extends Error {
5987
6016
  constructor(message: string);
5988
6017
  }
@@ -6015,6 +6044,8 @@ declare function getEventSelector(name: string): FieldElement$1;
6015
6044
 
6016
6045
  declare module "abi-wan-kanabi" {
6017
6046
  interface Config {
6047
+ AddressType: `0x${string}`;
6048
+ ClassHashType: `0x${string}`;
6018
6049
  FeltType: bigint;
6019
6050
  BigIntType: bigint;
6020
6051
  U256Type: bigint;
package/dist/index.d.mts CHANGED
@@ -4,7 +4,7 @@ import _m0 from 'protobufjs/minimal.js';
4
4
  import { StreamConfig } from '@apibara/protocol';
5
5
  import { Abi } from 'abi-wan-kanabi';
6
6
  export { Abi } from 'abi-wan-kanabi';
7
- import { ExtractAbiEventNames, EventToPrimitiveType } from 'abi-wan-kanabi/kanabi';
7
+ import { ExtractAbiEventNames, ExtractAbiEvent, AbiEventMember, StringToPrimitiveType } from 'abi-wan-kanabi/kanabi';
8
8
 
9
9
  declare const protobufPackage$1 = "starknet.v2";
10
10
  /** Starknet DNA definitions (data). */
@@ -5983,6 +5983,35 @@ declare function getTransaction(transactionIndex: number, params: {
5983
5983
  transactions: readonly Transaction[];
5984
5984
  } | readonly Transaction[]): Transaction | undefined;
5985
5985
 
5986
+ type ResolveNestedVariantType<TAbi extends Abi, TTypeName extends string> = EventToPrimitiveType<TAbi, TTypeName>;
5987
+ type VariantToTaggedUnion<TAbi extends Abi, TVariant extends AbiEventMember> = TVariant extends {
5988
+ kind: "nested";
5989
+ } ? // Nested: Use the helper to resolve the payload type.
5990
+ {
5991
+ _tag: TVariant["name"];
5992
+ } & {
5993
+ [K in TVariant["name"]]: ResolveNestedVariantType<TAbi, TVariant["type"]>;
5994
+ } : TVariant extends {
5995
+ kind: "flat";
5996
+ } ? EventToPrimitiveType<TAbi, TVariant["type"]> : never;
5997
+ type EventToPrimitiveType<TAbi extends Abi, TEventName extends ExtractAbiEventNames<TAbi>> = ExtractAbiEvent<TAbi, TEventName> extends infer TEventDef ? TEventDef extends {
5998
+ type: "event";
5999
+ kind: "struct";
6000
+ members: infer TMembers extends readonly AbiEventMember[];
6001
+ } ? {
6002
+ [Member in TMembers[number] as Member["name"]]: StringToPrimitiveType<TAbi, Member["type"]>;
6003
+ } : TEventDef extends {
6004
+ type: "event";
6005
+ kind: "enum";
6006
+ variants: infer TVariants extends readonly AbiEventMember[];
6007
+ } ? {
6008
+ [Idx in keyof TVariants]: VariantToTaggedUnion<TAbi, TVariants[Idx]>;
6009
+ }[number] : TEventDef extends {
6010
+ type: "event";
6011
+ kind: "enum";
6012
+ variants: [];
6013
+ } ? never : never : never;
6014
+
5986
6015
  declare class DecodeEventError extends Error {
5987
6016
  constructor(message: string);
5988
6017
  }
@@ -6015,6 +6044,8 @@ declare function getEventSelector(name: string): FieldElement$1;
6015
6044
 
6016
6045
  declare module "abi-wan-kanabi" {
6017
6046
  interface Config {
6047
+ AddressType: `0x${string}`;
6048
+ ClassHashType: `0x${string}`;
6018
6049
  FeltType: bigint;
6019
6050
  BigIntType: bigint;
6020
6051
  U256Type: bigint;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import _m0 from 'protobufjs/minimal.js';
4
4
  import { StreamConfig } from '@apibara/protocol';
5
5
  import { Abi } from 'abi-wan-kanabi';
6
6
  export { Abi } from 'abi-wan-kanabi';
7
- import { ExtractAbiEventNames, EventToPrimitiveType } from 'abi-wan-kanabi/kanabi';
7
+ import { ExtractAbiEventNames, ExtractAbiEvent, AbiEventMember, StringToPrimitiveType } from 'abi-wan-kanabi/kanabi';
8
8
 
9
9
  declare const protobufPackage$1 = "starknet.v2";
10
10
  /** Starknet DNA definitions (data). */
@@ -5983,6 +5983,35 @@ declare function getTransaction(transactionIndex: number, params: {
5983
5983
  transactions: readonly Transaction[];
5984
5984
  } | readonly Transaction[]): Transaction | undefined;
5985
5985
 
5986
+ type ResolveNestedVariantType<TAbi extends Abi, TTypeName extends string> = EventToPrimitiveType<TAbi, TTypeName>;
5987
+ type VariantToTaggedUnion<TAbi extends Abi, TVariant extends AbiEventMember> = TVariant extends {
5988
+ kind: "nested";
5989
+ } ? // Nested: Use the helper to resolve the payload type.
5990
+ {
5991
+ _tag: TVariant["name"];
5992
+ } & {
5993
+ [K in TVariant["name"]]: ResolveNestedVariantType<TAbi, TVariant["type"]>;
5994
+ } : TVariant extends {
5995
+ kind: "flat";
5996
+ } ? EventToPrimitiveType<TAbi, TVariant["type"]> : never;
5997
+ type EventToPrimitiveType<TAbi extends Abi, TEventName extends ExtractAbiEventNames<TAbi>> = ExtractAbiEvent<TAbi, TEventName> extends infer TEventDef ? TEventDef extends {
5998
+ type: "event";
5999
+ kind: "struct";
6000
+ members: infer TMembers extends readonly AbiEventMember[];
6001
+ } ? {
6002
+ [Member in TMembers[number] as Member["name"]]: StringToPrimitiveType<TAbi, Member["type"]>;
6003
+ } : TEventDef extends {
6004
+ type: "event";
6005
+ kind: "enum";
6006
+ variants: infer TVariants extends readonly AbiEventMember[];
6007
+ } ? {
6008
+ [Idx in keyof TVariants]: VariantToTaggedUnion<TAbi, TVariants[Idx]>;
6009
+ }[number] : TEventDef extends {
6010
+ type: "event";
6011
+ kind: "enum";
6012
+ variants: [];
6013
+ } ? never : never : never;
6014
+
5986
6015
  declare class DecodeEventError extends Error {
5987
6016
  constructor(message: string);
5988
6017
  }
@@ -6015,6 +6044,8 @@ declare function getEventSelector(name: string): FieldElement$1;
6015
6044
 
6016
6045
  declare module "abi-wan-kanabi" {
6017
6046
  interface Config {
6047
+ AddressType: `0x${string}`;
6048
+ ClassHashType: `0x${string}`;
6018
6049
  FeltType: bigint;
6019
6050
  BigIntType: bigint;
6020
6051
  U256Type: bigint;
package/dist/index.mjs CHANGED
@@ -8362,6 +8362,16 @@ function isEmptyType(type) {
8362
8362
  return type === "()";
8363
8363
  }
8364
8364
 
8365
+ function isEventAbi(item) {
8366
+ return item.type === "event";
8367
+ }
8368
+ function isStructEventAbi(item) {
8369
+ return isEventAbi(item) && item.kind === "struct";
8370
+ }
8371
+ function isEnumEventAbi(item) {
8372
+ return isEventAbi(item) && item.kind === "enum";
8373
+ }
8374
+
8365
8375
  class DecodeEventError extends Error {
8366
8376
  constructor(message) {
8367
8377
  super(message);
@@ -8373,50 +8383,120 @@ function decodeEvent(args) {
8373
8383
  const eventAbi = abi.find(
8374
8384
  (item) => item.name === eventName && item.type === "event"
8375
8385
  );
8376
- if (!eventAbi || eventAbi.type !== "event") {
8386
+ if (!eventAbi || !isEventAbi(eventAbi)) {
8377
8387
  if (strict) {
8378
8388
  throw new DecodeEventError(`Event ${eventName} not found in ABI`);
8379
8389
  }
8380
8390
  return null;
8381
8391
  }
8382
- if (eventAbi.kind === "enum") {
8383
- throw new DecodeEventError("enum: not implemented");
8392
+ try {
8393
+ if (isStructEventAbi(eventAbi)) {
8394
+ return decodeStructEvent(abi, eventAbi, event, eventName);
8395
+ }
8396
+ if (isEnumEventAbi(eventAbi)) {
8397
+ return decodeEnumEvent(abi, eventAbi, event, eventName);
8398
+ }
8399
+ throw new DecodeEventError(
8400
+ `Unsupported event kind: ${eventAbi?.kind}`
8401
+ );
8402
+ } catch (error) {
8403
+ if ((error instanceof DecodeEventError || error instanceof ParseError) && !strict) {
8404
+ return null;
8405
+ }
8406
+ throw error;
8384
8407
  }
8408
+ }
8409
+ function decodeStructEvent(abi, eventAbi, event, eventName) {
8385
8410
  const selector = BigInt(getEventSelector(eventName));
8386
8411
  if (event.keys && selector !== BigInt(event.keys[0]) || !event.keys) {
8387
- if (strict) {
8388
- throw new DecodeEventError(
8389
- `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`
8390
- );
8391
- }
8392
- return null;
8412
+ throw new DecodeEventError(
8413
+ `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`
8414
+ );
8393
8415
  }
8394
8416
  const keysAbi = eventAbi.members.filter((m) => m.kind === "key");
8395
8417
  const dataAbi = eventAbi.members.filter((m) => m.kind === "data");
8396
- try {
8397
- const keysParser = compileEventMembers(abi, keysAbi);
8398
- const dataParser = compileEventMembers(abi, dataAbi);
8399
- const keysWithoutSelector = event.keys?.slice(1) ?? [];
8400
- const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
8401
- const { out: decodedData } = dataParser(event.data ?? [], 0);
8402
- const decoded = {
8403
- ...decodedKeys,
8404
- ...decodedData
8405
- };
8406
- return {
8407
- ...event,
8408
- eventName,
8409
- args: decoded
8410
- };
8411
- } catch (error) {
8412
- if (error instanceof DecodeEventError && !strict) {
8413
- return null;
8418
+ const keysParser = compileEventMembers(abi, keysAbi);
8419
+ const dataParser = compileEventMembers(abi, dataAbi);
8420
+ const keysWithoutSelector = event.keys?.slice(1) ?? [];
8421
+ const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
8422
+ const { out: decodedData } = dataParser(event.data ?? [], 0);
8423
+ const decoded = {
8424
+ ...decodedKeys,
8425
+ ...decodedData
8426
+ };
8427
+ return {
8428
+ ...event,
8429
+ eventName,
8430
+ args: decoded
8431
+ };
8432
+ }
8433
+ function decodeEnumEvent(abi, eventAbi, event, eventName) {
8434
+ if (!event.keys || event.keys.length === 0) {
8435
+ throw new DecodeEventError(
8436
+ "Event has no keys; cannot determine variant selector"
8437
+ );
8438
+ }
8439
+ const variants = eventAbi.variants;
8440
+ const variantSelector = event.keys[0];
8441
+ const selectorToVariant = buildVariantSelectorMap(abi, variants);
8442
+ const matchingVariant = selectorToVariant[variantSelector];
8443
+ if (!matchingVariant) {
8444
+ throw new DecodeEventError(
8445
+ `No matching variant found for selector: ${variantSelector}`
8446
+ );
8447
+ }
8448
+ const structEventAbi = abi.find(
8449
+ (item) => item.name === matchingVariant.variant.type && item.type === "event"
8450
+ );
8451
+ if (!structEventAbi || !isStructEventAbi(structEventAbi)) {
8452
+ throw new DecodeEventError(
8453
+ `Nested event type not found or not a struct: ${matchingVariant.variant.type}`
8454
+ );
8455
+ }
8456
+ const decodedStruct = decodeStructEvent(
8457
+ abi,
8458
+ structEventAbi,
8459
+ event,
8460
+ matchingVariant.variant.name
8461
+ );
8462
+ return {
8463
+ ...event,
8464
+ eventName,
8465
+ args: {
8466
+ _tag: matchingVariant.variant.name,
8467
+ [matchingVariant.variant.name]: decodedStruct.args
8414
8468
  }
8415
- if (error instanceof ParseError && !strict) {
8416
- return null;
8469
+ };
8470
+ }
8471
+ function buildVariantSelectorMap(abi, variants) {
8472
+ const selectorMap = {};
8473
+ for (const variant of variants) {
8474
+ if (variant.kind === "nested") {
8475
+ const selector = getEventSelector(variant.name);
8476
+ selectorMap[selector] = { variant, path: [variant.name] };
8477
+ } else if (variant.kind === "flat") {
8478
+ const flatEventName = variant.type;
8479
+ const flatEventAbi = abi.find(
8480
+ (item) => item.name === flatEventName && item.type === "event"
8481
+ );
8482
+ if (!flatEventAbi) {
8483
+ continue;
8484
+ }
8485
+ if (isEnumEventAbi(flatEventAbi)) {
8486
+ const nestedMap = buildVariantSelectorMap(abi, flatEventAbi.variants);
8487
+ for (const [
8488
+ nestedSelector,
8489
+ { variant: nestedVariant, path: nestedPath }
8490
+ ] of Object.entries(nestedMap)) {
8491
+ selectorMap[nestedSelector] = {
8492
+ variant: nestedVariant,
8493
+ path: [variant.name, ...nestedPath]
8494
+ };
8495
+ }
8496
+ }
8417
8497
  }
8418
- throw error;
8419
8498
  }
8499
+ return selectorMap;
8420
8500
  }
8421
8501
  function compileEventMembers(abi, members) {
8422
8502
  return compileStructParser(abi, members);
@@ -8448,8 +8528,9 @@ function compileTypeParser(abi, type) {
8448
8528
  case "struct": {
8449
8529
  return compileStructParser(abi, typeAbi.members);
8450
8530
  }
8451
- case "enum":
8452
- throw new DecodeEventError("enum: not implemented");
8531
+ case "enum": {
8532
+ throw new DecodeEventError(`Enum types are not supported: ${type}`);
8533
+ }
8453
8534
  default:
8454
8535
  throw new DecodeEventError(`Invalid type ${typeAbi.type}`);
8455
8536
  }
package/dist/parser.cjs CHANGED
@@ -37,7 +37,7 @@ function parseU256(data, offset) {
37
37
  function parseAsHex(data, offset) {
38
38
  assertInBounds(data, offset);
39
39
  return {
40
- out: String(data[offset]),
40
+ out: data[offset],
41
41
  offset: offset + 1
42
42
  };
43
43
  }
@@ -53,7 +53,7 @@ function parseFelt252(data, offset) {
53
53
  offset: offset + 1
54
54
  };
55
55
  }
56
- function parseEmpty(data, offset) {
56
+ function parseEmpty(_data, offset) {
57
57
  return { out: null, offset };
58
58
  }
59
59
  function parseArray(type) {
@@ -84,16 +84,17 @@ function parseStruct(parsers) {
84
84
  const sortedParsers = Object.entries(parsers).sort(
85
85
  (a, b) => a[1].index - b[1].index
86
86
  );
87
- return (data, startingOffset) => {
87
+ const parser = (data, startingOffset) => {
88
88
  let offset = startingOffset;
89
89
  const out = {};
90
- for (const [key, { parser }] of sortedParsers) {
91
- const { out: value, offset: newOffset } = parser(data, offset);
90
+ for (const [key, { parser: parser2 }] of sortedParsers) {
91
+ const { out: value, offset: newOffset } = parser2(data, offset);
92
92
  out[key] = value;
93
93
  offset = newOffset;
94
94
  }
95
95
  return { out, offset };
96
96
  };
97
+ return parser;
97
98
  }
98
99
  function parseTuple(...parsers) {
99
100
  return (data, startingOffset) => {
package/dist/parser.d.cts CHANGED
@@ -28,7 +28,7 @@ declare function parseU256(data: readonly FieldElement[], offset: number): {
28
28
  offset: number;
29
29
  };
30
30
  declare function parseAsHex(data: readonly FieldElement[], offset: number): {
31
- out: string;
31
+ out: `0x${string}`;
32
32
  offset: number;
33
33
  };
34
34
  declare const parseContractAddress: typeof parseAsHex;
@@ -40,7 +40,7 @@ declare function parseFelt252(data: readonly FieldElement[], offset: number): {
40
40
  out: bigint;
41
41
  offset: number;
42
42
  };
43
- declare function parseEmpty(data: readonly FieldElement[], offset: number): {
43
+ declare function parseEmpty(_data: readonly FieldElement[], offset: number): {
44
44
  out: null;
45
45
  offset: number;
46
46
  };
@@ -53,17 +53,14 @@ declare function parseOption<T>(type: Parser<T>): (data: readonly FieldElement[]
53
53
  out: null;
54
54
  offset: number;
55
55
  };
56
- declare function parseStruct<T extends {
57
- [key: string]: unknown;
58
- }>(parsers: {
56
+ declare function parseStruct<T extends Record<string, unknown>>(parsers: {
59
57
  [K in keyof T]: {
60
58
  index: number;
61
59
  parser: Parser<T[K]>;
62
60
  };
63
- }): (data: readonly FieldElement[], startingOffset: number) => {
64
- out: Record<string, unknown>;
65
- offset: number;
66
- };
61
+ }): Parser<{
62
+ [K in keyof T]: T[K];
63
+ }>;
67
64
  declare function parseTuple<T extends Parser<unknown>[]>(...parsers: T): Parser<UnwrapParsers<T>>;
68
65
  type UnwrapParsers<TP> = {
69
66
  [Index in keyof TP]: TP[Index] extends Parser<infer U> ? U : never;
package/dist/parser.d.mts CHANGED
@@ -28,7 +28,7 @@ declare function parseU256(data: readonly FieldElement[], offset: number): {
28
28
  offset: number;
29
29
  };
30
30
  declare function parseAsHex(data: readonly FieldElement[], offset: number): {
31
- out: string;
31
+ out: `0x${string}`;
32
32
  offset: number;
33
33
  };
34
34
  declare const parseContractAddress: typeof parseAsHex;
@@ -40,7 +40,7 @@ declare function parseFelt252(data: readonly FieldElement[], offset: number): {
40
40
  out: bigint;
41
41
  offset: number;
42
42
  };
43
- declare function parseEmpty(data: readonly FieldElement[], offset: number): {
43
+ declare function parseEmpty(_data: readonly FieldElement[], offset: number): {
44
44
  out: null;
45
45
  offset: number;
46
46
  };
@@ -53,17 +53,14 @@ declare function parseOption<T>(type: Parser<T>): (data: readonly FieldElement[]
53
53
  out: null;
54
54
  offset: number;
55
55
  };
56
- declare function parseStruct<T extends {
57
- [key: string]: unknown;
58
- }>(parsers: {
56
+ declare function parseStruct<T extends Record<string, unknown>>(parsers: {
59
57
  [K in keyof T]: {
60
58
  index: number;
61
59
  parser: Parser<T[K]>;
62
60
  };
63
- }): (data: readonly FieldElement[], startingOffset: number) => {
64
- out: Record<string, unknown>;
65
- offset: number;
66
- };
61
+ }): Parser<{
62
+ [K in keyof T]: T[K];
63
+ }>;
67
64
  declare function parseTuple<T extends Parser<unknown>[]>(...parsers: T): Parser<UnwrapParsers<T>>;
68
65
  type UnwrapParsers<TP> = {
69
66
  [Index in keyof TP]: TP[Index] extends Parser<infer U> ? U : never;
package/dist/parser.d.ts CHANGED
@@ -28,7 +28,7 @@ declare function parseU256(data: readonly FieldElement[], offset: number): {
28
28
  offset: number;
29
29
  };
30
30
  declare function parseAsHex(data: readonly FieldElement[], offset: number): {
31
- out: string;
31
+ out: `0x${string}`;
32
32
  offset: number;
33
33
  };
34
34
  declare const parseContractAddress: typeof parseAsHex;
@@ -40,7 +40,7 @@ declare function parseFelt252(data: readonly FieldElement[], offset: number): {
40
40
  out: bigint;
41
41
  offset: number;
42
42
  };
43
- declare function parseEmpty(data: readonly FieldElement[], offset: number): {
43
+ declare function parseEmpty(_data: readonly FieldElement[], offset: number): {
44
44
  out: null;
45
45
  offset: number;
46
46
  };
@@ -53,17 +53,14 @@ declare function parseOption<T>(type: Parser<T>): (data: readonly FieldElement[]
53
53
  out: null;
54
54
  offset: number;
55
55
  };
56
- declare function parseStruct<T extends {
57
- [key: string]: unknown;
58
- }>(parsers: {
56
+ declare function parseStruct<T extends Record<string, unknown>>(parsers: {
59
57
  [K in keyof T]: {
60
58
  index: number;
61
59
  parser: Parser<T[K]>;
62
60
  };
63
- }): (data: readonly FieldElement[], startingOffset: number) => {
64
- out: Record<string, unknown>;
65
- offset: number;
66
- };
61
+ }): Parser<{
62
+ [K in keyof T]: T[K];
63
+ }>;
67
64
  declare function parseTuple<T extends Parser<unknown>[]>(...parsers: T): Parser<UnwrapParsers<T>>;
68
65
  type UnwrapParsers<TP> = {
69
66
  [Index in keyof TP]: TP[Index] extends Parser<infer U> ? U : never;
package/dist/parser.mjs CHANGED
@@ -35,7 +35,7 @@ function parseU256(data, offset) {
35
35
  function parseAsHex(data, offset) {
36
36
  assertInBounds(data, offset);
37
37
  return {
38
- out: String(data[offset]),
38
+ out: data[offset],
39
39
  offset: offset + 1
40
40
  };
41
41
  }
@@ -51,7 +51,7 @@ function parseFelt252(data, offset) {
51
51
  offset: offset + 1
52
52
  };
53
53
  }
54
- function parseEmpty(data, offset) {
54
+ function parseEmpty(_data, offset) {
55
55
  return { out: null, offset };
56
56
  }
57
57
  function parseArray(type) {
@@ -82,16 +82,17 @@ function parseStruct(parsers) {
82
82
  const sortedParsers = Object.entries(parsers).sort(
83
83
  (a, b) => a[1].index - b[1].index
84
84
  );
85
- return (data, startingOffset) => {
85
+ const parser = (data, startingOffset) => {
86
86
  let offset = startingOffset;
87
87
  const out = {};
88
- for (const [key, { parser }] of sortedParsers) {
89
- const { out: value, offset: newOffset } = parser(data, offset);
88
+ for (const [key, { parser: parser2 }] of sortedParsers) {
89
+ const { out: value, offset: newOffset } = parser2(data, offset);
90
90
  out[key] = value;
91
91
  offset = newOffset;
92
92
  }
93
93
  return { out, offset };
94
94
  };
95
+ return parser;
95
96
  }
96
97
  function parseTuple(...parsers) {
97
98
  return (data, startingOffset) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apibara/starknet",
3
- "version": "2.1.0-beta.24",
3
+ "version": "2.1.0-beta.26",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -27,8 +27,8 @@
27
27
  "build": "pnpm build:proto && unbuild",
28
28
  "build:proto": "buf generate proto",
29
29
  "typecheck": "tsc --noEmit",
30
- "test": "vitest",
31
- "test:ci": "vitest run",
30
+ "test": "vitest --typecheck",
31
+ "test:ci": "vitest run --typecheck",
32
32
  "lint": "biome check .",
33
33
  "lint:fix": "pnpm lint --write",
34
34
  "format": "biome format . --write"
@@ -42,7 +42,7 @@
42
42
  "vitest": "^1.6.0"
43
43
  },
44
44
  "dependencies": {
45
- "@apibara/protocol": "2.1.0-beta.24",
45
+ "@apibara/protocol": "2.1.0-beta.26",
46
46
  "@scure/starknet": "^1.1.0",
47
47
  "abi-wan-kanabi": "^2.2.4",
48
48
  "effect": "^3.2.6",
@@ -0,0 +1,140 @@
1
+ /*
2
+
3
+ This file extends "abi-wan-kanabi" to provide a more type-safe way to decode events.
4
+
5
+ https://github.com/keep-starknet-strange/abi-wan-kanabi
6
+
7
+ This is free and unencumbered software released into the public domain.
8
+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
9
+ In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11
+ For more information, please refer to https://unlicense.org
12
+
13
+ */
14
+
15
+ import type { Abi } from "abi-wan-kanabi";
16
+ import type {
17
+ AbiEventMember,
18
+ ExtractAbiEvent,
19
+ ExtractAbiEventNames,
20
+ StringToPrimitiveType,
21
+ } from "abi-wan-kanabi/kanabi";
22
+
23
+ export type AbiEventStruct = {
24
+ type: "event";
25
+ name: string;
26
+ kind: "struct";
27
+ members: AbiEventMember[];
28
+ };
29
+
30
+ export type AbiMember = {
31
+ name: string;
32
+ type: string;
33
+ };
34
+
35
+ export type AbiEventEnum = {
36
+ type: "event";
37
+ name: string;
38
+ kind: "enum";
39
+ variants: AbiEventMember[];
40
+ };
41
+
42
+ export type AbiEvent = AbiEventStruct | AbiEventEnum;
43
+
44
+ export type AbiItem = Abi[number];
45
+
46
+ export type DecodeEventArgs<
47
+ TAbi extends Abi = Abi,
48
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
49
+ TStrict extends boolean = true,
50
+ > = {
51
+ abi: TAbi;
52
+ eventName: TEventName;
53
+ event: Event;
54
+ strict?: TStrict;
55
+ };
56
+
57
+ export type DecodedEvent<
58
+ TAbi extends Abi = Abi,
59
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
60
+ > = Event & {
61
+ eventName: TEventName;
62
+ args: EventToPrimitiveType<TAbi, TEventName>;
63
+ };
64
+
65
+ export type DecodeEventReturn<
66
+ TAbi extends Abi = Abi,
67
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
68
+ TStrict extends boolean = true,
69
+ > = TStrict extends true
70
+ ? DecodedEvent<TAbi, TEventName>
71
+ : DecodedEvent<TAbi, TEventName> | null;
72
+
73
+ // Helper type to resolve the payload type of a nested variant.
74
+ // when the type name corresponds to an event; resolves it using EventToPrimitiveType,
75
+ export type ResolveNestedVariantType<
76
+ TAbi extends Abi,
77
+ TTypeName extends string,
78
+ > = EventToPrimitiveType<TAbi, TTypeName>; // resolve its structure recursively
79
+
80
+ // Helper type to convert a variant member (nested or flat) into its corresponding tagged union part(s).
81
+ export type VariantToTaggedUnion<
82
+ TAbi extends Abi,
83
+ TVariant extends AbiEventMember,
84
+ > = TVariant extends { kind: "nested" }
85
+ ? // Nested: Use the helper to resolve the payload type.
86
+ { _tag: TVariant["name"] } & {
87
+ [K in TVariant["name"]]: ResolveNestedVariantType<TAbi, TVariant["type"]>;
88
+ }
89
+ : TVariant extends { kind: "flat" }
90
+ ? // Flat: Recursively call EventToPrimitiveType on the referenced event type.
91
+ // This will return the union of tagged types for the nested event.
92
+ EventToPrimitiveType<TAbi, TVariant["type"]>
93
+ : never; // Should not happen for valid ABIs
94
+
95
+ // Main type to convert an event definition (struct or enum) to its TS representation.
96
+ export type EventToPrimitiveType<
97
+ TAbi extends Abi,
98
+ TEventName extends ExtractAbiEventNames<TAbi>,
99
+ > = ExtractAbiEvent<TAbi, TEventName> extends infer TEventDef
100
+ ? TEventDef extends {
101
+ type: "event";
102
+ kind: "struct";
103
+ members: infer TMembers extends readonly AbiEventMember[];
104
+ }
105
+ ? // Struct Event: A simple object with member names as keys and their primitive types as values.
106
+ {
107
+ [Member in TMembers[number] as Member["name"]]: StringToPrimitiveType<
108
+ TAbi,
109
+ Member["type"]
110
+ >;
111
+ }
112
+ : TEventDef extends {
113
+ type: "event";
114
+ kind: "enum";
115
+ variants: infer TVariants extends readonly AbiEventMember[];
116
+ }
117
+ ? // Enum Event: Create a union of all possible tagged types derived from its variants.
118
+ {
119
+ // Map each variant to its corresponding tagged union structure(s).
120
+ [Idx in keyof TVariants]: VariantToTaggedUnion<TAbi, TVariants[Idx]>;
121
+ }[number] // Indexing with [number] converts the tuple of union parts into a single union type.
122
+ : // Explicitly handle empty enum events to ensure the `extends never` check works reliably.
123
+ TEventDef extends { type: "event"; kind: "enum"; variants: [] }
124
+ ? never
125
+ : // Not an event definition found for TEventName -> never
126
+ never
127
+ : // If the event name is not found in the ABI, return never.
128
+ never;
129
+
130
+ export function isEventAbi(item: AbiItem): item is AbiEvent {
131
+ return item.type === "event";
132
+ }
133
+
134
+ export function isStructEventAbi(item: AbiItem): item is AbiEventStruct {
135
+ return isEventAbi(item) && item.kind === "struct";
136
+ }
137
+
138
+ export function isEnumEventAbi(item: AbiItem): item is AbiEventEnum {
139
+ return isEventAbi(item) && item.kind === "enum";
140
+ }
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 {
@@ -16,6 +15,16 @@ import {
16
15
  isPrimitiveType,
17
16
  isSpanType,
18
17
  } from "./abi";
18
+ import {
19
+ type AbiEvent,
20
+ type AbiEventEnum,
21
+ type AbiEventStruct,
22
+ type AbiMember,
23
+ type EventToPrimitiveType,
24
+ isEnumEventAbi,
25
+ isEventAbi,
26
+ isStructEventAbi,
27
+ } from "./abi-wan-helpers";
19
28
  import type { Event } from "./block";
20
29
  import {
21
30
  ParseError,
@@ -78,61 +87,184 @@ export function decodeEvent<
78
87
  (item) => item.name === eventName && item.type === "event",
79
88
  );
80
89
 
81
- if (!eventAbi || eventAbi.type !== "event") {
90
+ if (!eventAbi || !isEventAbi(eventAbi)) {
82
91
  if (strict) {
83
92
  throw new DecodeEventError(`Event ${eventName} not found in ABI`);
84
93
  }
85
-
86
94
  return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
87
95
  }
88
96
 
89
- if (eventAbi.kind === "enum") {
90
- throw new DecodeEventError("enum: not implemented");
97
+ try {
98
+ if (isStructEventAbi(eventAbi)) {
99
+ return decodeStructEvent(abi, eventAbi, event, eventName);
100
+ }
101
+
102
+ if (isEnumEventAbi(eventAbi)) {
103
+ return decodeEnumEvent(abi, eventAbi, event, eventName);
104
+ }
105
+
106
+ throw new DecodeEventError(
107
+ `Unsupported event kind: ${(eventAbi as AbiEvent)?.kind}`,
108
+ );
109
+ } catch (error) {
110
+ if (
111
+ (error instanceof DecodeEventError || error instanceof ParseError) &&
112
+ !strict
113
+ ) {
114
+ return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
115
+ }
116
+
117
+ throw error;
91
118
  }
119
+ }
92
120
 
121
+ function decodeStructEvent<
122
+ TAbi extends Abi = Abi,
123
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
124
+ >(
125
+ abi: TAbi,
126
+ eventAbi: AbiEventStruct,
127
+ event: Event,
128
+ eventName: TEventName,
129
+ ): DecodedEvent<TAbi, TEventName> {
93
130
  const selector = BigInt(getEventSelector(eventName));
94
131
  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>;
132
+ throw new DecodeEventError(
133
+ `Selector mismatch. Expected ${selector}, got ${event.keys?.[0]}`,
134
+ );
102
135
  }
103
136
 
104
137
  const keysAbi = eventAbi.members.filter((m) => m.kind === "key");
105
138
  const dataAbi = eventAbi.members.filter((m) => m.kind === "data");
106
139
 
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
- }
140
+ const keysParser = compileEventMembers(abi, keysAbi);
141
+ const dataParser = compileEventMembers(abi, dataAbi);
129
142
 
130
- if (error instanceof ParseError && !strict) {
131
- return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
143
+ const keysWithoutSelector = event.keys?.slice(1) ?? [];
144
+
145
+ const { out: decodedKeys } = keysParser(keysWithoutSelector, 0);
146
+ const { out: decodedData } = dataParser(event.data ?? [], 0);
147
+
148
+ const decoded = {
149
+ ...decodedKeys,
150
+ ...decodedData,
151
+ } as EventToPrimitiveType<TAbi, TEventName>;
152
+
153
+ return {
154
+ ...event,
155
+ eventName,
156
+ args: decoded,
157
+ } as DecodedEvent<TAbi, TEventName>;
158
+ }
159
+
160
+ function decodeEnumEvent<
161
+ TAbi extends Abi = Abi,
162
+ TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
163
+ >(
164
+ abi: TAbi,
165
+ eventAbi: AbiEventEnum,
166
+ event: Event,
167
+ eventName: TEventName,
168
+ ): DecodedEvent<TAbi, TEventName> {
169
+ if (!event.keys || event.keys.length === 0) {
170
+ throw new DecodeEventError(
171
+ "Event has no keys; cannot determine variant selector",
172
+ );
173
+ }
174
+
175
+ const variants = eventAbi.variants;
176
+
177
+ const variantSelector = event.keys[0];
178
+
179
+ // Create a map of all possible selectors to their variant paths
180
+ const selectorToVariant = buildVariantSelectorMap(abi, variants);
181
+
182
+ // Find the matching variant and path
183
+ const matchingVariant = selectorToVariant[variantSelector];
184
+
185
+ if (!matchingVariant) {
186
+ throw new DecodeEventError(
187
+ `No matching variant found for selector: ${variantSelector}`,
188
+ );
189
+ }
190
+
191
+ const structEventAbi = abi.find(
192
+ (item) =>
193
+ item.name === matchingVariant.variant.type && item.type === "event",
194
+ );
195
+
196
+ if (!structEventAbi || !isStructEventAbi(structEventAbi)) {
197
+ throw new DecodeEventError(
198
+ `Nested event type not found or not a struct: ${matchingVariant.variant.type}`,
199
+ );
200
+ }
201
+
202
+ const decodedStruct = decodeStructEvent(
203
+ abi,
204
+ structEventAbi,
205
+ event,
206
+ matchingVariant.variant.name,
207
+ );
208
+
209
+ return {
210
+ ...event,
211
+ eventName,
212
+ args: {
213
+ _tag: matchingVariant.variant.name,
214
+ [matchingVariant.variant.name]: decodedStruct.args,
215
+ },
216
+ } as DecodedEvent<TAbi, TEventName>;
217
+ }
218
+
219
+ type EnumFlatVariantMap = Record<
220
+ string,
221
+ { variant: AbiEventMember; path: string[] }
222
+ >;
223
+
224
+ // Helper to build a map of all possible selectors to their variant paths
225
+ function buildVariantSelectorMap(
226
+ abi: Abi,
227
+ variants: AbiEventMember[],
228
+ ): EnumFlatVariantMap {
229
+ const selectorMap: EnumFlatVariantMap = {};
230
+
231
+ for (const variant of variants) {
232
+ // For nested events, just map the variant's own selector
233
+ if (variant.kind === "nested") {
234
+ const selector = getEventSelector(variant.name);
235
+ selectorMap[selector] = { variant, path: [variant.name] };
132
236
  }
237
+ // For flat events, recursively map all possible selectors from the event hierarchy
238
+ else if (variant.kind === "flat") {
239
+ const flatEventName = variant.type;
240
+ const flatEventAbi = abi.find(
241
+ (item) => item.name === flatEventName && item.type === "event",
242
+ );
133
243
 
134
- throw error;
244
+ // Skip if the flat event type is not found
245
+ if (!flatEventAbi) {
246
+ continue;
247
+ }
248
+
249
+ if (isEnumEventAbi(flatEventAbi)) {
250
+ // For enum events, recursively map all their variants
251
+ const nestedMap = buildVariantSelectorMap(abi, flatEventAbi.variants);
252
+
253
+ // Add this variant to the path for all nested selectors
254
+ for (const [
255
+ nestedSelector,
256
+ { variant: nestedVariant, path: nestedPath },
257
+ ] of Object.entries(nestedMap)) {
258
+ selectorMap[nestedSelector] = {
259
+ variant: nestedVariant,
260
+ path: [variant.name, ...nestedPath],
261
+ };
262
+ }
263
+ }
264
+ }
135
265
  }
266
+
267
+ return selectorMap;
136
268
  }
137
269
 
138
270
  function compileEventMembers<T extends Record<string, unknown>>(
@@ -176,18 +308,16 @@ function compileTypeParser(abi: Abi, type: string): Parser<unknown> {
176
308
  case "struct": {
177
309
  return compileStructParser(abi, typeAbi.members);
178
310
  }
179
- case "enum":
180
- throw new DecodeEventError("enum: not implemented");
311
+ case "enum": {
312
+ // This should never happen anyways as compileTypeParser is only called
313
+ // primitive types or to compile structs parsers.
314
+ throw new DecodeEventError(`Enum types are not supported: ${type}`);
315
+ }
181
316
  default:
182
317
  throw new DecodeEventError(`Invalid type ${typeAbi.type}`);
183
318
  }
184
319
  }
185
320
 
186
- type AbiMember = {
187
- name: string;
188
- type: string;
189
- };
190
-
191
321
  function compileStructParser(
192
322
  abi: Abi,
193
323
  members: readonly AbiMember[],
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@ export { getBigIntSelector, getEventSelector, getSelector } from "./abi";
15
15
 
16
16
  declare module "abi-wan-kanabi" {
17
17
  interface Config {
18
+ AddressType: `0x${string}`;
19
+ ClassHashType: `0x${string}`;
18
20
  FeltType: bigint;
19
21
  BigIntType: bigint;
20
22
  U256Type: bigint;
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,13 @@ export function parseOption<T>(type: Parser<T>) {
132
132
  };
133
133
  }
134
134
 
135
- export function parseStruct<T extends { [key: string]: unknown }>(
135
+ export function parseStruct<T extends Record<string, unknown>>(
136
136
  parsers: { [K in keyof T]: { index: number; parser: Parser<T[K]> } },
137
- ) {
137
+ ): Parser<{ [K in keyof T]: T[K] }> {
138
138
  const sortedParsers = Object.entries(parsers).sort(
139
139
  (a, b) => a[1].index - b[1].index,
140
140
  );
141
- return (data: readonly FieldElement[], startingOffset: number) => {
141
+ const parser = (data: readonly FieldElement[], startingOffset: number) => {
142
142
  let offset = startingOffset;
143
143
  const out: Record<string, unknown> = {};
144
144
  for (const [key, { parser }] of sortedParsers) {
@@ -148,6 +148,7 @@ export function parseStruct<T extends { [key: string]: unknown }>(
148
148
  }
149
149
  return { out, offset };
150
150
  };
151
+ return parser as Parser<{ [K in keyof T]: T[K] }>;
151
152
  }
152
153
 
153
154
  export function parseTuple<T extends Parser<unknown>[]>(