@oobe-protocol-labs/synapse-sap-sdk 0.6.3 → 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 (125) 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/idl/synapse_agent_sap.json +7545 -3501
  6. package/dist/cjs/index.js +25 -3
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/modules/escrow-v2.js +241 -0
  9. package/dist/cjs/modules/escrow-v2.js.map +1 -0
  10. package/dist/cjs/modules/escrow.js +4 -0
  11. package/dist/cjs/modules/escrow.js.map +1 -1
  12. package/dist/cjs/modules/index.js +7 -1
  13. package/dist/cjs/modules/index.js.map +1 -1
  14. package/dist/cjs/modules/staking.js +94 -0
  15. package/dist/cjs/modules/staking.js.map +1 -0
  16. package/dist/cjs/modules/subscription.js +96 -0
  17. package/dist/cjs/modules/subscription.js.map +1 -0
  18. package/dist/cjs/pda/index.js +143 -1
  19. package/dist/cjs/pda/index.js.map +1 -1
  20. package/dist/cjs/registries/x402.js +88 -51
  21. package/dist/cjs/registries/x402.js.map +1 -1
  22. package/dist/cjs/types/enums.js +51 -1
  23. package/dist/cjs/types/enums.js.map +1 -1
  24. package/dist/cjs/types/index.js +4 -1
  25. package/dist/cjs/types/index.js.map +1 -1
  26. package/dist/cjs/types/instructions.js.map +1 -1
  27. package/dist/cjs/utils/escrow-validation.js +219 -0
  28. package/dist/cjs/utils/escrow-validation.js.map +1 -0
  29. package/dist/cjs/utils/index.js +12 -1
  30. package/dist/cjs/utils/index.js.map +1 -1
  31. package/dist/cjs/utils/merchant-validator.js +246 -0
  32. package/dist/cjs/utils/merchant-validator.js.map +1 -0
  33. package/dist/cjs/utils/x402-direct.js +231 -0
  34. package/dist/cjs/utils/x402-direct.js.map +1 -0
  35. package/dist/esm/constants/seeds.js +7 -0
  36. package/dist/esm/constants/seeds.js.map +1 -1
  37. package/dist/esm/core/client.js +42 -0
  38. package/dist/esm/core/client.js.map +1 -1
  39. package/dist/esm/idl/synapse_agent_sap.json +7545 -3501
  40. package/dist/esm/index.js +5 -3
  41. package/dist/esm/index.js.map +1 -1
  42. package/dist/esm/modules/escrow-v2.js +237 -0
  43. package/dist/esm/modules/escrow-v2.js.map +1 -0
  44. package/dist/esm/modules/escrow.js +4 -0
  45. package/dist/esm/modules/escrow.js.map +1 -1
  46. package/dist/esm/modules/index.js +3 -0
  47. package/dist/esm/modules/index.js.map +1 -1
  48. package/dist/esm/modules/staking.js +90 -0
  49. package/dist/esm/modules/staking.js.map +1 -0
  50. package/dist/esm/modules/subscription.js +92 -0
  51. package/dist/esm/modules/subscription.js.map +1 -0
  52. package/dist/esm/pda/index.js +135 -0
  53. package/dist/esm/pda/index.js.map +1 -1
  54. package/dist/esm/registries/x402.js +89 -52
  55. package/dist/esm/registries/x402.js.map +1 -1
  56. package/dist/esm/types/enums.js +50 -0
  57. package/dist/esm/types/enums.js.map +1 -1
  58. package/dist/esm/types/index.js +1 -1
  59. package/dist/esm/types/index.js.map +1 -1
  60. package/dist/esm/types/instructions.js.map +1 -1
  61. package/dist/esm/utils/escrow-validation.js +212 -0
  62. package/dist/esm/utils/escrow-validation.js.map +1 -0
  63. package/dist/esm/utils/index.js +4 -0
  64. package/dist/esm/utils/index.js.map +1 -1
  65. package/dist/esm/utils/merchant-validator.js +241 -0
  66. package/dist/esm/utils/merchant-validator.js.map +1 -0
  67. package/dist/esm/utils/x402-direct.js +228 -0
  68. package/dist/esm/utils/x402-direct.js.map +1 -0
  69. package/dist/types/constants/seeds.d.ts +7 -0
  70. package/dist/types/constants/seeds.d.ts.map +1 -1
  71. package/dist/types/core/client.d.ts +33 -0
  72. package/dist/types/core/client.d.ts.map +1 -1
  73. package/dist/types/index.d.ts +6 -4
  74. package/dist/types/index.d.ts.map +1 -1
  75. package/dist/types/modules/escrow-v2.d.ts +51 -0
  76. package/dist/types/modules/escrow-v2.d.ts.map +1 -0
  77. package/dist/types/modules/escrow.d.ts +4 -0
  78. package/dist/types/modules/escrow.d.ts.map +1 -1
  79. package/dist/types/modules/index.d.ts +3 -0
  80. package/dist/types/modules/index.d.ts.map +1 -1
  81. package/dist/types/modules/staking.d.ts +32 -0
  82. package/dist/types/modules/staking.d.ts.map +1 -0
  83. package/dist/types/modules/subscription.d.ts +33 -0
  84. package/dist/types/modules/subscription.d.ts.map +1 -0
  85. package/dist/types/pda/index.d.ts +99 -0
  86. package/dist/types/pda/index.d.ts.map +1 -1
  87. package/dist/types/plugin/schemas.d.ts +2 -2
  88. package/dist/types/registries/x402.d.ts +14 -12
  89. package/dist/types/registries/x402.d.ts.map +1 -1
  90. package/dist/types/types/accounts.d.ts +157 -1
  91. package/dist/types/types/accounts.d.ts.map +1 -1
  92. package/dist/types/types/enums.d.ts +64 -0
  93. package/dist/types/types/enums.d.ts.map +1 -1
  94. package/dist/types/types/index.d.ts +4 -4
  95. package/dist/types/types/index.d.ts.map +1 -1
  96. package/dist/types/types/instructions.d.ts +34 -0
  97. package/dist/types/types/instructions.d.ts.map +1 -1
  98. package/dist/types/utils/escrow-validation.d.ts +145 -0
  99. package/dist/types/utils/escrow-validation.d.ts.map +1 -0
  100. package/dist/types/utils/index.d.ts +6 -0
  101. package/dist/types/utils/index.d.ts.map +1 -1
  102. package/dist/types/utils/merchant-validator.d.ts +176 -0
  103. package/dist/types/utils/merchant-validator.d.ts.map +1 -0
  104. package/dist/types/utils/x402-direct.d.ts +114 -0
  105. package/dist/types/utils/x402-direct.d.ts.map +1 -0
  106. package/package.json +1 -1
  107. package/src/constants/seeds.ts +7 -0
  108. package/src/core/client.ts +45 -0
  109. package/src/idl/synapse_agent_sap.json +7545 -3501
  110. package/src/index.ts +47 -0
  111. package/src/modules/escrow-v2.ts +396 -0
  112. package/src/modules/escrow.ts +4 -0
  113. package/src/modules/index.ts +3 -0
  114. package/src/modules/staking.ts +122 -0
  115. package/src/modules/subscription.ts +147 -0
  116. package/src/pda/index.ts +196 -0
  117. package/src/registries/x402.ts +108 -69
  118. package/src/types/accounts.ts +192 -1
  119. package/src/types/enums.ts +65 -0
  120. package/src/types/index.ts +15 -0
  121. package/src/types/instructions.ts +40 -0
  122. package/src/utils/escrow-validation.ts +301 -0
  123. package/src/utils/index.ts +28 -0
  124. package/src/utils/merchant-validator.ts +359 -0
  125. 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
+ );
@@ -74,12 +74,14 @@ import {
74
74
  deriveAgent,
75
75
  deriveAgentStats,
76
76
  deriveEscrow,
77
+ deriveEscrowV2,
77
78
  } from "../pda";
78
79
  import { sha256, hashToArray } from "../utils";
79
80
  import { SapNetwork } from "../constants/network";
80
81
  import type { SapNetworkId } from "../constants/network";
81
82
  import type {
82
83
  EscrowAccountData,
84
+ EscrowAccountV2Data,
83
85
  AgentAccountData,
84
86
  VolumeCurveBreakpoint,
85
87
  Settlement,
@@ -371,18 +373,14 @@ export class X402Registry {
371
373
  volumeCurve = opts.volumeCurve ?? [];
372
374
  totalBefore = opts.totalCallsBefore ?? 0;
373
375
  } else {
374
- // Try to read from existing escrow
376
+ // Try to read from existing escrow (V2 first, then V1)
375
377
  const [agentPda] = deriveAgent(agentWallet);
376
- const [escrowPda] = deriveEscrow(agentPda, this.wallet);
377
- const escrow = await this.fetchNullable<EscrowAccountData>(
378
- "escrowAccount",
379
- escrowPda,
380
- );
381
-
382
- if (escrow) {
383
- pricePerCall = escrow.pricePerCall;
384
- volumeCurve = escrow.volumeCurve ?? [];
385
- totalBefore = escrow.totalCallsSettled.toNumber();
378
+ const resolved = await this.resolveEscrow(agentPda, this.wallet);
379
+
380
+ if (resolved) {
381
+ pricePerCall = resolved.escrow.pricePerCall;
382
+ volumeCurve = resolved.escrow.volumeCurve ?? [];
383
+ totalBefore = resolved.escrow.totalCallsSettled.toNumber();
386
384
  } else {
387
385
  // Fall back to agent's first pricing tier
388
386
  const agent = await this.fetchNullable<AgentAccountData>(
@@ -493,6 +491,8 @@ export class X402Registry {
493
491
  * @param opts - Payment options (price, max calls, deposit, etc.).
494
492
  * @returns A {@link PaymentContext} with escrow details and transaction signature.
495
493
  * @since v0.1.0
494
+ * @deprecated Since v0.7.0 — Creates a V1 escrow. Use `client.escrowV2.create()` for
495
+ * V2 escrows with dispute windows, co-signing, and settlement security.
496
496
  *
497
497
  * @example
498
498
  * ```ts
@@ -509,7 +509,6 @@ export class X402Registry {
509
509
  ): Promise<PaymentContext> {
510
510
  const [agentPda] = deriveAgent(agentWallet);
511
511
  const [escrowPda] = deriveEscrow(agentPda, this.wallet);
512
- const [statsPda] = deriveAgentStats(agentPda);
513
512
 
514
513
  const pricePerCall = new BN(opts.pricePerCall.toString());
515
514
  const maxCalls = new BN((opts.maxCalls ?? 0).toString());
@@ -536,7 +535,6 @@ export class X402Registry {
536
535
  .accounts({
537
536
  depositor: this.wallet,
538
537
  agent: agentPda,
539
- agentStats: statsPda,
540
538
  escrow: escrowPda,
541
539
  systemProgram: SystemProgram.programId,
542
540
  })
@@ -562,6 +560,7 @@ export class X402Registry {
562
560
  * @param amount - Amount to deposit in smallest token unit.
563
561
  * @returns The transaction signature.
564
562
  * @since v0.1.0
563
+ * @deprecated Since v0.7.0 — Operates on V1 escrows only. Use `client.escrowV2.deposit()` instead.
565
564
  */
566
565
  async addFunds(
567
566
  agentWallet: PublicKey,
@@ -588,6 +587,7 @@ export class X402Registry {
588
587
  * @param amount - Amount to withdraw in smallest token unit.
589
588
  * @returns The transaction signature.
590
589
  * @since v0.1.0
590
+ * @deprecated Since v0.7.0 — Operates on V1 escrows only. Use `client.escrowV2.withdraw()` instead.
591
591
  */
592
592
  async withdrawFunds(
593
593
  agentWallet: PublicKey,
@@ -614,6 +614,7 @@ export class X402Registry {
614
614
  * @param agentWallet - Agent wallet of the escrow.
615
615
  * @returns The transaction signature.
616
616
  * @since v0.1.0
617
+ * @deprecated Since v0.7.0 — Operates on V1 escrows only. Use `client.escrowV2.close()` instead.
617
618
  */
618
619
  async closeEscrow(agentWallet: PublicKey): Promise<TransactionSignature> {
619
620
  const [agentPda] = deriveAgent(agentWallet);
@@ -678,17 +679,14 @@ export class X402Registry {
678
679
  opts?: { network?: SapNetworkId | string },
679
680
  ): Promise<X402Headers | null> {
680
681
  const [agentPda] = deriveAgent(agentWallet);
681
- const [escrowPda] = deriveEscrow(agentPda, this.wallet);
682
+ const resolved = await this.resolveEscrow(agentPda, this.wallet);
683
+ if (!resolved) return null;
682
684
 
683
- const escrow = await this.fetchNullable<EscrowAccountData>(
684
- "escrowAccount",
685
- escrowPda,
686
- );
687
- if (!escrow) return null;
685
+ const escrow = resolved.escrow;
688
686
 
689
687
  return {
690
688
  "X-Payment-Protocol": "SAP-x402",
691
- "X-Payment-Escrow": escrowPda.toBase58(),
689
+ "X-Payment-Escrow": resolved.escrowPda.toBase58(),
692
690
  "X-Payment-Agent": agentPda.toBase58(),
693
691
  "X-Payment-Depositor": this.wallet.toBase58(),
694
692
  "X-Payment-MaxCalls": escrow.maxCalls.toString(),
@@ -742,14 +740,16 @@ export class X402Registry {
742
740
  );
743
741
 
744
742
  const [agentPda] = deriveAgent(this.wallet);
745
- const [escrowPda] = deriveEscrow(agentPda, depositorWallet);
746
743
  const [statsPda] = deriveAgentStats(agentPda);
747
744
 
748
- // Prefetch to calculate amount
749
- const escrow = await this.fetch<EscrowAccountData>(
750
- "escrowAccount",
751
- escrowPda,
752
- );
745
+ // Auto-detect escrow version
746
+ const resolved = await this.resolveEscrow(agentPda, depositorWallet);
747
+ if (!resolved) {
748
+ throw new Error("No escrow found for this agent + depositor pair");
749
+ }
750
+
751
+ const escrow = resolved.escrow;
752
+ const escrowPda = resolved.escrowPda;
753
753
  const estimate = this.calculateCost(
754
754
  escrow.pricePerCall,
755
755
  escrow.volumeCurve,
@@ -761,15 +761,29 @@ export class X402Registry {
761
761
  const preIxs = buildPriorityFeeIxs(opts);
762
762
  const rpcOpts = buildRpcOptions(opts);
763
763
 
764
- let builder = this.methods
765
- .settleCalls(new BN(callsToSettle), serviceHash)
766
- .accounts({
767
- wallet: this.wallet,
768
- agent: agentPda,
769
- agentStats: statsPda,
770
- escrow: escrowPda,
771
- systemProgram: SystemProgram.programId,
772
- });
764
+ let builder;
765
+ if (resolved.version === 2) {
766
+ // V2: settleCallsV2 requires escrow_nonce (default 0)
767
+ builder = this.methods
768
+ .settleCallsV2(new BN(0), new BN(callsToSettle), serviceHash)
769
+ .accounts({
770
+ wallet: this.wallet,
771
+ agent: agentPda,
772
+ agentStats: statsPda,
773
+ escrow: escrowPda,
774
+ systemProgram: SystemProgram.programId,
775
+ });
776
+ } else {
777
+ builder = this.methods
778
+ .settleCalls(new BN(callsToSettle), serviceHash)
779
+ .accounts({
780
+ wallet: this.wallet,
781
+ agent: agentPda,
782
+ agentStats: statsPda,
783
+ escrow: escrowPda,
784
+ systemProgram: SystemProgram.programId,
785
+ });
786
+ }
773
787
 
774
788
  if (preIxs.length > 0) {
775
789
  builder = builder.preInstructions(preIxs);
@@ -825,14 +839,16 @@ export class X402Registry {
825
839
  const totalCalls = entries.reduce((sum, e) => sum + e.calls, 0);
826
840
 
827
841
  const [agentPda] = deriveAgent(this.wallet);
828
- const [escrowPda] = deriveEscrow(agentPda, depositorWallet);
829
842
  const [statsPda] = deriveAgentStats(agentPda);
830
843
 
831
- // Prefetch for amount estimation
832
- const escrow = await this.fetch<EscrowAccountData>(
833
- "escrowAccount",
834
- escrowPda,
835
- );
844
+ // Auto-detect escrow version
845
+ const resolved = await this.resolveEscrow(agentPda, depositorWallet);
846
+ if (!resolved) {
847
+ throw new Error("No escrow found for this agent + depositor pair");
848
+ }
849
+
850
+ const escrow = resolved.escrow;
851
+ const escrowPda = resolved.escrowPda;
836
852
  const estimate = this.calculateCost(
837
853
  escrow.pricePerCall,
838
854
  escrow.volumeCurve,
@@ -887,21 +903,17 @@ export class X402Registry {
887
903
  ): Promise<EscrowBalance | null> {
888
904
  const [agentPda] = deriveAgent(agentWallet);
889
905
  const dep = depositor ?? this.wallet;
890
- const [escrowPda] = deriveEscrow(agentPda, dep);
891
906
 
892
- const escrow = await this.fetchNullable<EscrowAccountData>(
893
- "escrowAccount",
894
- escrowPda,
895
- );
896
- if (!escrow) return null;
907
+ const resolved = await this.resolveEscrow(agentPda, dep);
908
+ if (!resolved) return null;
897
909
 
910
+ const escrow = resolved.escrow;
898
911
  const now = Math.floor(Date.now() / 1000);
899
912
  const isExpired = escrow.expiresAt.toNumber() > 0 && now >= escrow.expiresAt.toNumber();
900
913
  const maxCalls = escrow.maxCalls.toNumber();
901
914
  const settled = escrow.totalCallsSettled.toNumber();
902
915
  const callsRemaining = maxCalls > 0 ? maxCalls - settled : Infinity;
903
916
 
904
- // Estimate affordable calls with current balance
905
917
  const pricePerCall = escrow.pricePerCall.toNumber();
906
918
  const balance = escrow.balance.toNumber();
907
919
  const affordableCalls = pricePerCall > 0
@@ -933,9 +945,18 @@ export class X402Registry {
933
945
  depositor?: PublicKey,
934
946
  ): Promise<boolean> {
935
947
  const [agentPda] = deriveAgent(agentWallet);
936
- const [escrowPda] = deriveEscrow(agentPda, depositor ?? this.wallet);
937
- const info = await this.program.provider.connection.getAccountInfo(escrowPda);
938
- return info !== null;
948
+ const dep = depositor ?? this.wallet;
949
+ const conn = this.program.provider.connection;
950
+
951
+ // Check V2 first (nonce=0)
952
+ const [v2Pda] = deriveEscrowV2(agentPda, dep, 0);
953
+ const v2Info = await conn.getAccountInfo(v2Pda);
954
+ if (v2Info !== null) return true;
955
+
956
+ // Fall back to V1
957
+ const [v1Pda] = deriveEscrow(agentPda, dep);
958
+ const v1Info = await conn.getAccountInfo(v1Pda);
959
+ return v1Info !== null;
939
960
  }
940
961
 
941
962
  /**
@@ -950,11 +971,11 @@ export class X402Registry {
950
971
  async fetchEscrow(
951
972
  agentWallet: PublicKey,
952
973
  depositor?: PublicKey,
953
- ): Promise<EscrowAccountData | null> {
974
+ ): Promise<EscrowAccountData | EscrowAccountV2Data | null> {
954
975
  const [agentPda] = deriveAgent(agentWallet);
955
976
  const dep = depositor ?? this.wallet;
956
- const [escrowPda] = deriveEscrow(agentPda, dep);
957
- return this.fetchNullable<EscrowAccountData>("escrowAccount", escrowPda);
977
+ const resolved = await this.resolveEscrow(agentPda, dep);
978
+ return resolved?.escrow ?? null;
958
979
  }
959
980
 
960
981
  // ── Internals ────────────────────────────────────────
@@ -971,21 +992,6 @@ export class X402Registry {
971
992
  return this.program.methods;
972
993
  }
973
994
 
974
- /**
975
- * @name fetch
976
- * @description Fetch an on-chain account by name and PDA. Throws if not found.
977
- * @param name - Anchor account discriminator name.
978
- * @param pda - Account public key to fetch.
979
- * @returns The deserialized account data.
980
- * @throws If the account does not exist.
981
- * @private
982
- */
983
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
984
- private async fetch<T>(name: string, pda: PublicKey): Promise<T> {
985
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
986
- return (this.program.account as any)[name].fetch(pda) as Promise<T>;
987
- }
988
-
989
995
  /**
990
996
  * @name fetchNullable
991
997
  * @description Fetch an on-chain account by name and PDA. Returns `null` if not found.
@@ -999,4 +1005,37 @@ export class X402Registry {
999
1005
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
1000
1006
  return (this.program.account as any)[name].fetchNullable(pda) as Promise<T | null>;
1001
1007
  }
1008
+
1009
+ /**
1010
+ * @name resolveEscrow
1011
+ * @description Try to find an escrow: V2 (nonce=0) first, then V1 fallback.
1012
+ * Returns the escrow data, PDA, and version indicator.
1013
+ * @private
1014
+ */
1015
+ private async resolveEscrow(
1016
+ agentPda: PublicKey,
1017
+ depositor: PublicKey,
1018
+ ): Promise<{
1019
+ escrow: EscrowAccountData | EscrowAccountV2Data;
1020
+ escrowPda: PublicKey;
1021
+ version: 1 | 2;
1022
+ } | null> {
1023
+ // Try V2 first (nonce=0 is the default)
1024
+ const [v2Pda] = deriveEscrowV2(agentPda, depositor, 0);
1025
+ const v2 = await this.fetchNullable<EscrowAccountV2Data>(
1026
+ "escrowAccountV2",
1027
+ v2Pda,
1028
+ );
1029
+ if (v2) return { escrow: v2, escrowPda: v2Pda, version: 2 };
1030
+
1031
+ // Fall back to V1
1032
+ const [v1Pda] = deriveEscrow(agentPda, depositor);
1033
+ const v1 = await this.fetchNullable<EscrowAccountData>(
1034
+ "escrowAccount",
1035
+ v1Pda,
1036
+ );
1037
+ if (v1) return { escrow: v1, escrowPda: v1Pda, version: 1 };
1038
+
1039
+ return null;
1040
+ }
1002
1041
  }