@apibara/starknet 2.0.0-beta.7 → 2.0.0-beta.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apibara/starknet",
3
- "version": "2.0.0-beta.7",
3
+ "version": "2.0.0-beta.9",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -36,7 +36,7 @@
36
36
  "vitest": "^1.6.0"
37
37
  },
38
38
  "dependencies": {
39
- "@apibara/protocol": "2.0.0-beta.7",
39
+ "@apibara/protocol": "2.0.0-beta.9",
40
40
  "@effect/schema": "^0.67.15",
41
41
  "effect": "^3.2.6",
42
42
  "long": "^5.2.1",
package/src/block.ts CHANGED
@@ -479,6 +479,103 @@ export const MessageToL1 = Schema.Struct({
479
479
 
480
480
  export type MessageToL1 = typeof MessageToL1.Type;
481
481
 
482
+ /** An entry in the storage diff.
483
+ *
484
+ * @prop key The storage location.
485
+ * @prop value The new value at the storage location.
486
+ */
487
+ export const StorageEntry = Schema.Struct({
488
+ key: Schema.optional(FieldElement),
489
+ value: Schema.optional(FieldElement),
490
+ });
491
+
492
+ export type StorageEntry = typeof StorageEntry.Type;
493
+
494
+ /** Storage diff.
495
+ *
496
+ * @prop contractAddress The contract address.
497
+ * @prop storageEntries The entries that changed.
498
+ */
499
+ export const StorageDiff = Schema.Struct({
500
+ filterIds: Schema.optional(Schema.Array(Schema.Number)),
501
+ contractAddress: Schema.optional(FieldElement),
502
+ storageEntries: Schema.optional(Schema.Array(StorageEntry)),
503
+ });
504
+
505
+ export type StorageDiff = typeof StorageDiff.Type;
506
+
507
+ /** A new class declared.
508
+ *
509
+ * @prop classHash The class hash.
510
+ * @prop compiledClassHash The compiled class hash. If undefined, it's the result of a deprecated Cairo 0 declaration.
511
+ */
512
+ export const DeclaredClass = Schema.Struct({
513
+ _tag: tag("declaredClass"),
514
+ declaredClass: Schema.Struct({
515
+ classHash: Schema.optional(FieldElement),
516
+ compiledClassHash: Schema.optional(FieldElement),
517
+ }),
518
+ });
519
+
520
+ export type DeclaredClass = typeof DeclaredClass.Type;
521
+
522
+ /** A class replaced.
523
+ *
524
+ * @prop contractAddress The contract address.
525
+ * @prop classHash The class new hash.
526
+ */
527
+ export const ReplacedClass = Schema.Struct({
528
+ _tag: tag("replacedClass"),
529
+ replacedClass: Schema.Struct({
530
+ contractAddress: Schema.optional(FieldElement),
531
+ classHash: Schema.optional(FieldElement),
532
+ }),
533
+ });
534
+
535
+ export type ReplacedClass = typeof ReplacedClass.Type;
536
+
537
+ /** A contract deployed.
538
+ *
539
+ * @prop contractAddress The contract address.
540
+ * @prop classHash The class hash.
541
+ */
542
+ export const DeployedContract = Schema.Struct({
543
+ _tag: tag("deployedContract"),
544
+ deployedContract: Schema.Struct({
545
+ contractAddress: Schema.optional(FieldElement),
546
+ classHash: Schema.optional(FieldElement),
547
+ }),
548
+ });
549
+
550
+ export type DeployedContract = typeof DeployedContract.Type;
551
+
552
+ /** A contract change.
553
+ *
554
+ * @prop contractAddress The contract address.
555
+ * @prop change The change.
556
+ */
557
+ export const ContractChange = Schema.Struct({
558
+ filterIds: Schema.optional(Schema.Array(Schema.Number)),
559
+ change: Schema.optional(
560
+ Schema.Union(DeclaredClass, ReplacedClass, DeployedContract),
561
+ ),
562
+ });
563
+
564
+ export type ContractChange = typeof ContractChange.Type;
565
+
566
+ /** A nonce update.
567
+ *
568
+ * @prop contractAddress The contract address.
569
+ * @prop nonce The new nonce.
570
+ */
571
+ export const NonceUpdate = Schema.Struct({
572
+ filterIds: Schema.optional(Schema.Array(Schema.Number)),
573
+ contractAddress: Schema.optional(FieldElement),
574
+ nonce: Schema.optional(FieldElement),
575
+ });
576
+
577
+ export type NonceUpdate = typeof NonceUpdate.Type;
578
+
482
579
  /** A block.
483
580
  *
484
581
  * @prop header The block header.
@@ -486,6 +583,8 @@ export type MessageToL1 = typeof MessageToL1.Type;
486
583
  * @prop receipts The receipts of the transactions.
487
584
  * @prop events The events emitted by the transactions.
488
585
  * @prop messages The messages sent to L1 by the transactions.
586
+ * @prop storageDiffs The changes to the storage.
587
+ * @prop contractChanges The changes to contracts and classes.
489
588
  */
490
589
  export const Block = Schema.Struct({
491
590
  header: Schema.optional(BlockHeader),
@@ -493,6 +592,9 @@ export const Block = Schema.Struct({
493
592
  receipts: Schema.Array(TransactionReceipt),
494
593
  events: Schema.Array(Event),
495
594
  messages: Schema.Array(MessageToL1),
595
+ storageDiffs: Schema.Array(StorageDiff),
596
+ contractChanges: Schema.Array(ContractChange),
597
+ nonceUpdates: Schema.Array(NonceUpdate),
496
598
  });
497
599
 
498
600
  export type Block = typeof Block.Type;
@@ -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 = { always: true };
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: { always: true } });
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: { always: true } }, {});
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
- * Change `always` to `true` to receive headers even if no other data matches.
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.Struct({
12
- always: Schema.optional(Schema.Boolean),
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
- return {
255
- always: a.always || b.always,
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
  }