@apibara/starknet 2.0.0-beta.4 → 2.0.0-beta.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +7711 -0
- package/dist/index.d.cts +5838 -0
- package/dist/index.d.mts +5838 -0
- package/dist/index.d.ts +5838 -0
- package/dist/index.mjs +7616 -0
- package/dist/parser.cjs +133 -0
- package/dist/parser.d.cts +72 -0
- package/dist/parser.d.mts +72 -0
- package/dist/parser.d.ts +72 -0
- package/dist/parser.mjs +109 -0
- package/dist/shared/starknet.2b19268a.d.cts +32 -0
- package/dist/shared/starknet.2b19268a.d.mts +32 -0
- package/dist/shared/starknet.2b19268a.d.ts +32 -0
- package/package.json +17 -11
- package/src/abi.ts +79 -0
- package/src/access.ts +6 -2
- package/src/block.ts +241 -143
- package/src/common.ts +2 -0
- package/src/event.ts +204 -0
- package/src/filter.test.ts +257 -9
- package/src/filter.ts +105 -7
- package/src/index.ts +11 -0
- package/src/parser.test.ts +169 -0
- package/src/parser.ts +170 -0
- package/src/proto/common.ts +1 -1
- package/src/proto/data.ts +905 -17
- package/src/proto/filter.ts +595 -76
- package/src/proto/google/protobuf/timestamp.ts +1 -1
package/src/event.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type { Abi } from "abi-wan-kanabi";
|
|
2
|
+
import type {
|
|
3
|
+
AbiEventMember,
|
|
4
|
+
EventToPrimitiveType,
|
|
5
|
+
ExtractAbiEventNames,
|
|
6
|
+
} from "abi-wan-kanabi/kanabi";
|
|
7
|
+
import {
|
|
8
|
+
PrimitiveTypeParsers,
|
|
9
|
+
getArrayElementType,
|
|
10
|
+
getEventSelector,
|
|
11
|
+
getOptionType,
|
|
12
|
+
getSpanType,
|
|
13
|
+
isArrayType,
|
|
14
|
+
isEmptyType,
|
|
15
|
+
isOptionType,
|
|
16
|
+
isPrimitiveType,
|
|
17
|
+
isSpanType,
|
|
18
|
+
} from "./abi";
|
|
19
|
+
import type { Event } from "./block";
|
|
20
|
+
import {
|
|
21
|
+
ParseError,
|
|
22
|
+
type Parser,
|
|
23
|
+
parseArray,
|
|
24
|
+
parseEmpty,
|
|
25
|
+
parseOption,
|
|
26
|
+
parseSpan,
|
|
27
|
+
parseStruct,
|
|
28
|
+
} from "./parser";
|
|
29
|
+
|
|
30
|
+
export class DecodeEventError extends Error {
|
|
31
|
+
constructor(message: string) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "DecodeEventError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type DecodeEventArgs<
|
|
38
|
+
TAbi extends Abi = Abi,
|
|
39
|
+
TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
|
|
40
|
+
TStrict extends boolean = true,
|
|
41
|
+
> = {
|
|
42
|
+
abi: TAbi;
|
|
43
|
+
eventName: TEventName;
|
|
44
|
+
event: Event;
|
|
45
|
+
strict?: TStrict;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type DecodedEvent<
|
|
49
|
+
TAbi extends Abi = Abi,
|
|
50
|
+
TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
|
|
51
|
+
> = Event & {
|
|
52
|
+
eventName: TEventName;
|
|
53
|
+
args: EventToPrimitiveType<TAbi, TEventName>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type DecodeEventReturn<
|
|
57
|
+
TAbi extends Abi = Abi,
|
|
58
|
+
TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
|
|
59
|
+
TStrict extends boolean = true,
|
|
60
|
+
> = TStrict extends true
|
|
61
|
+
? DecodedEvent<TAbi, TEventName>
|
|
62
|
+
: DecodedEvent<TAbi, TEventName> | null;
|
|
63
|
+
|
|
64
|
+
/** Decodes a single event.
|
|
65
|
+
*
|
|
66
|
+
* If `strict: true`, this function throws on failure. Otherwise, returns null.
|
|
67
|
+
*/
|
|
68
|
+
export function decodeEvent<
|
|
69
|
+
TAbi extends Abi = Abi,
|
|
70
|
+
TEventName extends ExtractAbiEventNames<TAbi> = ExtractAbiEventNames<TAbi>,
|
|
71
|
+
TStrict extends boolean = true,
|
|
72
|
+
>(
|
|
73
|
+
args: DecodeEventArgs<TAbi, TEventName, TStrict>,
|
|
74
|
+
): DecodeEventReturn<TAbi, TEventName, TStrict> {
|
|
75
|
+
const { abi, event, eventName, strict = true } = args;
|
|
76
|
+
|
|
77
|
+
const eventAbi = abi.find(
|
|
78
|
+
(item) => item.name === eventName && item.type === "event",
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (!eventAbi || eventAbi.type !== "event") {
|
|
82
|
+
if (strict) {
|
|
83
|
+
throw new DecodeEventError(`Event ${eventName} not found in ABI`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (eventAbi.kind === "enum") {
|
|
90
|
+
throw new DecodeEventError("enum: not implemented");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const selector = BigInt(getEventSelector(eventName));
|
|
94
|
+
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>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const keysAbi = eventAbi.members.filter((m) => m.kind === "key");
|
|
105
|
+
const dataAbi = eventAbi.members.filter((m) => m.kind === "data");
|
|
106
|
+
|
|
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
|
+
}
|
|
129
|
+
|
|
130
|
+
if (error instanceof ParseError && !strict) {
|
|
131
|
+
return null as DecodeEventReturn<TAbi, TEventName, TStrict>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function compileEventMembers<T extends Record<string, unknown>>(
|
|
139
|
+
abi: Abi,
|
|
140
|
+
members: AbiEventMember[],
|
|
141
|
+
): Parser<T> {
|
|
142
|
+
return compileStructParser(abi, members) as Parser<T>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function compileTypeParser(abi: Abi, type: string): Parser<unknown> {
|
|
146
|
+
if (isPrimitiveType(type)) {
|
|
147
|
+
return PrimitiveTypeParsers[type as keyof typeof PrimitiveTypeParsers];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (isArrayType(type)) {
|
|
151
|
+
const elementType = getArrayElementType(type);
|
|
152
|
+
return parseArray(compileTypeParser(abi, elementType));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (isSpanType(type)) {
|
|
156
|
+
const elementType = getSpanType(type);
|
|
157
|
+
return parseSpan(compileTypeParser(abi, elementType));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (isOptionType(type)) {
|
|
161
|
+
const elementType = getOptionType(type);
|
|
162
|
+
return parseOption(compileTypeParser(abi, elementType));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (isEmptyType(type)) {
|
|
166
|
+
return parseEmpty;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Not a well-known type. Look it up in the ABI.
|
|
170
|
+
const typeAbi = abi.find((item) => item.name === type);
|
|
171
|
+
if (!typeAbi) {
|
|
172
|
+
throw new DecodeEventError(`Type ${type} not found in ABI`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
switch (typeAbi.type) {
|
|
176
|
+
case "struct": {
|
|
177
|
+
return compileStructParser(abi, typeAbi.members);
|
|
178
|
+
}
|
|
179
|
+
case "enum":
|
|
180
|
+
throw new DecodeEventError("enum: not implemented");
|
|
181
|
+
default:
|
|
182
|
+
throw new DecodeEventError(`Invalid type ${typeAbi.type}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
type AbiMember = {
|
|
187
|
+
name: string;
|
|
188
|
+
type: string;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
function compileStructParser(
|
|
192
|
+
abi: Abi,
|
|
193
|
+
members: readonly AbiMember[],
|
|
194
|
+
): Parser<unknown> {
|
|
195
|
+
const parsers: Record<string, { index: number; parser: Parser<unknown> }> =
|
|
196
|
+
{};
|
|
197
|
+
for (const [index, member] of members.entries()) {
|
|
198
|
+
parsers[member.name] = {
|
|
199
|
+
index,
|
|
200
|
+
parser: compileTypeParser(abi, member.type),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return parseStruct(parsers);
|
|
204
|
+
}
|
package/src/filter.test.ts
CHANGED
|
@@ -2,9 +2,12 @@ import { Schema } from "@effect/schema";
|
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
ContractChangeFilter,
|
|
5
6
|
EventFilter,
|
|
6
7
|
HeaderFilter,
|
|
7
8
|
Key,
|
|
9
|
+
NonceUpdateFilter,
|
|
10
|
+
StorageDiffFilter,
|
|
8
11
|
TransactionFilter,
|
|
9
12
|
mergeFilter,
|
|
10
13
|
} from "./filter";
|
|
@@ -14,7 +17,7 @@ describe("HeaderFilter", () => {
|
|
|
14
17
|
const decode = Schema.decodeSync(HeaderFilter);
|
|
15
18
|
|
|
16
19
|
it("should encode and decode", () => {
|
|
17
|
-
const always =
|
|
20
|
+
const always = "always";
|
|
18
21
|
|
|
19
22
|
const proto = encode(always);
|
|
20
23
|
const decoded = decode(proto);
|
|
@@ -468,27 +471,168 @@ describe("TransactionFilter", () => {
|
|
|
468
471
|
});
|
|
469
472
|
});
|
|
470
473
|
|
|
474
|
+
describe("StorageDiffFilter", () => {
|
|
475
|
+
const encode = Schema.encodeSync(StorageDiffFilter);
|
|
476
|
+
const decode = Schema.decodeSync(StorageDiffFilter);
|
|
477
|
+
|
|
478
|
+
it("should encode and decode storage diffs", () => {
|
|
479
|
+
const proto = encode({
|
|
480
|
+
contractAddress: "0xAABBCCDD",
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
484
|
+
{
|
|
485
|
+
"contractAddress": {
|
|
486
|
+
"x0": 0n,
|
|
487
|
+
"x1": 0n,
|
|
488
|
+
"x2": 0n,
|
|
489
|
+
"x3": 2864434397n,
|
|
490
|
+
},
|
|
491
|
+
}
|
|
492
|
+
`);
|
|
493
|
+
const decoded = decode(proto);
|
|
494
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
495
|
+
{
|
|
496
|
+
"contractAddress": "0x00000000000000000000000000000000000000000000000000000000aabbccdd",
|
|
497
|
+
}
|
|
498
|
+
`);
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
describe("ContractChangeFilter", () => {
|
|
503
|
+
const encode = Schema.encodeSync(ContractChangeFilter);
|
|
504
|
+
const decode = Schema.decodeSync(ContractChangeFilter);
|
|
505
|
+
|
|
506
|
+
it("should encode and decode declared class changes", () => {
|
|
507
|
+
const proto = encode({
|
|
508
|
+
change: {
|
|
509
|
+
_tag: "declaredClass",
|
|
510
|
+
declaredClass: {},
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
514
|
+
{
|
|
515
|
+
"change": {
|
|
516
|
+
"$case": "declaredClass",
|
|
517
|
+
"declaredClass": {},
|
|
518
|
+
},
|
|
519
|
+
}
|
|
520
|
+
`);
|
|
521
|
+
const decoded = decode(proto);
|
|
522
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
523
|
+
{
|
|
524
|
+
"change": {
|
|
525
|
+
"_tag": "declaredClass",
|
|
526
|
+
"declaredClass": {},
|
|
527
|
+
},
|
|
528
|
+
}
|
|
529
|
+
`);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("should encode and decode replaced class changes", () => {
|
|
533
|
+
const proto = encode({
|
|
534
|
+
change: {
|
|
535
|
+
_tag: "replacedClass",
|
|
536
|
+
replacedClass: {},
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
540
|
+
{
|
|
541
|
+
"change": {
|
|
542
|
+
"$case": "replacedClass",
|
|
543
|
+
"replacedClass": {},
|
|
544
|
+
},
|
|
545
|
+
}
|
|
546
|
+
`);
|
|
547
|
+
const decoded = decode(proto);
|
|
548
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
549
|
+
{
|
|
550
|
+
"change": {
|
|
551
|
+
"_tag": "replacedClass",
|
|
552
|
+
"replacedClass": {},
|
|
553
|
+
},
|
|
554
|
+
}
|
|
555
|
+
`);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it("should encode and decode deployed contract changes", () => {
|
|
559
|
+
const proto = encode({
|
|
560
|
+
change: {
|
|
561
|
+
_tag: "deployedContract",
|
|
562
|
+
deployedContract: {},
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
566
|
+
{
|
|
567
|
+
"change": {
|
|
568
|
+
"$case": "deployedContract",
|
|
569
|
+
"deployedContract": {},
|
|
570
|
+
},
|
|
571
|
+
}
|
|
572
|
+
`);
|
|
573
|
+
const decoded = decode(proto);
|
|
574
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
575
|
+
{
|
|
576
|
+
"change": {
|
|
577
|
+
"_tag": "deployedContract",
|
|
578
|
+
"deployedContract": {},
|
|
579
|
+
},
|
|
580
|
+
}
|
|
581
|
+
`);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
describe("NonceUpdateFilter", () => {
|
|
586
|
+
const encode = Schema.encodeSync(NonceUpdateFilter);
|
|
587
|
+
const decode = Schema.decodeSync(NonceUpdateFilter);
|
|
588
|
+
|
|
589
|
+
it("should encode and decode nonce updates", () => {
|
|
590
|
+
const proto = encode({
|
|
591
|
+
contractAddress: "0xAABBCCDD",
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
595
|
+
{
|
|
596
|
+
"contractAddress": {
|
|
597
|
+
"x0": 0n,
|
|
598
|
+
"x1": 0n,
|
|
599
|
+
"x2": 0n,
|
|
600
|
+
"x3": 2864434397n,
|
|
601
|
+
},
|
|
602
|
+
}
|
|
603
|
+
`);
|
|
604
|
+
const decoded = decode(proto);
|
|
605
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
606
|
+
{
|
|
607
|
+
"contractAddress": "0x00000000000000000000000000000000000000000000000000000000aabbccdd",
|
|
608
|
+
}
|
|
609
|
+
`);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
471
613
|
describe("mergeFilter", () => {
|
|
472
614
|
it("returns header.always if any has it", () => {
|
|
473
|
-
const fa = mergeFilter({}, { header:
|
|
615
|
+
const fa = mergeFilter({}, { header: "always" });
|
|
474
616
|
expect(fa).toMatchInlineSnapshot(`
|
|
475
617
|
{
|
|
618
|
+
"contractChanges": [],
|
|
476
619
|
"events": [],
|
|
477
|
-
"header":
|
|
478
|
-
"always": true,
|
|
479
|
-
},
|
|
620
|
+
"header": "always",
|
|
480
621
|
"messages": [],
|
|
622
|
+
"nonceUpdates": [],
|
|
623
|
+
"storageDiffs": [],
|
|
481
624
|
"transactions": [],
|
|
482
625
|
}
|
|
483
626
|
`);
|
|
484
|
-
const fb = mergeFilter({ header:
|
|
627
|
+
const fb = mergeFilter({ header: "always" }, {});
|
|
485
628
|
expect(fb).toMatchInlineSnapshot(`
|
|
486
629
|
{
|
|
630
|
+
"contractChanges": [],
|
|
487
631
|
"events": [],
|
|
488
|
-
"header":
|
|
489
|
-
"always": true,
|
|
490
|
-
},
|
|
632
|
+
"header": "always",
|
|
491
633
|
"messages": [],
|
|
634
|
+
"nonceUpdates": [],
|
|
635
|
+
"storageDiffs": [],
|
|
492
636
|
"transactions": [],
|
|
493
637
|
}
|
|
494
638
|
`);
|
|
@@ -498,9 +642,12 @@ describe("mergeFilter", () => {
|
|
|
498
642
|
const f = mergeFilter({}, {});
|
|
499
643
|
expect(f).toMatchInlineSnapshot(`
|
|
500
644
|
{
|
|
645
|
+
"contractChanges": [],
|
|
501
646
|
"events": [],
|
|
502
647
|
"header": undefined,
|
|
503
648
|
"messages": [],
|
|
649
|
+
"nonceUpdates": [],
|
|
650
|
+
"storageDiffs": [],
|
|
504
651
|
"transactions": [],
|
|
505
652
|
}
|
|
506
653
|
`);
|
|
@@ -517,9 +664,12 @@ describe("mergeFilter", () => {
|
|
|
517
664
|
);
|
|
518
665
|
expect(f).toMatchInlineSnapshot(`
|
|
519
666
|
{
|
|
667
|
+
"contractChanges": [],
|
|
520
668
|
"events": [],
|
|
521
669
|
"header": undefined,
|
|
522
670
|
"messages": [],
|
|
671
|
+
"nonceUpdates": [],
|
|
672
|
+
"storageDiffs": [],
|
|
523
673
|
"transactions": [
|
|
524
674
|
{
|
|
525
675
|
"transactionType": {
|
|
@@ -545,6 +695,7 @@ describe("mergeFilter", () => {
|
|
|
545
695
|
);
|
|
546
696
|
expect(f).toMatchInlineSnapshot(`
|
|
547
697
|
{
|
|
698
|
+
"contractChanges": [],
|
|
548
699
|
"events": [
|
|
549
700
|
{
|
|
550
701
|
"address": "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
@@ -555,6 +706,8 @@ describe("mergeFilter", () => {
|
|
|
555
706
|
],
|
|
556
707
|
"header": undefined,
|
|
557
708
|
"messages": [],
|
|
709
|
+
"nonceUpdates": [],
|
|
710
|
+
"storageDiffs": [],
|
|
558
711
|
"transactions": [],
|
|
559
712
|
}
|
|
560
713
|
`);
|
|
@@ -567,6 +720,7 @@ describe("mergeFilter", () => {
|
|
|
567
720
|
);
|
|
568
721
|
expect(f).toMatchInlineSnapshot(`
|
|
569
722
|
{
|
|
723
|
+
"contractChanges": [],
|
|
570
724
|
"events": [],
|
|
571
725
|
"header": undefined,
|
|
572
726
|
"messages": [
|
|
@@ -577,6 +731,100 @@ describe("mergeFilter", () => {
|
|
|
577
731
|
"fromAddress": "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
|
578
732
|
},
|
|
579
733
|
],
|
|
734
|
+
"nonceUpdates": [],
|
|
735
|
+
"storageDiffs": [],
|
|
736
|
+
"transactions": [],
|
|
737
|
+
}
|
|
738
|
+
`);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it("concatenates storage diffs", () => {
|
|
742
|
+
const f = mergeFilter(
|
|
743
|
+
{ storageDiffs: [{ contractAddress: "0xAABBCCDD" }] },
|
|
744
|
+
{ storageDiffs: [{ contractAddress: "0xBBBBCCDD" }] },
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
expect(f).toMatchInlineSnapshot(`
|
|
748
|
+
{
|
|
749
|
+
"contractChanges": [],
|
|
750
|
+
"events": [],
|
|
751
|
+
"header": undefined,
|
|
752
|
+
"messages": [],
|
|
753
|
+
"nonceUpdates": [],
|
|
754
|
+
"storageDiffs": [
|
|
755
|
+
{
|
|
756
|
+
"contractAddress": "0xAABBCCDD",
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
"contractAddress": "0xBBBBCCDD",
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
"transactions": [],
|
|
763
|
+
}
|
|
764
|
+
`);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it("concatenates contract changes", () => {
|
|
768
|
+
const f = mergeFilter(
|
|
769
|
+
{
|
|
770
|
+
contractChanges: [
|
|
771
|
+
{ change: { _tag: "declaredClass", declaredClass: {} } },
|
|
772
|
+
],
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
contractChanges: [
|
|
776
|
+
{ change: { _tag: "replacedClass", replacedClass: {} } },
|
|
777
|
+
],
|
|
778
|
+
},
|
|
779
|
+
);
|
|
780
|
+
|
|
781
|
+
expect(f).toMatchInlineSnapshot(`
|
|
782
|
+
{
|
|
783
|
+
"contractChanges": [
|
|
784
|
+
{
|
|
785
|
+
"change": {
|
|
786
|
+
"_tag": "declaredClass",
|
|
787
|
+
"declaredClass": {},
|
|
788
|
+
},
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
"change": {
|
|
792
|
+
"_tag": "replacedClass",
|
|
793
|
+
"replacedClass": {},
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
],
|
|
797
|
+
"events": [],
|
|
798
|
+
"header": undefined,
|
|
799
|
+
"messages": [],
|
|
800
|
+
"nonceUpdates": [],
|
|
801
|
+
"storageDiffs": [],
|
|
802
|
+
"transactions": [],
|
|
803
|
+
}
|
|
804
|
+
`);
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
it("concatenates nonce updates", () => {
|
|
808
|
+
const f = mergeFilter(
|
|
809
|
+
{ nonceUpdates: [{ contractAddress: "0xAABBCCDD" }] },
|
|
810
|
+
{ nonceUpdates: [{ contractAddress: "0xBBBBCCDD" }] },
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
expect(f).toMatchInlineSnapshot(`
|
|
814
|
+
{
|
|
815
|
+
"contractChanges": [],
|
|
816
|
+
"events": [],
|
|
817
|
+
"header": undefined,
|
|
818
|
+
"messages": [],
|
|
819
|
+
"nonceUpdates": [
|
|
820
|
+
{
|
|
821
|
+
"contractAddress": "0xAABBCCDD",
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
"contractAddress": "0xBBBBCCDD",
|
|
825
|
+
},
|
|
826
|
+
],
|
|
827
|
+
"storageDiffs": [],
|
|
580
828
|
"transactions": [],
|
|
581
829
|
}
|
|
582
830
|
`);
|
package/src/filter.ts
CHANGED
|
@@ -6,11 +6,39 @@ import * as proto from "./proto";
|
|
|
6
6
|
|
|
7
7
|
/** Header options.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* - `always`: receive all block headers.
|
|
10
|
+
* - `on_data`: receive headers only if any other filter matches.
|
|
11
|
+
* - `on_data_or_on_new_block`: receive headers only if any other filter matches and for "live" blocks.
|
|
10
12
|
*/
|
|
11
|
-
export const HeaderFilter = Schema.
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
export const HeaderFilter = Schema.transform(
|
|
14
|
+
Schema.Enums(proto.filter.HeaderFilter),
|
|
15
|
+
Schema.Literal("always", "on_data", "on_data_or_on_new_block", "unknown"),
|
|
16
|
+
{
|
|
17
|
+
decode(value) {
|
|
18
|
+
const enumMap = {
|
|
19
|
+
[proto.filter.HeaderFilter.ALWAYS]: "always",
|
|
20
|
+
[proto.filter.HeaderFilter.ON_DATA]: "on_data",
|
|
21
|
+
[proto.filter.HeaderFilter.ON_DATA_OR_ON_NEW_BLOCK]:
|
|
22
|
+
"on_data_or_on_new_block",
|
|
23
|
+
[proto.filter.HeaderFilter.UNSPECIFIED]: "unknown",
|
|
24
|
+
[proto.filter.HeaderFilter.UNRECOGNIZED]: "unknown",
|
|
25
|
+
} as const;
|
|
26
|
+
return enumMap[value] ?? "unknown";
|
|
27
|
+
},
|
|
28
|
+
encode(value) {
|
|
29
|
+
switch (value) {
|
|
30
|
+
case "always":
|
|
31
|
+
return proto.filter.HeaderFilter.ALWAYS;
|
|
32
|
+
case "on_data":
|
|
33
|
+
return proto.filter.HeaderFilter.ON_DATA;
|
|
34
|
+
case "on_data_or_on_new_block":
|
|
35
|
+
return proto.filter.HeaderFilter.ON_DATA_OR_ON_NEW_BLOCK;
|
|
36
|
+
default:
|
|
37
|
+
return proto.filter.HeaderFilter.UNSPECIFIED;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
);
|
|
14
42
|
|
|
15
43
|
export type HeaderFilter = typeof HeaderFilter.Type;
|
|
16
44
|
|
|
@@ -202,11 +230,68 @@ export const TransactionFilter = Schema.Struct({
|
|
|
202
230
|
|
|
203
231
|
export type TransactionFilter = typeof TransactionFilter.Type;
|
|
204
232
|
|
|
233
|
+
/** Filter storage diffs.
|
|
234
|
+
*
|
|
235
|
+
* @prop contractAddress Filter by contract address.
|
|
236
|
+
*/
|
|
237
|
+
export const StorageDiffFilter = Schema.Struct({
|
|
238
|
+
id: Schema.optional(Schema.Number),
|
|
239
|
+
contractAddress: Schema.optional(FieldElement),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
export type StorageDiffFilter = typeof StorageDiffFilter.Type;
|
|
243
|
+
|
|
244
|
+
/** Filter declared classes. */
|
|
245
|
+
export const DeclaredClassFilter = Schema.Struct({
|
|
246
|
+
_tag: tag("declaredClass"),
|
|
247
|
+
declaredClass: Schema.Struct({}),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
export type DeclaredClassFilter = typeof DeclaredClassFilter.Type;
|
|
251
|
+
|
|
252
|
+
export const ReplacedClassFilter = Schema.Struct({
|
|
253
|
+
_tag: tag("replacedClass"),
|
|
254
|
+
replacedClass: Schema.Struct({}),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export type ReplacedClassFilter = typeof ReplacedClassFilter.Type;
|
|
258
|
+
|
|
259
|
+
export const DeployedContractFilter = Schema.Struct({
|
|
260
|
+
_tag: tag("deployedContract"),
|
|
261
|
+
deployedContract: Schema.Struct({}),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
export type DeployedContractFilter = typeof DeployedContractFilter.Type;
|
|
265
|
+
|
|
266
|
+
/** Filter contract changes. */
|
|
267
|
+
export const ContractChangeFilter = Schema.Struct({
|
|
268
|
+
id: Schema.optional(Schema.Number),
|
|
269
|
+
change: Schema.optional(
|
|
270
|
+
Schema.Union(
|
|
271
|
+
DeclaredClassFilter,
|
|
272
|
+
ReplacedClassFilter,
|
|
273
|
+
DeployedContractFilter,
|
|
274
|
+
),
|
|
275
|
+
),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
/** Filter updates to nonces.
|
|
279
|
+
*
|
|
280
|
+
* @prop contractAddress Filter by contract address.
|
|
281
|
+
*/
|
|
282
|
+
export const NonceUpdateFilter = Schema.Struct({
|
|
283
|
+
id: Schema.optional(Schema.Number),
|
|
284
|
+
contractAddress: Schema.optional(FieldElement),
|
|
285
|
+
});
|
|
286
|
+
|
|
205
287
|
export const Filter = Schema.Struct({
|
|
206
288
|
header: Schema.optional(HeaderFilter),
|
|
207
289
|
transactions: Schema.optional(Schema.Array(TransactionFilter)),
|
|
208
290
|
events: Schema.optional(Schema.Array(EventFilter)),
|
|
209
291
|
messages: Schema.optional(Schema.Array(MessageToL1Filter)),
|
|
292
|
+
storageDiffs: Schema.optional(Schema.Array(StorageDiffFilter)),
|
|
293
|
+
contractChanges: Schema.optional(Schema.Array(ContractChangeFilter)),
|
|
294
|
+
nonceUpdates: Schema.optional(Schema.Array(NonceUpdateFilter)),
|
|
210
295
|
});
|
|
211
296
|
|
|
212
297
|
export type Filter = typeof Filter.Type;
|
|
@@ -238,6 +323,12 @@ export function mergeFilter(a: Filter, b: Filter): Filter {
|
|
|
238
323
|
transactions: [...(a.transactions ?? []), ...(b.transactions ?? [])],
|
|
239
324
|
events: [...(a.events ?? []), ...(b.events ?? [])],
|
|
240
325
|
messages: [...(a.messages ?? []), ...(b.messages ?? [])],
|
|
326
|
+
storageDiffs: [...(a.storageDiffs ?? []), ...(b.storageDiffs ?? [])],
|
|
327
|
+
contractChanges: [
|
|
328
|
+
...(a.contractChanges ?? []),
|
|
329
|
+
...(b.contractChanges ?? []),
|
|
330
|
+
],
|
|
331
|
+
nonceUpdates: [...(a.nonceUpdates ?? []), ...(b.nonceUpdates ?? [])],
|
|
241
332
|
};
|
|
242
333
|
}
|
|
243
334
|
|
|
@@ -251,7 +342,14 @@ function mergeHeaderFilter(
|
|
|
251
342
|
if (b === undefined) {
|
|
252
343
|
return a;
|
|
253
344
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
345
|
+
|
|
346
|
+
if (a === "always" || b === "always") {
|
|
347
|
+
return "always";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (a === "on_data_or_on_new_block" || b === "on_data_or_on_new_block") {
|
|
351
|
+
return "on_data_or_on_new_block";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return "on_data";
|
|
257
355
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StreamConfig } from "@apibara/protocol";
|
|
2
|
+
export type { Abi } from "abi-wan-kanabi";
|
|
2
3
|
import { BlockFromBytes } from "./block";
|
|
3
4
|
import { FilterFromBytes, mergeFilter } from "./filter";
|
|
4
5
|
|
|
@@ -9,6 +10,16 @@ export * from "./filter";
|
|
|
9
10
|
export * from "./block";
|
|
10
11
|
|
|
11
12
|
export * from "./access";
|
|
13
|
+
export * from "./event";
|
|
14
|
+
export { getBigIntSelector, getEventSelector, getSelector } from "./abi";
|
|
15
|
+
|
|
16
|
+
declare module "abi-wan-kanabi" {
|
|
17
|
+
interface Config {
|
|
18
|
+
FeltType: bigint;
|
|
19
|
+
BigIntType: bigint;
|
|
20
|
+
U256Type: bigint;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
export const StarknetStream = new StreamConfig(
|
|
14
25
|
FilterFromBytes,
|