@agirails/sdk 2.2.0 → 2.2.2

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 (120) hide show
  1. package/dist/ACTPClient.d.ts +200 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +266 -2
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/abi/ACTPKernel.json +16 -0
  6. package/dist/adapters/AdapterRegistry.d.ts +140 -0
  7. package/dist/adapters/AdapterRegistry.d.ts.map +1 -0
  8. package/dist/adapters/AdapterRegistry.js +166 -0
  9. package/dist/adapters/AdapterRegistry.js.map +1 -0
  10. package/dist/adapters/AdapterRouter.d.ts +165 -0
  11. package/dist/adapters/AdapterRouter.d.ts.map +1 -0
  12. package/dist/adapters/AdapterRouter.js +350 -0
  13. package/dist/adapters/AdapterRouter.js.map +1 -0
  14. package/dist/adapters/BaseAdapter.d.ts +17 -0
  15. package/dist/adapters/BaseAdapter.d.ts.map +1 -1
  16. package/dist/adapters/BaseAdapter.js +21 -0
  17. package/dist/adapters/BaseAdapter.js.map +1 -1
  18. package/dist/adapters/BasicAdapter.d.ts +72 -3
  19. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  20. package/dist/adapters/BasicAdapter.js +170 -2
  21. package/dist/adapters/BasicAdapter.js.map +1 -1
  22. package/dist/adapters/IAdapter.d.ts +230 -0
  23. package/dist/adapters/IAdapter.d.ts.map +1 -0
  24. package/dist/adapters/IAdapter.js +44 -0
  25. package/dist/adapters/IAdapter.js.map +1 -0
  26. package/dist/adapters/StandardAdapter.d.ts +70 -1
  27. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  28. package/dist/adapters/StandardAdapter.js +184 -0
  29. package/dist/adapters/StandardAdapter.js.map +1 -1
  30. package/dist/adapters/X402Adapter.d.ts +208 -0
  31. package/dist/adapters/X402Adapter.d.ts.map +1 -0
  32. package/dist/adapters/X402Adapter.js +423 -0
  33. package/dist/adapters/X402Adapter.js.map +1 -0
  34. package/dist/adapters/index.d.ts +8 -0
  35. package/dist/adapters/index.d.ts.map +1 -1
  36. package/dist/adapters/index.js +19 -1
  37. package/dist/adapters/index.js.map +1 -1
  38. package/dist/cli/commands/init.d.ts +4 -0
  39. package/dist/cli/commands/init.d.ts.map +1 -1
  40. package/dist/cli/commands/init.js +184 -4
  41. package/dist/cli/commands/init.js.map +1 -1
  42. package/dist/config/networks.js +3 -3
  43. package/dist/config/networks.js.map +1 -1
  44. package/dist/erc8004/ERC8004Bridge.d.ts +155 -0
  45. package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -0
  46. package/dist/erc8004/ERC8004Bridge.js +325 -0
  47. package/dist/erc8004/ERC8004Bridge.js.map +1 -0
  48. package/dist/erc8004/ReputationReporter.d.ts +223 -0
  49. package/dist/erc8004/ReputationReporter.d.ts.map +1 -0
  50. package/dist/erc8004/ReputationReporter.js +266 -0
  51. package/dist/erc8004/ReputationReporter.js.map +1 -0
  52. package/dist/erc8004/index.d.ts +36 -0
  53. package/dist/erc8004/index.d.ts.map +1 -0
  54. package/dist/erc8004/index.js +46 -0
  55. package/dist/erc8004/index.js.map +1 -0
  56. package/dist/index.d.ts +5 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +21 -2
  59. package/dist/index.js.map +1 -1
  60. package/dist/protocol/ACTPKernel.d.ts +1 -1
  61. package/dist/protocol/ACTPKernel.d.ts.map +1 -1
  62. package/dist/protocol/ACTPKernel.js +16 -7
  63. package/dist/protocol/ACTPKernel.js.map +1 -1
  64. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  65. package/dist/runtime/BlockchainRuntime.js +2 -0
  66. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  67. package/dist/runtime/IACTPRuntime.d.ts +6 -0
  68. package/dist/runtime/IACTPRuntime.d.ts.map +1 -1
  69. package/dist/runtime/MockRuntime.d.ts +12 -0
  70. package/dist/runtime/MockRuntime.d.ts.map +1 -1
  71. package/dist/runtime/MockRuntime.js +41 -0
  72. package/dist/runtime/MockRuntime.js.map +1 -1
  73. package/dist/runtime/types/MockState.d.ts +6 -0
  74. package/dist/runtime/types/MockState.d.ts.map +1 -1
  75. package/dist/runtime/types/MockState.js.map +1 -1
  76. package/dist/types/adapter.d.ts +359 -0
  77. package/dist/types/adapter.d.ts.map +1 -0
  78. package/dist/types/adapter.js +115 -0
  79. package/dist/types/adapter.js.map +1 -0
  80. package/dist/types/erc8004.d.ts +184 -0
  81. package/dist/types/erc8004.d.ts.map +1 -0
  82. package/dist/types/erc8004.js +132 -0
  83. package/dist/types/erc8004.js.map +1 -0
  84. package/dist/types/index.d.ts +3 -0
  85. package/dist/types/index.d.ts.map +1 -1
  86. package/dist/types/index.js +3 -0
  87. package/dist/types/index.js.map +1 -1
  88. package/dist/types/transaction.d.ts +12 -0
  89. package/dist/types/transaction.d.ts.map +1 -1
  90. package/dist/types/x402.d.ts +162 -0
  91. package/dist/types/x402.d.ts.map +1 -0
  92. package/dist/types/x402.js +162 -0
  93. package/dist/types/x402.js.map +1 -0
  94. package/package.json +3 -2
  95. package/src/ACTPClient.ts +318 -2
  96. package/src/abi/ACTPKernel.json +16 -0
  97. package/src/adapters/AdapterRegistry.ts +173 -0
  98. package/src/adapters/AdapterRouter.ts +417 -0
  99. package/src/adapters/BaseAdapter.ts +25 -0
  100. package/src/adapters/BasicAdapter.ts +199 -3
  101. package/src/adapters/IAdapter.ts +292 -0
  102. package/src/adapters/StandardAdapter.ts +220 -1
  103. package/src/adapters/X402Adapter.ts +653 -0
  104. package/src/adapters/index.ts +27 -0
  105. package/src/cli/commands/init.ts +208 -3
  106. package/src/config/networks.ts +3 -3
  107. package/src/erc8004/ERC8004Bridge.ts +461 -0
  108. package/src/erc8004/ReputationReporter.ts +472 -0
  109. package/src/erc8004/index.ts +61 -0
  110. package/src/index.ts +43 -0
  111. package/src/protocol/ACTPKernel.ts +26 -7
  112. package/src/runtime/BlockchainRuntime.ts +2 -0
  113. package/src/runtime/IACTPRuntime.ts +6 -0
  114. package/src/runtime/MockRuntime.ts +42 -0
  115. package/src/runtime/types/MockState.ts +7 -0
  116. package/src/types/adapter.ts +296 -0
  117. package/src/types/erc8004.ts +293 -0
  118. package/src/types/index.ts +3 -0
  119. package/src/types/transaction.ts +12 -0
  120. package/src/types/x402.ts +219 -0
package/src/ACTPClient.ts CHANGED
@@ -45,7 +45,14 @@ import { BlockchainRuntime } from './runtime/BlockchainRuntime';
45
45
  import { IACTPRuntime, IMockRuntime } from './runtime/IACTPRuntime';
46
46
  import { BasicAdapter } from './adapters/BasicAdapter';
47
47
  import { StandardAdapter } from './adapters/StandardAdapter';
48
+ import { AdapterRegistry } from './adapters/AdapterRegistry';
49
+ import { AdapterRouter } from './adapters/AdapterRouter';
50
+ import { IAdapter, TransactionStatus } from './adapters/IAdapter';
51
+ import { UnifiedPayParams, UnifiedPayResult } from './types/adapter';
48
52
  import { EASHelper, EASConfig } from './protocol/EASHelper';
53
+ import { ERC8004Bridge } from './erc8004/ERC8004Bridge';
54
+ import { ReputationReporter } from './erc8004/ReputationReporter';
55
+ import { ERC8004Network } from './types/erc8004';
49
56
  import { getNetwork } from './config/networks';
50
57
 
51
58
  // ============================================================================
@@ -435,6 +442,27 @@ export class ACTPClient {
435
442
  */
436
443
  public readonly easHelper?: EASHelper;
437
444
 
445
+ /**
446
+ * Adapter registry for managing available adapters.
447
+ *
448
+ * Used internally by the router but exposed for custom adapter registration.
449
+ */
450
+ private readonly registry: AdapterRegistry;
451
+
452
+ /**
453
+ * Adapter router for intelligent adapter selection.
454
+ *
455
+ * Selects the best adapter based on payment parameters and metadata.
456
+ */
457
+ private readonly router: AdapterRouter;
458
+
459
+ /**
460
+ * ERC-8004 Reputation Reporter (testnet/mainnet only).
461
+ * Used to report settlement outcomes to ERC-8004 Reputation Registry.
462
+ * @internal
463
+ */
464
+ private readonly reputationReporter?: ReputationReporter;
465
+
438
466
  /**
439
467
  * Private constructor - use ACTPClient.create() factory method.
440
468
  */
@@ -442,13 +470,22 @@ export class ACTPClient {
442
470
  runtime: IACTPRuntime,
443
471
  requesterAddress: string,
444
472
  info: ACTPClientInfo,
445
- easHelper?: EASHelper
473
+ easHelper?: EASHelper,
474
+ erc8004Bridge?: ERC8004Bridge,
475
+ reputationReporter?: ReputationReporter
446
476
  ) {
447
477
  this.runtime = runtime;
448
478
  this.info = info;
449
479
  this.easHelper = easHelper;
480
+ this.reputationReporter = reputationReporter;
450
481
  this.basic = new BasicAdapter(runtime, requesterAddress, easHelper);
451
482
  this.standard = new StandardAdapter(runtime, requesterAddress, easHelper);
483
+
484
+ // Initialize registry and router
485
+ this.registry = new AdapterRegistry();
486
+ this.registry.register(this.basic);
487
+ this.registry.register(this.standard);
488
+ this.router = new AdapterRouter(this.registry, erc8004Bridge);
452
489
  }
453
490
 
454
491
  // ==========================================================================
@@ -505,6 +542,8 @@ export class ACTPClient {
505
542
  let runtime: IACTPRuntime;
506
543
  let stateDirectory: string | undefined;
507
544
  let easHelper: EASHelper | undefined;
545
+ let erc8004Bridge: ERC8004Bridge | undefined;
546
+ let reputationReporter: ReputationReporter | undefined;
508
547
 
509
548
  // If custom runtime provided, use it directly
510
549
  if (config.runtime) {
@@ -577,6 +616,23 @@ export class ACTPClient {
577
616
  if (config.easConfig) {
578
617
  easHelper = blockchainRuntime.getEASHelper();
579
618
  }
619
+
620
+ // ERC-8004 INTEGRATION: Create bridge for agent ID resolution
621
+ // Maps network to ERC8004Network ('base-sepolia' or 'base')
622
+ const erc8004Network: ERC8004Network =
623
+ config.mode === 'testnet' ? 'base-sepolia' : 'base';
624
+ erc8004Bridge = new ERC8004Bridge({
625
+ network: erc8004Network,
626
+ rpcUrl,
627
+ });
628
+
629
+ // ERC-8004 REPUTATION: Create reporter for settlement outcome reporting
630
+ // Reports successful settlements and dispute outcomes to Reputation Registry
631
+ reputationReporter = new ReputationReporter({
632
+ network: erc8004Network,
633
+ signer,
634
+ });
635
+
580
636
  break;
581
637
  }
582
638
 
@@ -598,7 +654,8 @@ export class ACTPClient {
598
654
  };
599
655
 
600
656
  // SECURITY FIX (C-4): Pass EASHelper to adapters for attestation verification
601
- return new ACTPClient(runtime, normalizedAddress, info, easHelper);
657
+ // ERC-8004: Pass bridge for agent ID resolution, reporter for settlement outcomes
658
+ return new ACTPClient(runtime, normalizedAddress, info, easHelper, erc8004Bridge, reputationReporter);
602
659
  }
603
660
 
604
661
  // ==========================================================================
@@ -787,4 +844,263 @@ export class ACTPClient {
787
844
 
788
845
  return this.runtime.getBalance(address);
789
846
  }
847
+
848
+ // ==========================================================================
849
+ // Unified Payment API (Router-based)
850
+ // ==========================================================================
851
+
852
+ /**
853
+ * Unified pay method - auto-selects the best adapter.
854
+ *
855
+ * This is the recommended way to initiate payments. The router
856
+ * intelligently selects the appropriate adapter based on:
857
+ * - Explicit adapter preference (metadata.preferredAdapter)
858
+ * - Required capabilities (escrow, disputes)
859
+ * - Recipient type (address vs HTTP endpoint)
860
+ *
861
+ * IMPORTANT: Returns with state=COMMITTED, NOT settled.
862
+ * You MUST call the lifecycle methods to complete:
863
+ *
864
+ * ```typescript
865
+ * const result = await client.pay({ to, amount });
866
+ * // ... provider does work ...
867
+ * await client.startWork(result.txId);
868
+ * await client.deliver(result.txId);
869
+ * // ... after dispute window ...
870
+ * await client.release(result.escrowId!); // EXPLICIT release
871
+ * ```
872
+ *
873
+ * @param params - Unified payment parameters
874
+ * @returns Promise resolving to unified payment result
875
+ * @throws {ValidationError} If params are invalid
876
+ * @throws {Error} If no suitable adapter found
877
+ *
878
+ * @example
879
+ * ```typescript
880
+ * // Simple payment (uses basic adapter by default)
881
+ * const result = await client.pay({
882
+ * to: '0xProvider...',
883
+ * amount: '100',
884
+ * });
885
+ *
886
+ * // Require escrow (prefers standard adapter)
887
+ * const result = await client.pay({
888
+ * to: '0xProvider...',
889
+ * amount: '100',
890
+ * metadata: { requiresEscrow: true }
891
+ * });
892
+ *
893
+ * // Explicit adapter selection
894
+ * const result = await client.pay({
895
+ * to: '0xProvider...',
896
+ * amount: '100',
897
+ * metadata: { preferredAdapter: 'standard' }
898
+ * });
899
+ * ```
900
+ */
901
+ async pay(params: UnifiedPayParams): Promise<UnifiedPayResult> {
902
+ // Use selectAndResolve to auto-resolve ERC-8004 agent IDs to wallet addresses
903
+ const { adapter, resolvedParams } = await this.router.selectAndResolve(params);
904
+ return adapter.pay(resolvedParams);
905
+ }
906
+
907
+ /**
908
+ * Get transaction status by ID.
909
+ *
910
+ * Returns current state plus action hints indicating
911
+ * what operations are available.
912
+ *
913
+ * @param txId - Transaction ID
914
+ * @returns Promise resolving to transaction status
915
+ * @throws {Error} If transaction not found
916
+ *
917
+ * @example
918
+ * ```typescript
919
+ * const status = await client.getStatus(txId);
920
+ * if (status.canRelease) {
921
+ * await client.release(txId);
922
+ * }
923
+ * ```
924
+ */
925
+ async getStatus(txId: string): Promise<TransactionStatus> {
926
+ // Use standard adapter for status - it has access to all tx details
927
+ return this.standard.getStatus(txId);
928
+ }
929
+
930
+ /**
931
+ * Transition to IN_PROGRESS state (provider starts work).
932
+ *
933
+ * Must be called by provider after accepting the transaction.
934
+ * ACTP requires this explicit transition before delivery.
935
+ *
936
+ * @param txId - Transaction ID
937
+ * @throws {Error} If transaction not found or wrong state
938
+ *
939
+ * @example
940
+ * ```typescript
941
+ * // Provider acknowledges and starts work
942
+ * await client.startWork(txId);
943
+ * ```
944
+ */
945
+ async startWork(txId: string): Promise<void> {
946
+ await this.runtime.transitionState(txId, 'IN_PROGRESS');
947
+ }
948
+
949
+ /**
950
+ * Transition to DELIVERED state (provider completes work).
951
+ *
952
+ * When no disputeWindowSeconds is provided, uses the transaction's actual
953
+ * disputeWindow from creation time. This ensures consistency and prevents
954
+ * mismatches between transaction creation and delivery.
955
+ *
956
+ * @param txId - Transaction ID
957
+ * @param disputeWindowSeconds - Optional dispute window override in seconds.
958
+ * If not provided, uses transaction's disputeWindow.
959
+ * @throws {Error} If transaction not found or wrong state
960
+ *
961
+ * @example
962
+ * ```typescript
963
+ * // Use transaction's disputeWindow (recommended)
964
+ * await client.deliver(txId);
965
+ *
966
+ * // Override with custom dispute window (use with caution)
967
+ * await client.deliver(txId, 7200);
968
+ * ```
969
+ */
970
+ async deliver(txId: string, disputeWindowSeconds?: number): Promise<void> {
971
+ // Fetch transaction
972
+ const tx = await this.runtime.getTransaction(txId);
973
+ if (!tx) {
974
+ throw new Error(`Transaction ${txId} not found`);
975
+ }
976
+
977
+ // First ensure we're in IN_PROGRESS state
978
+ if (tx.state === 'COMMITTED') {
979
+ await this.runtime.transitionState(txId, 'IN_PROGRESS');
980
+ }
981
+
982
+ // Use provided disputeWindow or fall back to transaction's disputeWindow
983
+ const effectiveDisputeWindow = disputeWindowSeconds ?? tx.disputeWindow;
984
+
985
+ // Encode dispute window as proof
986
+ const proof = ethers.AbiCoder.defaultAbiCoder().encode(
987
+ ['uint256'],
988
+ [effectiveDisputeWindow]
989
+ );
990
+
991
+ await this.runtime.transitionState(txId, 'DELIVERED', proof);
992
+ }
993
+
994
+ /**
995
+ * Release escrow funds (EXPLICIT settlement).
996
+ *
997
+ * MUST be called after dispute window expires or requester approves.
998
+ * This is the ONLY way to settle - NO auto-settle.
999
+ *
1000
+ * If ERC-8004 agent ID was set during transaction creation, this method
1001
+ * also reports the settlement to the ERC-8004 Reputation Registry.
1002
+ * Reputation reporting is non-blocking - failures don't affect settlement.
1003
+ *
1004
+ * @param escrowId - Escrow ID (usually same as txId)
1005
+ * @param attestationUID - Optional attestation UID for verification
1006
+ * @throws {Error} If escrow not found or dispute window active
1007
+ *
1008
+ * @example
1009
+ * ```typescript
1010
+ * // After dispute window expires
1011
+ * await client.release(result.escrowId!);
1012
+ * // Transaction is now SETTLED
1013
+ * // If ERC-8004 agent, reputation is automatically reported
1014
+ * ```
1015
+ */
1016
+ async release(escrowId: string, attestationUID?: string): Promise<void> {
1017
+ // In ACTP, escrowId === txId
1018
+ const txId = escrowId;
1019
+
1020
+ // Get transaction to find agentId (for reputation reporting)
1021
+ const tx = await this.runtime.getTransaction(txId);
1022
+ const agentId = tx?.agentId;
1023
+
1024
+ // Release escrow (this is the critical operation)
1025
+ await this.runtime.releaseEscrow(escrowId, attestationUID);
1026
+
1027
+ // ERC-8004 REPUTATION: Report settlement if agent ID exists
1028
+ // Non-blocking - fire and forget (settlement already succeeded)
1029
+ if (this.reputationReporter && agentId && agentId !== '0') {
1030
+ // Don't await - reputation reporting shouldn't block the release
1031
+ this.reputationReporter
1032
+ .reportSettlement({
1033
+ agentId,
1034
+ txId,
1035
+ })
1036
+ .then((result) => {
1037
+ if (result) {
1038
+ console.log(
1039
+ `[ERC8004] Settlement reported for agent ${agentId}: ${result.txHash}`
1040
+ );
1041
+ }
1042
+ })
1043
+ .catch(() => {
1044
+ // Errors already logged by reporter - silently ignore here
1045
+ });
1046
+ }
1047
+ }
1048
+
1049
+ /**
1050
+ * Register a custom adapter.
1051
+ *
1052
+ * Allows adding custom payment adapters (e.g., x402, ERC-8004)
1053
+ * that will be considered during router selection.
1054
+ *
1055
+ * @param adapter - Adapter to register
1056
+ *
1057
+ * @example
1058
+ * ```typescript
1059
+ * // Register a custom x402 adapter
1060
+ * client.registerAdapter(new X402Adapter(client.runtime, requesterAddress));
1061
+ * ```
1062
+ */
1063
+ registerAdapter(adapter: IAdapter): void {
1064
+ this.registry.register(adapter);
1065
+ }
1066
+
1067
+ /**
1068
+ * Get all registered adapter IDs.
1069
+ *
1070
+ * @returns Array of adapter IDs
1071
+ *
1072
+ * @example
1073
+ * ```typescript
1074
+ * const adapters = client.getRegisteredAdapters();
1075
+ * console.log(adapters); // ['basic', 'standard', 'x402']
1076
+ * ```
1077
+ */
1078
+ getRegisteredAdapters(): string[] {
1079
+ return this.registry.getIds();
1080
+ }
1081
+
1082
+ /**
1083
+ * Get the ERC-8004 Reputation Reporter instance.
1084
+ *
1085
+ * Only available in testnet/mainnet modes. Returns undefined in mock mode.
1086
+ * Use this for manual reputation reporting or checking stats.
1087
+ *
1088
+ * @returns ReputationReporter instance or undefined
1089
+ *
1090
+ * @example
1091
+ * ```typescript
1092
+ * const reporter = client.getReputationReporter();
1093
+ * if (reporter) {
1094
+ * // Check if already reported
1095
+ * const reported = reporter.isReported(txId);
1096
+ *
1097
+ * // Get agent reputation
1098
+ * const rep = await reporter.getAgentReputation('12345');
1099
+ * console.log(`Agent has ${rep?.count} reviews, score: ${rep?.score}`);
1100
+ * }
1101
+ * ```
1102
+ */
1103
+ getReputationReporter(): ReputationReporter | undefined {
1104
+ return this.reputationReporter;
1105
+ }
790
1106
  }
@@ -328,6 +328,11 @@
328
328
  "name": "serviceHash",
329
329
  "type": "bytes32",
330
330
  "internalType": "bytes32"
331
+ },
332
+ {
333
+ "name": "agentId",
334
+ "type": "uint256",
335
+ "internalType": "uint256"
331
336
  }
332
337
  ],
333
338
  "outputs": [
@@ -477,6 +482,11 @@
477
482
  "name": "platformFeeBpsLocked",
478
483
  "type": "uint16",
479
484
  "internalType": "uint16"
485
+ },
486
+ {
487
+ "name": "agentId",
488
+ "type": "uint256",
489
+ "internalType": "uint256"
480
490
  }
481
491
  ]
482
492
  }
@@ -1328,6 +1338,12 @@
1328
1338
  "type": "uint256",
1329
1339
  "indexed": false,
1330
1340
  "internalType": "uint256"
1341
+ },
1342
+ {
1343
+ "name": "agentId",
1344
+ "type": "uint256",
1345
+ "indexed": false,
1346
+ "internalType": "uint256"
1331
1347
  }
1332
1348
  ],
1333
1349
  "anonymous": false
@@ -0,0 +1,173 @@
1
+ /**
2
+ * AdapterRegistry - Central registry for payment adapters.
3
+ *
4
+ * Manages the collection of available adapters and provides
5
+ * methods for registration, lookup, and priority-based retrieval.
6
+ *
7
+ * @module adapters/AdapterRegistry
8
+ */
9
+
10
+ import { IAdapter } from './IAdapter';
11
+
12
+ /**
13
+ * AdapterRegistry - Central registry for managing available adapters.
14
+ *
15
+ * The registry maintains a collection of adapters indexed by their ID.
16
+ * It provides methods for:
17
+ * - Registration and unregistration
18
+ * - Lookup by ID
19
+ * - Priority-sorted retrieval
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const registry = new AdapterRegistry();
24
+ *
25
+ * // Register adapters
26
+ * registry.register(basicAdapter);
27
+ * registry.register(standardAdapter);
28
+ *
29
+ * // Lookup by ID
30
+ * const adapter = registry.get('basic');
31
+ *
32
+ * // Get all adapters sorted by priority
33
+ * const adapters = registry.getByPriority();
34
+ * ```
35
+ */
36
+ export class AdapterRegistry {
37
+ /**
38
+ * Internal map of adapters by ID.
39
+ */
40
+ private adapters: Map<string, IAdapter> = new Map();
41
+
42
+ /**
43
+ * Register an adapter.
44
+ *
45
+ * If an adapter with the same ID already exists, it will be replaced.
46
+ *
47
+ * @param adapter - Adapter to register
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * registry.register(new BasicAdapter(runtime, requesterAddress));
52
+ * ```
53
+ */
54
+ register(adapter: IAdapter): void {
55
+ if (!adapter.metadata?.id) {
56
+ throw new Error('Cannot register adapter without metadata.id');
57
+ }
58
+ this.adapters.set(adapter.metadata.id, adapter);
59
+ }
60
+
61
+ /**
62
+ * Unregister an adapter by ID.
63
+ *
64
+ * @param id - Adapter ID to remove
65
+ * @returns True if adapter was found and removed, false otherwise
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const removed = registry.unregister('custom-adapter');
70
+ * ```
71
+ */
72
+ unregister(id: string): boolean {
73
+ return this.adapters.delete(id);
74
+ }
75
+
76
+ /**
77
+ * Get an adapter by ID.
78
+ *
79
+ * @param id - Adapter ID to look up
80
+ * @returns The adapter or undefined if not found
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const adapter = registry.get('standard');
85
+ * if (adapter) {
86
+ * await adapter.pay(params);
87
+ * }
88
+ * ```
89
+ */
90
+ get(id: string): IAdapter | undefined {
91
+ return this.adapters.get(id);
92
+ }
93
+
94
+ /**
95
+ * Get all registered adapters.
96
+ *
97
+ * Returns adapters in insertion order.
98
+ *
99
+ * @returns Array of all registered adapters
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const all = registry.getAll();
104
+ * console.log(`${all.length} adapters registered`);
105
+ * ```
106
+ */
107
+ getAll(): IAdapter[] {
108
+ return Array.from(this.adapters.values());
109
+ }
110
+
111
+ /**
112
+ * Check if an adapter is registered.
113
+ *
114
+ * @param id - Adapter ID to check
115
+ * @returns True if adapter is registered
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * if (registry.has('x402')) {
120
+ * console.log('x402 adapter available');
121
+ * }
122
+ * ```
123
+ */
124
+ has(id: string): boolean {
125
+ return this.adapters.has(id);
126
+ }
127
+
128
+ /**
129
+ * Get adapters sorted by priority (highest first).
130
+ *
131
+ * Higher priority adapters are tried first during selection.
132
+ *
133
+ * @returns Array of adapters sorted by priority descending
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * const byPriority = registry.getByPriority();
138
+ * // byPriority[0] has highest priority
139
+ * ```
140
+ */
141
+ getByPriority(): IAdapter[] {
142
+ return this.getAll().sort(
143
+ (a, b) => b.metadata.priority - a.metadata.priority
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Get the number of registered adapters.
149
+ *
150
+ * @returns Number of adapters
151
+ */
152
+ get size(): number {
153
+ return this.adapters.size;
154
+ }
155
+
156
+ /**
157
+ * Get all adapter IDs.
158
+ *
159
+ * @returns Array of adapter IDs
160
+ */
161
+ getIds(): string[] {
162
+ return Array.from(this.adapters.keys());
163
+ }
164
+
165
+ /**
166
+ * Clear all registered adapters.
167
+ *
168
+ * Primarily useful for testing.
169
+ */
170
+ clear(): void {
171
+ this.adapters.clear();
172
+ }
173
+ }