@agirails/sdk 2.3.0 → 2.3.3

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 (153) hide show
  1. package/README.md +45 -8
  2. package/dist/ACTPClient.d.ts +35 -1
  3. package/dist/ACTPClient.d.ts.map +1 -1
  4. package/dist/ACTPClient.js +156 -26
  5. package/dist/ACTPClient.js.map +1 -1
  6. package/dist/adapters/AdapterRouter.d.ts.map +1 -1
  7. package/dist/adapters/AdapterRouter.js.map +1 -1
  8. package/dist/adapters/BasicAdapter.d.ts +10 -1
  9. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  10. package/dist/adapters/BasicAdapter.js +36 -1
  11. package/dist/adapters/BasicAdapter.js.map +1 -1
  12. package/dist/cli/commands/init.d.ts +2 -1
  13. package/dist/cli/commands/init.d.ts.map +1 -1
  14. package/dist/cli/commands/init.js +345 -25
  15. package/dist/cli/commands/init.js.map +1 -1
  16. package/dist/cli/commands/publish.d.ts.map +1 -1
  17. package/dist/cli/commands/publish.js.map +1 -1
  18. package/dist/cli/commands/register.d.ts +16 -0
  19. package/dist/cli/commands/register.d.ts.map +1 -0
  20. package/dist/cli/commands/register.js +211 -0
  21. package/dist/cli/commands/register.js.map +1 -0
  22. package/dist/cli/index.js +3 -0
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/cli/utils/config.d.ts +20 -0
  25. package/dist/cli/utils/config.d.ts.map +1 -1
  26. package/dist/cli/utils/config.js.map +1 -1
  27. package/dist/config/networks.d.ts +20 -4
  28. package/dist/config/networks.d.ts.map +1 -1
  29. package/dist/config/networks.js +59 -27
  30. package/dist/config/networks.js.map +1 -1
  31. package/dist/config/publishPipeline.d.ts +14 -0
  32. package/dist/config/publishPipeline.d.ts.map +1 -1
  33. package/dist/config/publishPipeline.js +2 -1
  34. package/dist/config/publishPipeline.js.map +1 -1
  35. package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -1
  36. package/dist/erc8004/ERC8004Bridge.js +6 -5
  37. package/dist/erc8004/ERC8004Bridge.js.map +1 -1
  38. package/dist/erc8004/ReputationReporter.d.ts.map +1 -1
  39. package/dist/erc8004/ReputationReporter.js +9 -12
  40. package/dist/erc8004/ReputationReporter.js.map +1 -1
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +7 -3
  44. package/dist/index.js.map +1 -1
  45. package/dist/level1/Agent.js +4 -4
  46. package/dist/level1/Agent.js.map +1 -1
  47. package/dist/protocol/ACTPKernel.d.ts +7 -1
  48. package/dist/protocol/ACTPKernel.d.ts.map +1 -1
  49. package/dist/protocol/ACTPKernel.js +13 -10
  50. package/dist/protocol/ACTPKernel.js.map +1 -1
  51. package/dist/protocol/EventMonitor.d.ts +14 -0
  52. package/dist/protocol/EventMonitor.d.ts.map +1 -1
  53. package/dist/protocol/EventMonitor.js +14 -0
  54. package/dist/protocol/EventMonitor.js.map +1 -1
  55. package/dist/runtime/BlockchainRuntime.d.ts +5 -0
  56. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  57. package/dist/runtime/BlockchainRuntime.js +1 -1
  58. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  59. package/dist/storage/ArchiveBundleBuilder.d.ts.map +1 -1
  60. package/dist/storage/ArchiveBundleBuilder.js.map +1 -1
  61. package/dist/storage/ArweaveClient.d.ts.map +1 -1
  62. package/dist/storage/ArweaveClient.js +2 -0
  63. package/dist/storage/ArweaveClient.js.map +1 -1
  64. package/dist/storage/FilebaseClient.d.ts.map +1 -1
  65. package/dist/storage/FilebaseClient.js +2 -0
  66. package/dist/storage/FilebaseClient.js.map +1 -1
  67. package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -1
  68. package/dist/utils/ErrorRecoveryGuide.js +3 -2
  69. package/dist/utils/ErrorRecoveryGuide.js.map +1 -1
  70. package/dist/utils/IPFSClient.d.ts +3 -2
  71. package/dist/utils/IPFSClient.d.ts.map +1 -1
  72. package/dist/utils/IPFSClient.js +7 -5
  73. package/dist/utils/IPFSClient.js.map +1 -1
  74. package/dist/utils/computeTypeHash.js +1 -3
  75. package/dist/utils/computeTypeHash.js.map +1 -1
  76. package/dist/utils/retry.d.ts.map +1 -1
  77. package/dist/utils/retry.js +0 -1
  78. package/dist/utils/retry.js.map +1 -1
  79. package/dist/utils/validation.d.ts +2 -2
  80. package/dist/utils/validation.d.ts.map +1 -1
  81. package/dist/utils/validation.js +2 -2
  82. package/dist/utils/validation.js.map +1 -1
  83. package/dist/wallet/AutoWalletProvider.d.ts +77 -0
  84. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -0
  85. package/dist/wallet/AutoWalletProvider.js +197 -0
  86. package/dist/wallet/AutoWalletProvider.js.map +1 -0
  87. package/dist/wallet/EOAWalletProvider.d.ts +21 -0
  88. package/dist/wallet/EOAWalletProvider.d.ts.map +1 -0
  89. package/dist/wallet/EOAWalletProvider.js +57 -0
  90. package/dist/wallet/EOAWalletProvider.js.map +1 -0
  91. package/dist/wallet/IWalletProvider.d.ts +115 -0
  92. package/dist/wallet/IWalletProvider.d.ts.map +1 -0
  93. package/dist/wallet/IWalletProvider.js +12 -0
  94. package/dist/wallet/IWalletProvider.js.map +1 -0
  95. package/dist/wallet/aa/BundlerClient.d.ts +70 -0
  96. package/dist/wallet/aa/BundlerClient.d.ts.map +1 -0
  97. package/dist/wallet/aa/BundlerClient.js +184 -0
  98. package/dist/wallet/aa/BundlerClient.js.map +1 -0
  99. package/dist/wallet/aa/DualNonceManager.d.ts +56 -0
  100. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -0
  101. package/dist/wallet/aa/DualNonceManager.js +142 -0
  102. package/dist/wallet/aa/DualNonceManager.js.map +1 -0
  103. package/dist/wallet/aa/PaymasterClient.d.ts +52 -0
  104. package/dist/wallet/aa/PaymasterClient.d.ts.map +1 -0
  105. package/dist/wallet/aa/PaymasterClient.js +116 -0
  106. package/dist/wallet/aa/PaymasterClient.js.map +1 -0
  107. package/dist/wallet/aa/TransactionBatcher.d.ts +87 -0
  108. package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -0
  109. package/dist/wallet/aa/TransactionBatcher.js +148 -0
  110. package/dist/wallet/aa/TransactionBatcher.js.map +1 -0
  111. package/dist/wallet/aa/UserOpBuilder.d.ts +71 -0
  112. package/dist/wallet/aa/UserOpBuilder.d.ts.map +1 -0
  113. package/dist/wallet/aa/UserOpBuilder.js +196 -0
  114. package/dist/wallet/aa/UserOpBuilder.js.map +1 -0
  115. package/dist/wallet/aa/constants.d.ts +54 -0
  116. package/dist/wallet/aa/constants.d.ts.map +1 -0
  117. package/dist/wallet/aa/constants.js +18 -0
  118. package/dist/wallet/aa/constants.js.map +1 -0
  119. package/package.json +4 -2
  120. package/src/ACTPClient.ts +217 -31
  121. package/src/adapters/AdapterRouter.ts +0 -1
  122. package/src/adapters/BasicAdapter.ts +41 -1
  123. package/src/cli/commands/init.ts +394 -25
  124. package/src/cli/commands/publish.ts +1 -2
  125. package/src/cli/commands/register.ts +233 -0
  126. package/src/cli/index.ts +4 -0
  127. package/src/cli/utils/config.ts +30 -0
  128. package/src/config/networks.ts +82 -27
  129. package/src/config/publishPipeline.ts +2 -2
  130. package/src/erc8004/ERC8004Bridge.ts +6 -5
  131. package/src/erc8004/ReputationReporter.ts +14 -18
  132. package/src/index.ts +12 -0
  133. package/src/level1/Agent.ts +5 -5
  134. package/src/protocol/ACTPKernel.ts +20 -10
  135. package/src/protocol/EventMonitor.ts +14 -0
  136. package/src/runtime/BlockchainRuntime.ts +7 -1
  137. package/src/storage/ArchiveBundleBuilder.ts +0 -2
  138. package/src/storage/ArweaveClient.ts +2 -1
  139. package/src/storage/FilebaseClient.ts +3 -3
  140. package/src/utils/ErrorRecoveryGuide.ts +4 -2
  141. package/src/utils/IPFSClient.ts +9 -7
  142. package/src/utils/computeTypeHash.ts +1 -3
  143. package/src/utils/retry.ts +0 -1
  144. package/src/utils/validation.ts +2 -2
  145. package/src/wallet/AutoWalletProvider.ts +294 -0
  146. package/src/wallet/EOAWalletProvider.ts +69 -0
  147. package/src/wallet/IWalletProvider.ts +133 -0
  148. package/src/wallet/aa/BundlerClient.ts +274 -0
  149. package/src/wallet/aa/DualNonceManager.ts +173 -0
  150. package/src/wallet/aa/PaymasterClient.ts +174 -0
  151. package/src/wallet/aa/TransactionBatcher.ts +240 -0
  152. package/src/wallet/aa/UserOpBuilder.ts +246 -0
  153. package/src/wallet/aa/constants.ts +60 -0
@@ -37,14 +37,25 @@ interface GasOptions {
37
37
  export class ACTPKernel {
38
38
  private contract: Contract;
39
39
  private readonly gasSettings?: GasOptions;
40
+ /**
41
+ * Number of block confirmations to wait after each state-changing tx.
42
+ * Default: 2 (Base L2 reorg safety — ~4-6 s on Base's 2 s blocks).
43
+ * Set to 1 for faster feedback on testnet; never set to 0 in production.
44
+ */
45
+ private readonly confirmations: number;
40
46
 
41
47
  constructor(
42
48
  private readonly address: string,
43
49
  signer: Signer,
44
- gasSettings?: GasOptions
50
+ gasSettings?: GasOptions,
51
+ confirmations: number = 2
45
52
  ) {
53
+ if (confirmations < 1) {
54
+ throw new Error(`confirmations must be >= 1, got ${confirmations}`);
55
+ }
46
56
  this.contract = new Contract(address, ACTPKernelABI, signer);
47
57
  this.gasSettings = gasSettings;
58
+ this.confirmations = confirmations;
48
59
  }
49
60
 
50
61
  /**
@@ -213,7 +224,7 @@ export class ACTPKernel {
213
224
  txOptions
214
225
  );
215
226
 
216
- const receipt = await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
227
+ const receipt = await tx.wait(this.confirmations);
217
228
  if (!receipt) {
218
229
  throw new Error('Transaction receipt not available');
219
230
  }
@@ -280,7 +291,7 @@ export class ACTPKernel {
280
291
 
281
292
  const tx = await transitionFunc(txId, newState, proof, txOptions);
282
293
 
283
- await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
294
+ await tx.wait(this.confirmations);
284
295
  } catch (error: any) {
285
296
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
286
297
  }
@@ -400,8 +411,7 @@ export class ACTPKernel {
400
411
 
401
412
  const tx = await linkEscrowFunc(txId, escrowContract, escrowId, txOptions);
402
413
 
403
- // Wait for 2 confirmations to ensure state is updated on RPC nodes (Base Sepolia reorg safety)
404
- await tx.wait(2);
414
+ await tx.wait(this.confirmations);
405
415
  } catch (error: any) {
406
416
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
407
417
  }
@@ -437,7 +447,7 @@ export class ACTPKernel {
437
447
 
438
448
  const tx = await releaseMilestoneFunc(txId, amount, txOptions);
439
449
 
440
- await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
450
+ await tx.wait(this.confirmations);
441
451
  } catch (error: any) {
442
452
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
443
453
  }
@@ -521,7 +531,7 @@ export class ACTPKernel {
521
531
 
522
532
  const tx = await releaseEscrowFunc(txId, txOptions);
523
533
 
524
- await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
534
+ await tx.wait(this.confirmations);
525
535
  } catch (error: any) {
526
536
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
527
537
  }
@@ -669,7 +679,7 @@ export class ACTPKernel {
669
679
  txOptions
670
680
  );
671
681
 
672
- await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
682
+ await tx.wait(this.confirmations);
673
683
  } catch (error: any) {
674
684
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
675
685
  }
@@ -735,7 +745,7 @@ export class ACTPKernel {
735
745
  txOptions
736
746
  );
737
747
 
738
- await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
748
+ await tx.wait(this.confirmations);
739
749
  } catch (error: any) {
740
750
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
741
751
  }
@@ -789,7 +799,7 @@ export class ACTPKernel {
789
799
  txOptions
790
800
  );
791
801
 
792
- await tx.wait(2); // Wait for 2 confirmations (Base L2 reorg safety)
802
+ await tx.wait(this.confirmations);
793
803
  } catch (error: any) {
794
804
  throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
795
805
  }
@@ -4,6 +4,20 @@ import { State, Transaction } from '../types';
4
4
  /**
5
5
  * EventMonitor - Listen to blockchain events
6
6
  *
7
+ * ## Confirmation Policy
8
+ *
9
+ * Events received by EventMonitor are already confirmed. ACTPKernel waits
10
+ * for N block confirmations (default 2, configurable via `confirmations`
11
+ * parameter in BlockchainRuntimeConfig) before returning from state-changing
12
+ * operations. On Base L2 (~2 s blocks), the default means events arrive
13
+ * ~4-6 s after submission and are safe from reorgs.
14
+ *
15
+ * Confirmation flow:
16
+ * User calls ACTPKernel.createTransaction()
17
+ * → tx.wait(confirmations) blocks until N confirmations
18
+ * → Event emitted (already confirmed)
19
+ * → EventMonitor receives event (instant)
20
+ *
7
21
  * SECURITY FIX (EVENT-MONITOR): Corrected event parameter order to match ABI.
8
22
  * Per ACTPKernel.json, TransactionCreated signature is:
9
23
  * (bytes32 indexed transactionId, address indexed requester, address indexed provider, uint256 amount, bytes32 serviceHash)
@@ -65,6 +65,11 @@ export interface BlockchainRuntimeConfig {
65
65
  * If provided, attestation replay protection will survive restarts
66
66
  */
67
67
  stateDirectory?: string;
68
+ /**
69
+ * Number of block confirmations to wait after each state-changing tx.
70
+ * Default: 2 (Base L2 reorg safety). Set to 1 on testnet for speed.
71
+ */
72
+ confirmations?: number;
68
73
  }
69
74
 
70
75
  /**
@@ -163,7 +168,8 @@ export class BlockchainRuntime implements IACTPRuntime {
163
168
  this.kernel = new ACTPKernel(
164
169
  this.networkConfig.contracts.actpKernel,
165
170
  this.signer,
166
- config.gasSettings
171
+ config.gasSettings,
172
+ config.confirmations
167
173
  );
168
174
 
169
175
  this.escrow = new EscrowVault(
@@ -15,14 +15,12 @@ import { ValidationError } from '../errors';
15
15
  import {
16
16
  ArchiveBundle,
17
17
  ArchiveChainId,
18
- ArchiveFinalState,
19
18
  ArchiveParticipants,
20
19
  ArchiveReferences,
21
20
  ArchiveHashes,
22
21
  ArchiveSignatures,
23
22
  ArchiveAttestation,
24
23
  ArchiveSettlement,
25
- EscrowRelease,
26
24
  ARCHIVE_BUNDLE_TYPE
27
25
  } from './types';
28
26
 
@@ -48,7 +48,6 @@ import {
48
48
  import { withRetry, RetryOptions } from '../utils/retry';
49
49
  import {
50
50
  GatewayCircuitBreaker,
51
- CircuitBreakerConfig
52
51
  } from '../utils/circuitBreaker';
53
52
 
54
53
  // ============================================================================
@@ -538,6 +537,7 @@ export class ArweaveClient {
538
537
  const chunks: Uint8Array[] = [];
539
538
  let totalSize = 0;
540
539
 
540
+ // eslint-disable-next-line no-constant-condition
541
541
  while (true) {
542
542
  const { done, value } = await reader.read();
543
543
  if (done) break;
@@ -671,6 +671,7 @@ export class ArweaveClient {
671
671
  const chunks: Uint8Array[] = [];
672
672
  let totalSize = 0;
673
673
 
674
+ // eslint-disable-next-line no-constant-condition
674
675
  while (true) {
675
676
  const { done, value } = await reader.read();
676
677
  if (done) break;
@@ -14,7 +14,7 @@
14
14
  * @module storage/FilebaseClient
15
15
  */
16
16
 
17
- import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3';
17
+ import { S3Client, PutObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3';
18
18
  import {
19
19
  StorageError,
20
20
  StorageAuthenticationError,
@@ -39,8 +39,6 @@ import {
39
39
  import { withRetry, RetryOptions } from '../utils/retry';
40
40
  import {
41
41
  GatewayCircuitBreaker,
42
- CircuitBreakerConfig,
43
- globalCircuitBreaker
44
42
  } from '../utils/circuitBreaker';
45
43
 
46
44
  // ============================================================================
@@ -329,6 +327,7 @@ export class FilebaseClient {
329
327
  const chunks: Uint8Array[] = [];
330
328
  let totalSize = 0;
331
329
 
330
+ // eslint-disable-next-line no-constant-condition
332
331
  while (true) {
333
332
  const { done, value } = await reader.read();
334
333
  if (done) break;
@@ -516,6 +515,7 @@ export class FilebaseClient {
516
515
  const chunks: Uint8Array[] = [];
517
516
  let totalSize = 0;
518
517
 
518
+ // eslint-disable-next-line no-constant-condition
519
519
  while (true) {
520
520
  const { done, value } = await reader.read();
521
521
  if (done) break;
@@ -7,6 +7,8 @@
7
7
  * @module utils/ErrorRecoveryGuide
8
8
  */
9
9
 
10
+ import { sdkLogger } from './Logger';
11
+
10
12
  /**
11
13
  * Error severity levels for prioritization
12
14
  */
@@ -650,14 +652,14 @@ export async function withRecoveryGuidance<T>(
650
652
  }
651
653
 
652
654
  if (logGuidance) {
653
- console.error(ErrorRecoveryGuide.formatGuidance(error));
655
+ sdkLogger.error(ErrorRecoveryGuide.formatGuidance(error));
654
656
  }
655
657
 
656
658
  if (autoRetry && recovery.retryable) {
657
659
  const retryParams = ErrorRecoveryGuide.getRetryParams(error);
658
660
  if (retryParams && attempts < retryParams.maxRetries) {
659
661
  attempts++;
660
- console.log(
662
+ sdkLogger.info(
661
663
  `Retrying (${attempts}/${retryParams.maxRetries}) after ${retryParams.delayMs}ms...`
662
664
  );
663
665
  await new Promise((resolve) =>
@@ -74,8 +74,8 @@ export interface IPFSClientConfig {
74
74
  allowedProtocols?: string[];
75
75
 
76
76
  /**
77
- * SECURITY FIX (MEDIUM-3): Allow localhost URLs
78
- * Default: true (required for local IPFS daemon)
77
+ * Allow localhost URLs (e.g., local IPFS daemon).
78
+ * Default: false (SSRF protection). Set to true explicitly for local development.
79
79
  */
80
80
  allowLocalhost?: boolean;
81
81
  }
@@ -85,7 +85,8 @@ export interface IPFSClientConfig {
85
85
  */
86
86
  export const IPFS_CONFIGS = {
87
87
  local: {
88
- url: 'http://localhost:5001'
88
+ url: 'http://localhost:5001',
89
+ allowLocalhost: true, // Explicit opt-in for local development
89
90
  },
90
91
  infura: {
91
92
  url: 'https://ipfs.infura.io:5001/api/v0'
@@ -128,14 +129,14 @@ export class IPFSHTTPClientImpl implements IPFSClient {
128
129
  const url = config.url || 'http://localhost:5001';
129
130
 
130
131
  // SECURITY FIX (MEDIUM-3): Validate URL
131
- this.validateUrl(url, config.allowLocalhost ?? true, config.allowedProtocols);
132
+ this.validateUrl(url, config.allowLocalhost ?? false, config.allowedProtocols);
132
133
 
133
134
  this.config = {
134
135
  url,
135
136
  timeout: config.timeout || 60000,
136
137
  maxSize: config.maxSize || IPFSHTTPClientImpl.DEFAULT_MAX_SIZE,
137
138
  allowedProtocols: config.allowedProtocols || IPFSHTTPClientImpl.DEFAULT_ALLOWED_PROTOCOLS,
138
- allowLocalhost: config.allowLocalhost ?? true,
139
+ allowLocalhost: config.allowLocalhost ?? false,
139
140
  auth: config.auth,
140
141
  headers: config.headers,
141
142
  } as Required<IPFSClientConfig>;
@@ -359,8 +360,9 @@ export function createIPFSClient(): IPFSClient {
359
360
  });
360
361
  }
361
362
 
362
- // Default to local IPFS daemon
363
+ // Default to local IPFS daemon (explicit localhost opt-in)
363
364
  return new IPFSHTTPClientImpl({
364
- url: 'http://localhost:5001'
365
+ url: 'http://localhost:5001',
366
+ allowLocalhost: true,
365
367
  });
366
368
  }
@@ -44,7 +44,5 @@ if (require.main === module) {
44
44
  console.log(typeHash);
45
45
  console.log();
46
46
 
47
- console.log(' Update this value in:');
48
- console.log('- /Testnet/docs/AIP-4.md:204');
49
- console.log('- /Testnet/docs/schemas/aip-4-delivery.eip712.json:11');
47
+ console.log('Use this type hash in your EIP-712 typed data configuration.');
50
48
  }
@@ -236,7 +236,6 @@ export async function withRetry<T>(
236
236
  } = options;
237
237
 
238
238
  let lastError: unknown;
239
- const startTime = Date.now();
240
239
 
241
240
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
242
241
  try {
@@ -320,7 +320,7 @@ export async function validateEndpointURL(endpoint: string, fieldName: string =
320
320
  * @param fieldName - Field name for error messages
321
321
  * @throws {InvalidCIDError} If CID is invalid
322
322
  */
323
- export function validateCID(cid: string, fieldName: string = 'cid'): void {
323
+ export function validateCID(cid: string, _fieldName: string = 'cid'): void {
324
324
  if (!cid || typeof cid !== 'string') {
325
325
  throw new InvalidCIDError(String(cid), 'CID is required');
326
326
  }
@@ -337,7 +337,7 @@ export function validateCID(cid: string, fieldName: string = 'cid'): void {
337
337
  * @param fieldName - Field name for error messages
338
338
  * @throws {InvalidArweaveTxIdError} If TX ID is invalid
339
339
  */
340
- export function validateArweaveTxId(txId: string, fieldName: string = 'txId'): void {
340
+ export function validateArweaveTxId(txId: string, _fieldName: string = 'txId'): void {
341
341
  if (!txId || typeof txId !== 'string') {
342
342
  throw new InvalidArweaveTxIdError(String(txId), 'TX ID is required');
343
343
  }
@@ -0,0 +1,294 @@
1
+ /**
2
+ * AutoWalletProvider — Tier 1 (Auto) wallet implementation.
3
+ *
4
+ * Creates a CoinbaseSmartWallet with gas-sponsored transactions:
5
+ * 1. Load/generate local encrypted key
6
+ * 2. Derive Smart Wallet address (counterfactual CREATE2)
7
+ * 3. Check AgentRegistry registration
8
+ * 4. Build UserOp with executeBatch
9
+ * 5. Get paymaster sponsorship (Coinbase → Pimlico fallback)
10
+ * 6. Submit via bundler
11
+ *
12
+ * If agent is NOT registered, falls back to EOA behavior with a warning.
13
+ *
14
+ * @module wallet/AutoWalletProvider
15
+ */
16
+
17
+ import { ethers } from 'ethers';
18
+ import {
19
+ IWalletProvider,
20
+ TransactionRequest,
21
+ TransactionReceipt,
22
+ WalletInfo,
23
+ BatchedPayParams,
24
+ BatchedPayResult,
25
+ } from './IWalletProvider';
26
+ import { computeSmartWalletAddress } from './aa/UserOpBuilder';
27
+ import {
28
+ buildUserOp,
29
+ signUserOp,
30
+ } from './aa/UserOpBuilder';
31
+ import { SmartWalletCall } from './aa/constants';
32
+ import { BundlerClient } from './aa/BundlerClient';
33
+ import { PaymasterClient } from './aa/PaymasterClient';
34
+ import { DualNonceManager } from './aa/DualNonceManager';
35
+ import { buildACTPPayBatch } from './aa/TransactionBatcher';
36
+ import { sdkLogger } from '../utils/Logger';
37
+
38
+ // ============================================================================
39
+ // Types
40
+ // ============================================================================
41
+
42
+ export interface AutoWalletConfig {
43
+ /** Ethers signer (the EOA that owns the Smart Wallet) */
44
+ signer: ethers.Wallet;
45
+ /** Ethers provider */
46
+ provider: ethers.JsonRpcProvider;
47
+ /** Chain ID */
48
+ chainId: number;
49
+ /** ACTPKernel contract address (for ACTP nonce reads) */
50
+ actpKernelAddress: string;
51
+ /** Bundler configuration */
52
+ bundler: {
53
+ primaryUrl: string;
54
+ backupUrl?: string;
55
+ };
56
+ /** Paymaster configuration */
57
+ paymaster: {
58
+ primaryUrl: string;
59
+ backupUrl?: string;
60
+ };
61
+ }
62
+
63
+ // ============================================================================
64
+ // AutoWalletProvider
65
+ // ============================================================================
66
+
67
+ export class AutoWalletProvider implements IWalletProvider {
68
+ private readonly signer: ethers.Wallet;
69
+ private readonly provider: ethers.JsonRpcProvider;
70
+ private readonly chainId: number;
71
+ private readonly bundler: BundlerClient;
72
+ private readonly paymaster: PaymasterClient;
73
+ private readonly nonceManager: DualNonceManager;
74
+ private smartWalletAddress: string = '';
75
+ private isDeployed: boolean = false;
76
+
77
+ private constructor(
78
+ config: AutoWalletConfig,
79
+ smartWalletAddress: string,
80
+ isDeployed: boolean
81
+ ) {
82
+ this.signer = config.signer;
83
+ this.provider = config.provider;
84
+ this.chainId = config.chainId;
85
+ this.smartWalletAddress = smartWalletAddress;
86
+ this.isDeployed = isDeployed;
87
+
88
+ this.bundler = new BundlerClient({
89
+ primaryUrl: config.bundler.primaryUrl,
90
+ backupUrl: config.bundler.backupUrl,
91
+ });
92
+
93
+ this.paymaster = new PaymasterClient({
94
+ primaryUrl: config.paymaster.primaryUrl,
95
+ backupUrl: config.paymaster.backupUrl,
96
+ chainId: config.chainId,
97
+ });
98
+
99
+ this.nonceManager = new DualNonceManager(
100
+ config.provider,
101
+ smartWalletAddress,
102
+ config.actpKernelAddress
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Factory method — computes counterfactual address and checks deployment.
108
+ */
109
+ static async create(config: AutoWalletConfig): Promise<AutoWalletProvider> {
110
+ const signerAddress = config.signer.address;
111
+ const smartWalletAddress = await computeSmartWalletAddress(
112
+ signerAddress,
113
+ config.provider
114
+ );
115
+
116
+ // Check if wallet is already deployed
117
+ const code = await config.provider.getCode(smartWalletAddress);
118
+ const isDeployed = code !== '0x';
119
+
120
+ sdkLogger.info('AutoWalletProvider initialized', {
121
+ signer: signerAddress,
122
+ smartWallet: smartWalletAddress,
123
+ deployed: isDeployed,
124
+ });
125
+
126
+ return new AutoWalletProvider(config, smartWalletAddress, isDeployed);
127
+ }
128
+
129
+ getAddress(): string {
130
+ return this.smartWalletAddress;
131
+ }
132
+
133
+ async sendTransaction(tx: TransactionRequest): Promise<TransactionReceipt> {
134
+ return this.sendBatchTransaction([tx]);
135
+ }
136
+
137
+ async sendBatchTransaction(
138
+ txs: TransactionRequest[]
139
+ ): Promise<TransactionReceipt> {
140
+ if (txs.length === 0) {
141
+ throw new Error('sendBatchTransaction requires at least one transaction');
142
+ }
143
+
144
+ // Convert to SmartWalletCall[]
145
+ const calls: SmartWalletCall[] = txs.map((tx) => ({
146
+ target: tx.to,
147
+ value: tx.value ? BigInt(tx.value) : 0n,
148
+ data: tx.data,
149
+ }));
150
+
151
+ // Use nonce manager for sequential ordering
152
+ // Note: We don't know if this specific batch increments ACTP nonce
153
+ // (that's handled at the ACTPClient level), so pass false here.
154
+ // The caller (ACTPClient.payBatched) manages ACTP nonce tracking.
155
+ return this.nonceManager.enqueue(
156
+ async ({ entryPointNonce }) => {
157
+ const receipt = await this.submitUserOp(calls, entryPointNonce);
158
+ return { result: receipt, success: receipt.success };
159
+ },
160
+ false // ACTP nonce management is done at ACTPClient level
161
+ );
162
+ }
163
+
164
+ getWalletInfo(): WalletInfo {
165
+ return {
166
+ address: this.smartWalletAddress,
167
+ tier: 'auto',
168
+ supportsBatching: true,
169
+ gasSponsored: true,
170
+ chainId: this.chainId,
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Get the DualNonceManager (for ACTPClient to use for ACTP-aware batching).
176
+ */
177
+ getNonceManager(): DualNonceManager {
178
+ return this.nonceManager;
179
+ }
180
+
181
+ /**
182
+ * Check if the Smart Wallet is deployed on-chain.
183
+ */
184
+ getIsDeployed(): boolean {
185
+ return this.isDeployed;
186
+ }
187
+
188
+ /**
189
+ * Execute a batched ACTP payment atomically.
190
+ *
191
+ * Builds approve + createTransaction + linkEscrow as a single UserOp.
192
+ * Manages ACTP nonce inside the mutex queue for concurrent safety.
193
+ */
194
+ async payACTPBatched(params: BatchedPayParams): Promise<BatchedPayResult> {
195
+ return this.nonceManager.enqueue(
196
+ async ({ entryPointNonce, actpNonce }) => {
197
+ const batch = buildACTPPayBatch({
198
+ ...params,
199
+ actpNonce,
200
+ });
201
+
202
+ const receipt = await this.submitUserOp(batch.calls, entryPointNonce);
203
+
204
+ return {
205
+ result: {
206
+ txId: batch.txId,
207
+ hash: receipt.hash,
208
+ success: receipt.success,
209
+ },
210
+ success: receipt.success,
211
+ };
212
+ },
213
+ true // Increment ACTP nonce on success
214
+ );
215
+ }
216
+
217
+ // ==========================================================================
218
+ // Internal
219
+ // ==========================================================================
220
+
221
+ /**
222
+ * Build, sponsor, sign, and submit a UserOp.
223
+ */
224
+ private async submitUserOp(
225
+ calls: SmartWalletCall[],
226
+ entryPointNonce: bigint
227
+ ): Promise<TransactionReceipt> {
228
+ const signerAddress = this.signer.address;
229
+
230
+ // 1. Build unsigned UserOp
231
+ const userOp = buildUserOp({
232
+ sender: this.smartWalletAddress,
233
+ nonce: entryPointNonce,
234
+ calls,
235
+ isFirstDeploy: !this.isDeployed,
236
+ signerAddress,
237
+ });
238
+
239
+ // 2. Get fee data
240
+ const feeData = await this.provider.getFeeData();
241
+ userOp.maxFeePerGas = feeData.maxFeePerGas ?? 2_000_000_000n;
242
+ userOp.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 1_000_000_000n;
243
+
244
+ // 3. Get stub paymaster data (for gas estimation)
245
+ const stubData = await this.paymaster.getPaymasterStubData(userOp);
246
+ userOp.paymasterAndData = stubData.paymasterAndData;
247
+
248
+ // 4. Dummy signature for gas estimation
249
+ userOp.signature = dummySignature();
250
+
251
+ // 5. Estimate gas
252
+ const gasEstimate = await this.bundler.estimateUserOperationGas(userOp);
253
+ userOp.callGasLimit = gasEstimate.callGasLimit;
254
+ userOp.verificationGasLimit = gasEstimate.verificationGasLimit;
255
+ userOp.preVerificationGas = gasEstimate.preVerificationGas;
256
+
257
+ // 6. Get final paymaster data (with real gas values)
258
+ const finalPaymaster = await this.paymaster.getPaymasterData(userOp);
259
+ userOp.paymasterAndData = finalPaymaster.paymasterAndData;
260
+
261
+ // 7. Sign
262
+ userOp.signature = await signUserOp(userOp, this.signer, this.chainId);
263
+
264
+ // 8. Submit
265
+ const userOpHash = await this.bundler.sendUserOperation(userOp);
266
+ sdkLogger.info('UserOp submitted', { userOpHash });
267
+
268
+ // 9. Wait for receipt
269
+ const receipt = await this.bundler.waitForReceipt(userOpHash);
270
+
271
+ // Mark as deployed after first successful UserOp
272
+ if (!this.isDeployed && receipt.success) {
273
+ this.isDeployed = true;
274
+ }
275
+
276
+ return {
277
+ hash: receipt.transactionHash,
278
+ success: receipt.success,
279
+ };
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Dummy signature for gas estimation.
285
+ * CoinbaseSmartWallet expects abi.encode(uint256 ownerIndex, bytes sig)
286
+ * where sig is 65 bytes (r,s,v).
287
+ */
288
+ function dummySignature(): string {
289
+ const dummySig = '0x' + 'ff'.repeat(65);
290
+ return ethers.AbiCoder.defaultAbiCoder().encode(
291
+ ['uint256', 'bytes'],
292
+ [0, dummySig]
293
+ );
294
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * EOAWalletProvider — Tier 2 (BYOW) wallet implementation.
3
+ *
4
+ * Wraps an ethers.Wallet for traditional EOA signing.
5
+ * sendBatchTransaction() executes calls sequentially (no atomic batching).
6
+ * This is the backward-compatible path for agents that don't register.
7
+ *
8
+ * @module wallet/EOAWalletProvider
9
+ */
10
+
11
+ import { ethers } from 'ethers';
12
+ import {
13
+ IWalletProvider,
14
+ TransactionRequest,
15
+ TransactionReceipt,
16
+ WalletInfo,
17
+ } from './IWalletProvider';
18
+
19
+ export class EOAWalletProvider implements IWalletProvider {
20
+ private readonly wallet: ethers.Wallet;
21
+ private readonly chainId: number;
22
+
23
+ constructor(wallet: ethers.Wallet, chainId: number) {
24
+ this.wallet = wallet;
25
+ this.chainId = chainId;
26
+ }
27
+
28
+ getAddress(): string {
29
+ return this.wallet.address;
30
+ }
31
+
32
+ async sendTransaction(tx: TransactionRequest): Promise<TransactionReceipt> {
33
+ const response = await this.wallet.sendTransaction({
34
+ to: tx.to,
35
+ data: tx.data,
36
+ value: tx.value ? BigInt(tx.value) : 0n,
37
+ });
38
+ const receipt = await response.wait();
39
+ return {
40
+ hash: receipt!.hash,
41
+ success: receipt!.status === 1,
42
+ };
43
+ }
44
+
45
+ async sendBatchTransaction(txs: TransactionRequest[]): Promise<TransactionReceipt> {
46
+ if (txs.length === 0) {
47
+ throw new Error('sendBatchTransaction requires at least one transaction');
48
+ }
49
+
50
+ let lastReceipt: TransactionReceipt | undefined;
51
+ for (const tx of txs) {
52
+ lastReceipt = await this.sendTransaction(tx);
53
+ if (!lastReceipt.success) {
54
+ return lastReceipt; // Fail fast
55
+ }
56
+ }
57
+ return lastReceipt!;
58
+ }
59
+
60
+ getWalletInfo(): WalletInfo {
61
+ return {
62
+ address: this.wallet.address,
63
+ tier: 'eoa',
64
+ supportsBatching: false,
65
+ gasSponsored: false,
66
+ chainId: this.chainId,
67
+ };
68
+ }
69
+ }