@apibara/starknet 2.1.0-beta.24 → 2.1.0-beta.25
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 +113 -32
- package/dist/index.d.cts +32 -1
- package/dist/index.d.mts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.mjs +113 -32
- package/dist/parser.cjs +6 -5
- package/dist/parser.d.cts +6 -9
- package/dist/parser.d.mts +6 -9
- package/dist/parser.d.ts +6 -9
- package/dist/parser.mjs +6 -5
- package/package.json +4 -4
- package/src/abi-wan-helpers.ts +140 -0
- package/src/event.ts +174 -44
- package/src/index.ts +2 -0
- package/src/parser.ts +6 -5
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
|
|
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
|
-
|
|
8390
|
-
|
|
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
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
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
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
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
|
-
|
|
8423
|
-
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
8383
|
-
|
|
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
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
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
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
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
|
-
|
|
8416
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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 } =
|
|
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(
|
|
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
|
-
}):
|
|
64
|
-
|
|
65
|
-
|
|
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(
|
|
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
|
-
}):
|
|
64
|
-
|
|
65
|
-
|
|
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(
|
|
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
|
-
}):
|
|
64
|
-
|
|
65
|
-
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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 } =
|
|
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.
|
|
3
|
+
"version": "2.1.0-beta.25",
|
|
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.
|
|
45
|
+
"@apibara/protocol": "2.1.0-beta.25",
|
|
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
|
|
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
|
-
|
|
90
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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
|
|
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
|
-
|
|
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>[]>(
|