@oobe-protocol-labs/synapse-sap-sdk 0.6.2 → 0.7.0

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.
Files changed (140) hide show
  1. package/dist/cjs/constants/seeds.js +7 -0
  2. package/dist/cjs/constants/seeds.js.map +1 -1
  3. package/dist/cjs/core/client.js +42 -0
  4. package/dist/cjs/core/client.js.map +1 -1
  5. package/dist/cjs/events/geyser.js +295 -0
  6. package/dist/cjs/events/geyser.js.map +1 -0
  7. package/dist/cjs/idl/synapse_agent_sap.json +7545 -3501
  8. package/dist/cjs/index.js +28 -3
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/modules/escrow-v2.js +241 -0
  11. package/dist/cjs/modules/escrow-v2.js.map +1 -0
  12. package/dist/cjs/modules/escrow.js +4 -0
  13. package/dist/cjs/modules/escrow.js.map +1 -1
  14. package/dist/cjs/modules/index.js +7 -1
  15. package/dist/cjs/modules/index.js.map +1 -1
  16. package/dist/cjs/modules/staking.js +94 -0
  17. package/dist/cjs/modules/staking.js.map +1 -0
  18. package/dist/cjs/modules/subscription.js +96 -0
  19. package/dist/cjs/modules/subscription.js.map +1 -0
  20. package/dist/cjs/pda/index.js +143 -1
  21. package/dist/cjs/pda/index.js.map +1 -1
  22. package/dist/cjs/postgres/sync.js +72 -4
  23. package/dist/cjs/postgres/sync.js.map +1 -1
  24. package/dist/cjs/registries/x402.js +88 -51
  25. package/dist/cjs/registries/x402.js.map +1 -1
  26. package/dist/cjs/types/enums.js +51 -1
  27. package/dist/cjs/types/enums.js.map +1 -1
  28. package/dist/cjs/types/index.js +4 -1
  29. package/dist/cjs/types/index.js.map +1 -1
  30. package/dist/cjs/types/instructions.js.map +1 -1
  31. package/dist/cjs/utils/escrow-validation.js +219 -0
  32. package/dist/cjs/utils/escrow-validation.js.map +1 -0
  33. package/dist/cjs/utils/index.js +12 -1
  34. package/dist/cjs/utils/index.js.map +1 -1
  35. package/dist/cjs/utils/merchant-validator.js +246 -0
  36. package/dist/cjs/utils/merchant-validator.js.map +1 -0
  37. package/dist/cjs/utils/x402-direct.js +231 -0
  38. package/dist/cjs/utils/x402-direct.js.map +1 -0
  39. package/dist/esm/constants/seeds.js +7 -0
  40. package/dist/esm/constants/seeds.js.map +1 -1
  41. package/dist/esm/core/client.js +42 -0
  42. package/dist/esm/core/client.js.map +1 -1
  43. package/dist/esm/events/geyser.js +258 -0
  44. package/dist/esm/events/geyser.js.map +1 -0
  45. package/dist/esm/idl/synapse_agent_sap.json +7545 -3501
  46. package/dist/esm/index.js +7 -3
  47. package/dist/esm/index.js.map +1 -1
  48. package/dist/esm/modules/escrow-v2.js +237 -0
  49. package/dist/esm/modules/escrow-v2.js.map +1 -0
  50. package/dist/esm/modules/escrow.js +4 -0
  51. package/dist/esm/modules/escrow.js.map +1 -1
  52. package/dist/esm/modules/index.js +3 -0
  53. package/dist/esm/modules/index.js.map +1 -1
  54. package/dist/esm/modules/staking.js +90 -0
  55. package/dist/esm/modules/staking.js.map +1 -0
  56. package/dist/esm/modules/subscription.js +92 -0
  57. package/dist/esm/modules/subscription.js.map +1 -0
  58. package/dist/esm/pda/index.js +135 -0
  59. package/dist/esm/pda/index.js.map +1 -1
  60. package/dist/esm/postgres/sync.js +72 -4
  61. package/dist/esm/postgres/sync.js.map +1 -1
  62. package/dist/esm/registries/x402.js +89 -52
  63. package/dist/esm/registries/x402.js.map +1 -1
  64. package/dist/esm/types/enums.js +50 -0
  65. package/dist/esm/types/enums.js.map +1 -1
  66. package/dist/esm/types/index.js +1 -1
  67. package/dist/esm/types/index.js.map +1 -1
  68. package/dist/esm/types/instructions.js.map +1 -1
  69. package/dist/esm/utils/escrow-validation.js +212 -0
  70. package/dist/esm/utils/escrow-validation.js.map +1 -0
  71. package/dist/esm/utils/index.js +4 -0
  72. package/dist/esm/utils/index.js.map +1 -1
  73. package/dist/esm/utils/merchant-validator.js +241 -0
  74. package/dist/esm/utils/merchant-validator.js.map +1 -0
  75. package/dist/esm/utils/x402-direct.js +228 -0
  76. package/dist/esm/utils/x402-direct.js.map +1 -0
  77. package/dist/types/constants/seeds.d.ts +7 -0
  78. package/dist/types/constants/seeds.d.ts.map +1 -1
  79. package/dist/types/core/client.d.ts +33 -0
  80. package/dist/types/core/client.d.ts.map +1 -1
  81. package/dist/types/events/geyser.d.ts +150 -0
  82. package/dist/types/events/geyser.d.ts.map +1 -0
  83. package/dist/types/index.d.ts +8 -4
  84. package/dist/types/index.d.ts.map +1 -1
  85. package/dist/types/modules/escrow-v2.d.ts +51 -0
  86. package/dist/types/modules/escrow-v2.d.ts.map +1 -0
  87. package/dist/types/modules/escrow.d.ts +4 -0
  88. package/dist/types/modules/escrow.d.ts.map +1 -1
  89. package/dist/types/modules/index.d.ts +3 -0
  90. package/dist/types/modules/index.d.ts.map +1 -1
  91. package/dist/types/modules/staking.d.ts +32 -0
  92. package/dist/types/modules/staking.d.ts.map +1 -0
  93. package/dist/types/modules/subscription.d.ts +33 -0
  94. package/dist/types/modules/subscription.d.ts.map +1 -0
  95. package/dist/types/pda/index.d.ts +99 -0
  96. package/dist/types/pda/index.d.ts.map +1 -1
  97. package/dist/types/plugin/schemas.d.ts +2 -2
  98. package/dist/types/postgres/sync.d.ts +26 -2
  99. package/dist/types/postgres/sync.d.ts.map +1 -1
  100. package/dist/types/registries/x402.d.ts +14 -12
  101. package/dist/types/registries/x402.d.ts.map +1 -1
  102. package/dist/types/types/accounts.d.ts +157 -1
  103. package/dist/types/types/accounts.d.ts.map +1 -1
  104. package/dist/types/types/enums.d.ts +64 -0
  105. package/dist/types/types/enums.d.ts.map +1 -1
  106. package/dist/types/types/index.d.ts +4 -4
  107. package/dist/types/types/index.d.ts.map +1 -1
  108. package/dist/types/types/instructions.d.ts +34 -0
  109. package/dist/types/types/instructions.d.ts.map +1 -1
  110. package/dist/types/utils/escrow-validation.d.ts +145 -0
  111. package/dist/types/utils/escrow-validation.d.ts.map +1 -0
  112. package/dist/types/utils/index.d.ts +6 -0
  113. package/dist/types/utils/index.d.ts.map +1 -1
  114. package/dist/types/utils/merchant-validator.d.ts +176 -0
  115. package/dist/types/utils/merchant-validator.d.ts.map +1 -0
  116. package/dist/types/utils/x402-direct.d.ts +114 -0
  117. package/dist/types/utils/x402-direct.d.ts.map +1 -0
  118. package/package.json +5 -1
  119. package/src/constants/seeds.ts +7 -0
  120. package/src/core/client.ts +45 -0
  121. package/src/events/geyser.ts +384 -0
  122. package/src/events/yellowstone.d.ts +7 -0
  123. package/src/idl/synapse_agent_sap.json +7545 -3501
  124. package/src/index.ts +51 -0
  125. package/src/modules/escrow-v2.ts +396 -0
  126. package/src/modules/escrow.ts +4 -0
  127. package/src/modules/index.ts +3 -0
  128. package/src/modules/staking.ts +122 -0
  129. package/src/modules/subscription.ts +147 -0
  130. package/src/pda/index.ts +196 -0
  131. package/src/postgres/sync.ts +90 -4
  132. package/src/registries/x402.ts +108 -69
  133. package/src/types/accounts.ts +192 -1
  134. package/src/types/enums.ts +65 -0
  135. package/src/types/index.ts +15 -0
  136. package/src/types/instructions.ts +40 -0
  137. package/src/utils/escrow-validation.ts +301 -0
  138. package/src/utils/index.ts +28 -0
  139. package/src/utils/merchant-validator.ts +359 -0
  140. package/src/utils/x402-direct.ts +370 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @module subscription
3
+ * @description Agent subscription lifecycle — create, fund, cancel,
4
+ * close, and fetch subscription accounts.
5
+ *
6
+ * @category Modules
7
+ * @since v0.7.0
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import {
12
+ SystemProgram,
13
+ type PublicKey,
14
+ type TransactionSignature,
15
+ } from "@solana/web3.js";
16
+ import { BN } from "@coral-xyz/anchor";
17
+ import { BaseModule } from "./base";
18
+ import { deriveAgent, deriveSubscription } from "../pda";
19
+ import type { SubscriptionData, CreateSubscriptionArgs } from "../types";
20
+
21
+ /**
22
+ * @name SubscriptionModule
23
+ * @description Manages recurring subscriptions between subscribers and agents.
24
+ *
25
+ * @category Modules
26
+ * @since v0.7.0
27
+ * @extends BaseModule
28
+ */
29
+ export class SubscriptionModule extends BaseModule {
30
+ // ── PDA helpers ──────────────────────────────────────
31
+
32
+ private toNum(v: BN | number | bigint): number {
33
+ return BN.isBN(v) ? v.toNumber() : Number(v);
34
+ }
35
+
36
+ deriveSubscription(
37
+ agentPda: PublicKey,
38
+ subscriber?: PublicKey,
39
+ subId: BN | number | bigint = 0,
40
+ ): readonly [PublicKey, number] {
41
+ return deriveSubscription(
42
+ agentPda,
43
+ subscriber ?? this.walletPubkey,
44
+ this.toNum(subId),
45
+ );
46
+ }
47
+
48
+ // ── Instructions ─────────────────────────────────────
49
+
50
+ async create(
51
+ agentWallet: PublicKey,
52
+ args: CreateSubscriptionArgs,
53
+ ): Promise<TransactionSignature> {
54
+ const [agentPda] = deriveAgent(agentWallet);
55
+ const [subPda] = this.deriveSubscription(agentPda, undefined, args.subId);
56
+
57
+ return this.methods
58
+ .createSubscription(
59
+ this.bn(args.subId),
60
+ this.bn(args.pricePerInterval),
61
+ args.billingInterval,
62
+ this.bn(args.initialFund),
63
+ )
64
+ .accounts({
65
+ subscriber: this.walletPubkey,
66
+ agent: agentPda,
67
+ subscription: subPda,
68
+ systemProgram: SystemProgram.programId,
69
+ })
70
+ .rpc();
71
+ }
72
+
73
+ async fund(
74
+ agentWallet: PublicKey,
75
+ subId: BN | number | bigint,
76
+ amount: BN | number | bigint,
77
+ ): Promise<TransactionSignature> {
78
+ const [agentPda] = deriveAgent(agentWallet);
79
+ const [subPda] = this.deriveSubscription(agentPda, undefined, subId);
80
+
81
+ return this.methods
82
+ .fundSubscription(this.bn(amount))
83
+ .accounts({
84
+ subscriber: this.walletPubkey,
85
+ subscription: subPda,
86
+ systemProgram: SystemProgram.programId,
87
+ })
88
+ .rpc();
89
+ }
90
+
91
+ async cancel(
92
+ agentWallet: PublicKey,
93
+ subId: BN | number | bigint = 0,
94
+ ): Promise<TransactionSignature> {
95
+ const [agentPda] = deriveAgent(agentWallet);
96
+ const [subPda] = this.deriveSubscription(agentPda, undefined, subId);
97
+
98
+ return this.methods
99
+ .cancelSubscription()
100
+ .accounts({
101
+ subscriber: this.walletPubkey,
102
+ agentWallet,
103
+ subscription: subPda,
104
+ })
105
+ .rpc();
106
+ }
107
+
108
+ async close(
109
+ agentWallet: PublicKey,
110
+ subId: BN | number | bigint = 0,
111
+ ): Promise<TransactionSignature> {
112
+ const [agentPda] = deriveAgent(agentWallet);
113
+ const [subPda] = this.deriveSubscription(agentPda, undefined, subId);
114
+
115
+ return this.methods
116
+ .closeSubscription()
117
+ .accounts({
118
+ subscriber: this.walletPubkey,
119
+ subscription: subPda,
120
+ })
121
+ .rpc();
122
+ }
123
+
124
+ // ── Fetchers ─────────────────────────────────────────
125
+
126
+ async fetch(
127
+ agentPda: PublicKey,
128
+ subscriber?: PublicKey,
129
+ subId: BN | number | bigint = 0,
130
+ ): Promise<SubscriptionData> {
131
+ const [pda] = this.deriveSubscription(agentPda, subscriber, subId);
132
+ return this.fetchAccount<SubscriptionData>("subscriptionAccount", pda);
133
+ }
134
+
135
+ async fetchNullable(
136
+ agentPda: PublicKey,
137
+ subscriber?: PublicKey,
138
+ subId: BN | number | bigint = 0,
139
+ ): Promise<SubscriptionData | null> {
140
+ const [pda] = this.deriveSubscription(agentPda, subscriber, subId);
141
+ return this.fetchAccountNullable<SubscriptionData>("subscriptionAccount", pda);
142
+ }
143
+
144
+ async fetchByPda(subPda: PublicKey): Promise<SubscriptionData> {
145
+ return this.fetchAccount<SubscriptionData>("subscriptionAccount", subPda);
146
+ }
147
+ }
package/src/pda/index.ts CHANGED
@@ -79,6 +79,22 @@ const u32le = (n: number): Buffer => {
79
79
  return buf;
80
80
  };
81
81
 
82
+ /**
83
+ * Encode an unsigned 64-bit integer as a little-endian `Buffer`.
84
+ *
85
+ * @name u64le
86
+ * @description Produces an 8-byte LE buffer for u64 PDA seed segments (e.g., escrow nonce, sub_id).
87
+ * @param n - The number or bigint to encode.
88
+ * @returns {Buffer} 8-byte little-endian buffer.
89
+ * @category PDA
90
+ * @since v0.5.0
91
+ */
92
+ const u64le = (n: number | bigint): Buffer => {
93
+ const buf = Buffer.alloc(8);
94
+ buf.writeBigUInt64LE(BigInt(n), 0);
95
+ return buf;
96
+ };
97
+
82
98
  // ═════════════════════════════════════════════
83
99
  // Core PDAs
84
100
  // ═════════════════════════════════════════════
@@ -421,6 +437,7 @@ export const deriveTool = (
421
437
  * @returns {PdaResult} `[pda, bump]` tuple.
422
438
  * @category PDA
423
439
  * @since v0.1.0
440
+ * @deprecated Since v0.7.0 — Use {@link deriveEscrowV2} for V2 escrows with nonce support.
424
441
  * @see EscrowAccount
425
442
  */
426
443
  export const deriveEscrow = (
@@ -650,3 +667,182 @@ export const deriveMemoryChunk = (
650
667
  ],
651
668
  programId,
652
669
  );
670
+
671
+ // ═════════════════════════════════════════════
672
+ // Escrow V2
673
+ // ═════════════════════════════════════════════
674
+
675
+ /**
676
+ * Derive the **EscrowAccountV2** PDA.
677
+ *
678
+ * Seeds: `["sap_escrow_v2", agent_pda, depositor_wallet, nonce_u64_le]`
679
+ *
680
+ * @name deriveEscrowV2
681
+ * @description Computes the V2 escrow PDA supporting nonce-based multi-escrow.
682
+ * @param agentPda - The agent's PDA.
683
+ * @param depositor - The depositor's wallet.
684
+ * @param nonce - Escrow nonce (u64) allowing multiple escrows per pair.
685
+ * @param programId - Override program ID.
686
+ * @returns {PdaResult} `[pda, bump]` tuple.
687
+ * @category PDA
688
+ * @since v0.5.0
689
+ */
690
+ export const deriveEscrowV2 = (
691
+ agentPda: PublicKey,
692
+ depositor: PublicKey,
693
+ nonce: number | bigint = 0,
694
+ programId = SAP_PROGRAM_ID,
695
+ ): PdaResult =>
696
+ findPda(
697
+ [
698
+ toSeedBuf(SEEDS.ESCROW_V2),
699
+ agentPda.toBuffer(),
700
+ depositor.toBuffer(),
701
+ u64le(nonce),
702
+ ],
703
+ programId,
704
+ );
705
+
706
+ /**
707
+ * Derive the **PendingSettlement** PDA.
708
+ *
709
+ * Seeds: `["sap_pending", escrow_v2_pda, settlement_index_u64_le]`
710
+ *
711
+ * @name derivePendingSettlement
712
+ * @param escrowV2Pda - The parent V2 escrow PDA.
713
+ * @param settlementIndex - The monotonic settlement index (u64).
714
+ * @param programId - Override program ID.
715
+ * @returns {PdaResult} `[pda, bump]` tuple.
716
+ * @category PDA
717
+ * @since v0.5.0
718
+ */
719
+ export const derivePendingSettlement = (
720
+ escrowV2Pda: PublicKey,
721
+ settlementIndex: number | bigint,
722
+ programId = SAP_PROGRAM_ID,
723
+ ): PdaResult =>
724
+ findPda(
725
+ [
726
+ toSeedBuf(SEEDS.PENDING),
727
+ escrowV2Pda.toBuffer(),
728
+ u64le(settlementIndex),
729
+ ],
730
+ programId,
731
+ );
732
+
733
+ /**
734
+ * Derive the **DisputeRecord** PDA.
735
+ *
736
+ * Seeds: `["sap_dispute", pending_settlement_pda]`
737
+ *
738
+ * @name deriveDispute
739
+ * @param pendingSettlementPda - The parent pending settlement PDA.
740
+ * @param programId - Override program ID.
741
+ * @returns {PdaResult} `[pda, bump]` tuple.
742
+ * @category PDA
743
+ * @since v0.5.0
744
+ */
745
+ export const deriveDispute = (
746
+ pendingSettlementPda: PublicKey,
747
+ programId = SAP_PROGRAM_ID,
748
+ ): PdaResult =>
749
+ findPda(
750
+ [toSeedBuf(SEEDS.DISPUTE), pendingSettlementPda.toBuffer()],
751
+ programId,
752
+ );
753
+
754
+ /**
755
+ * Derive the **AgentStake** PDA.
756
+ *
757
+ * Seeds: `["sap_stake", agent_pda]`
758
+ *
759
+ * @name deriveStake
760
+ * @param agentPda - The agent's PDA.
761
+ * @param programId - Override program ID.
762
+ * @returns {PdaResult} `[pda, bump]` tuple.
763
+ * @category PDA
764
+ * @since v0.5.0
765
+ */
766
+ export const deriveStake = (
767
+ agentPda: PublicKey,
768
+ programId = SAP_PROGRAM_ID,
769
+ ): PdaResult =>
770
+ findPda([toSeedBuf(SEEDS.STAKE), agentPda.toBuffer()], programId);
771
+
772
+ /**
773
+ * Derive the **Subscription** PDA.
774
+ *
775
+ * Seeds: `["sap_sub", agent_pda, subscriber_wallet, sub_id_u64_le]`
776
+ *
777
+ * @name deriveSubscription
778
+ * @param agentPda - The agent's PDA.
779
+ * @param subscriber - The subscriber's wallet.
780
+ * @param subId - Subscription ID (u64).
781
+ * @param programId - Override program ID.
782
+ * @returns {PdaResult} `[pda, bump]` tuple.
783
+ * @category PDA
784
+ * @since v0.5.0
785
+ */
786
+ export const deriveSubscription = (
787
+ agentPda: PublicKey,
788
+ subscriber: PublicKey,
789
+ subId: number | bigint = 0,
790
+ programId = SAP_PROGRAM_ID,
791
+ ): PdaResult =>
792
+ findPda(
793
+ [
794
+ toSeedBuf(SEEDS.SUBSCRIPTION),
795
+ agentPda.toBuffer(),
796
+ subscriber.toBuffer(),
797
+ u64le(subId),
798
+ ],
799
+ programId,
800
+ );
801
+
802
+ /**
803
+ * Derive the **CounterShard** PDA.
804
+ *
805
+ * Seeds: `["sap_shard", shard_index_u8]`
806
+ *
807
+ * @name deriveShard
808
+ * @param shardIndex - The shard index (0–7).
809
+ * @param programId - Override program ID.
810
+ * @returns {PdaResult} `[pda, bump]` tuple.
811
+ * @category PDA
812
+ * @since v0.5.0
813
+ */
814
+ export const deriveShard = (
815
+ shardIndex: number,
816
+ programId = SAP_PROGRAM_ID,
817
+ ): PdaResult =>
818
+ findPda(
819
+ [toSeedBuf(SEEDS.SHARD), Buffer.from([shardIndex])],
820
+ programId,
821
+ );
822
+
823
+ /**
824
+ * Derive the **IndexPage** PDA.
825
+ *
826
+ * Seeds: `["sap_idx_page", parent_index_pda, page_index_u8]`
827
+ *
828
+ * @name deriveIndexPage
829
+ * @param parentIndexPda - The parent index PDA (CapabilityIndex, ProtocolIndex, etc.).
830
+ * @param pageIndex - The page index (0–255).
831
+ * @param programId - Override program ID.
832
+ * @returns {PdaResult} `[pda, bump]` tuple.
833
+ * @category PDA
834
+ * @since v0.5.0
835
+ */
836
+ export const deriveIndexPage = (
837
+ parentIndexPda: PublicKey,
838
+ pageIndex: number,
839
+ programId = SAP_PROGRAM_ID,
840
+ ): PdaResult =>
841
+ findPda(
842
+ [
843
+ toSeedBuf(SEEDS.INDEX_PAGE),
844
+ parentIndexPda.toBuffer(),
845
+ Buffer.from([pageIndex]),
846
+ ],
847
+ programId,
848
+ );
@@ -35,6 +35,7 @@
35
35
 
36
36
  import type { SapClient } from "../core/client";
37
37
  import { EventParser } from "../events";
38
+ import { GeyserEventStream, type GeyserConfig } from "../events/geyser";
38
39
  import type { SapPostgres, SyncAllResult } from "./adapter";
39
40
  import type { SyncOptions } from "./types";
40
41
 
@@ -67,6 +68,7 @@ export class SapSyncEngine {
67
68
 
68
69
  private intervalId: ReturnType<typeof setInterval> | null = null;
69
70
  private logSubId: number | null = null;
71
+ private geyserStream: GeyserEventStream | null = null;
70
72
  private running = false;
71
73
 
72
74
  constructor(pg: SapPostgres, client: SapClient, debug = false) {
@@ -208,7 +210,7 @@ export class SapSyncEngine {
208
210
 
209
211
  /**
210
212
  * @name stopEventStream
211
- * @description Unsubscribe from the program log stream.
213
+ * @description Unsubscribe from the program log stream (WebSocket or Geyser).
212
214
  * @since v0.1.0
213
215
  */
214
216
  async stopEventStream(): Promise<void> {
@@ -216,8 +218,92 @@ export class SapSyncEngine {
216
218
  const connection = this.client.program.provider.connection;
217
219
  await connection.removeOnLogsListener(this.logSubId);
218
220
  this.logSubId = null;
219
- this.log("Event stream stopped");
221
+ this.log("WebSocket event stream stopped");
220
222
  }
223
+ if (this.geyserStream) {
224
+ await this.geyserStream.disconnect();
225
+ this.geyserStream = null;
226
+ this.log("Geyser event stream stopped");
227
+ }
228
+ }
229
+
230
+ // ═════════════════════════════════════════════
231
+ // Geyser gRPC Event Stream
232
+ // ═════════════════════════════════════════════
233
+
234
+ /**
235
+ * @name startGeyserStream
236
+ * @description Subscribe to SAP program transactions via Yellowstone gRPC
237
+ * and insert parsed events into the `sap_events` table in real-time.
238
+ *
239
+ * Drop-in replacement for {@link startEventStream} with lower latency,
240
+ * no missed events, and automatic reconnection.
241
+ *
242
+ * Requires `@triton-one/yellowstone-grpc` to be installed.
243
+ *
244
+ * @param geyserConfig - Yellowstone gRPC connection config.
245
+ * @since v0.6.3
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * await sync.startGeyserStream({
250
+ * endpoint: "https://grpc.triton.one",
251
+ * token: process.env.GEYSER_TOKEN!,
252
+ * });
253
+ * ```
254
+ */
255
+ async startGeyserStream(geyserConfig: GeyserConfig): Promise<void> {
256
+ if (this.geyserStream) {
257
+ this.log("Geyser stream already running");
258
+ return;
259
+ }
260
+
261
+ const eventParser = new EventParser(this.client.program);
262
+ const stream = new GeyserEventStream(geyserConfig);
263
+
264
+ stream.on("logs", async (logs: string[], signature: string, slot: number) => {
265
+ try {
266
+ const events = eventParser.parseLogs(logs);
267
+ for (const event of events) {
268
+ const data = event.data as Record<string, unknown>;
269
+ const agentPda =
270
+ (data.agent as string) ?? (data.agentPda as string) ?? undefined;
271
+ const wallet =
272
+ (data.wallet as string) ?? (data.owner as string) ?? undefined;
273
+
274
+ await this.pg.syncEvent(
275
+ event.name,
276
+ signature,
277
+ slot,
278
+ data,
279
+ agentPda,
280
+ wallet,
281
+ );
282
+ }
283
+ } catch (err) {
284
+ this.log(`Geyser event parse error: ${err}`);
285
+ }
286
+ });
287
+
288
+ stream.on("connected", () => {
289
+ this.log("Geyser gRPC connected");
290
+ });
291
+
292
+ stream.on("disconnected", (reason: string) => {
293
+ this.log(`Geyser gRPC disconnected: ${reason}`);
294
+ });
295
+
296
+ stream.on("error", (err: Error) => {
297
+ this.log(`Geyser error: ${err.message}`);
298
+ });
299
+
300
+ stream.on("reconnecting", (attempt: number) => {
301
+ this.log(`Geyser reconnecting (attempt ${attempt})...`);
302
+ });
303
+
304
+ await stream.connect();
305
+ this.geyserStream = stream;
306
+ this.log("Geyser event stream started");
221
307
  }
222
308
 
223
309
  // ═════════════════════════════════════════════
@@ -235,11 +321,11 @@ export class SapSyncEngine {
235
321
 
236
322
  /**
237
323
  * @name isStreaming
238
- * @description Check whether the event stream is active.
324
+ * @description Check whether the event stream is active (WebSocket or Geyser).
239
325
  * @since v0.1.0
240
326
  */
241
327
  isStreaming(): boolean {
242
- return this.logSubId !== null;
328
+ return this.logSubId !== null || this.geyserStream?.connected === true;
243
329
  }
244
330
 
245
331
  // ═════════════════════════════════════════════