@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.
- package/README.md +45 -8
- package/dist/ACTPClient.d.ts +35 -1
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +156 -26
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/AdapterRouter.d.ts.map +1 -1
- package/dist/adapters/AdapterRouter.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +10 -1
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +36 -1
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +345 -25
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/publish.d.ts.map +1 -1
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.d.ts +16 -0
- package/dist/cli/commands/register.d.ts.map +1 -0
- package/dist/cli/commands/register.js +211 -0
- package/dist/cli/commands/register.js.map +1 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/config.d.ts +20 -0
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/config/networks.d.ts +20 -4
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +59 -27
- package/dist/config/networks.js.map +1 -1
- package/dist/config/publishPipeline.d.ts +14 -0
- package/dist/config/publishPipeline.d.ts.map +1 -1
- package/dist/config/publishPipeline.js +2 -1
- package/dist/config/publishPipeline.js.map +1 -1
- package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -1
- package/dist/erc8004/ERC8004Bridge.js +6 -5
- package/dist/erc8004/ERC8004Bridge.js.map +1 -1
- package/dist/erc8004/ReputationReporter.d.ts.map +1 -1
- package/dist/erc8004/ReputationReporter.js +9 -12
- package/dist/erc8004/ReputationReporter.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/level1/Agent.js +4 -4
- package/dist/level1/Agent.js.map +1 -1
- package/dist/protocol/ACTPKernel.d.ts +7 -1
- package/dist/protocol/ACTPKernel.d.ts.map +1 -1
- package/dist/protocol/ACTPKernel.js +13 -10
- package/dist/protocol/ACTPKernel.js.map +1 -1
- package/dist/protocol/EventMonitor.d.ts +14 -0
- package/dist/protocol/EventMonitor.d.ts.map +1 -1
- package/dist/protocol/EventMonitor.js +14 -0
- package/dist/protocol/EventMonitor.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts +5 -0
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +1 -1
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/storage/ArchiveBundleBuilder.d.ts.map +1 -1
- package/dist/storage/ArchiveBundleBuilder.js.map +1 -1
- package/dist/storage/ArweaveClient.d.ts.map +1 -1
- package/dist/storage/ArweaveClient.js +2 -0
- package/dist/storage/ArweaveClient.js.map +1 -1
- package/dist/storage/FilebaseClient.d.ts.map +1 -1
- package/dist/storage/FilebaseClient.js +2 -0
- package/dist/storage/FilebaseClient.js.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.js +3 -2
- package/dist/utils/ErrorRecoveryGuide.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -2
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +7 -5
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/utils/computeTypeHash.js +1 -3
- package/dist/utils/computeTypeHash.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +0 -1
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/validation.d.ts +2 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +2 -2
- package/dist/utils/validation.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +77 -0
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -0
- package/dist/wallet/AutoWalletProvider.js +197 -0
- package/dist/wallet/AutoWalletProvider.js.map +1 -0
- package/dist/wallet/EOAWalletProvider.d.ts +21 -0
- package/dist/wallet/EOAWalletProvider.d.ts.map +1 -0
- package/dist/wallet/EOAWalletProvider.js +57 -0
- package/dist/wallet/EOAWalletProvider.js.map +1 -0
- package/dist/wallet/IWalletProvider.d.ts +115 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -0
- package/dist/wallet/IWalletProvider.js +12 -0
- package/dist/wallet/IWalletProvider.js.map +1 -0
- package/dist/wallet/aa/BundlerClient.d.ts +70 -0
- package/dist/wallet/aa/BundlerClient.d.ts.map +1 -0
- package/dist/wallet/aa/BundlerClient.js +184 -0
- package/dist/wallet/aa/BundlerClient.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +56 -0
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -0
- package/dist/wallet/aa/DualNonceManager.js +142 -0
- package/dist/wallet/aa/DualNonceManager.js.map +1 -0
- package/dist/wallet/aa/PaymasterClient.d.ts +52 -0
- package/dist/wallet/aa/PaymasterClient.d.ts.map +1 -0
- package/dist/wallet/aa/PaymasterClient.js +116 -0
- package/dist/wallet/aa/PaymasterClient.js.map +1 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts +87 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -0
- package/dist/wallet/aa/TransactionBatcher.js +148 -0
- package/dist/wallet/aa/TransactionBatcher.js.map +1 -0
- package/dist/wallet/aa/UserOpBuilder.d.ts +71 -0
- package/dist/wallet/aa/UserOpBuilder.d.ts.map +1 -0
- package/dist/wallet/aa/UserOpBuilder.js +196 -0
- package/dist/wallet/aa/UserOpBuilder.js.map +1 -0
- package/dist/wallet/aa/constants.d.ts +54 -0
- package/dist/wallet/aa/constants.d.ts.map +1 -0
- package/dist/wallet/aa/constants.js +18 -0
- package/dist/wallet/aa/constants.js.map +1 -0
- package/package.json +4 -2
- package/src/ACTPClient.ts +217 -31
- package/src/adapters/AdapterRouter.ts +0 -1
- package/src/adapters/BasicAdapter.ts +41 -1
- package/src/cli/commands/init.ts +394 -25
- package/src/cli/commands/publish.ts +1 -2
- package/src/cli/commands/register.ts +233 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/utils/config.ts +30 -0
- package/src/config/networks.ts +82 -27
- package/src/config/publishPipeline.ts +2 -2
- package/src/erc8004/ERC8004Bridge.ts +6 -5
- package/src/erc8004/ReputationReporter.ts +14 -18
- package/src/index.ts +12 -0
- package/src/level1/Agent.ts +5 -5
- package/src/protocol/ACTPKernel.ts +20 -10
- package/src/protocol/EventMonitor.ts +14 -0
- package/src/runtime/BlockchainRuntime.ts +7 -1
- package/src/storage/ArchiveBundleBuilder.ts +0 -2
- package/src/storage/ArweaveClient.ts +2 -1
- package/src/storage/FilebaseClient.ts +3 -3
- package/src/utils/ErrorRecoveryGuide.ts +4 -2
- package/src/utils/IPFSClient.ts +9 -7
- package/src/utils/computeTypeHash.ts +1 -3
- package/src/utils/retry.ts +0 -1
- package/src/utils/validation.ts +2 -2
- package/src/wallet/AutoWalletProvider.ts +294 -0
- package/src/wallet/EOAWalletProvider.ts +69 -0
- package/src/wallet/IWalletProvider.ts +133 -0
- package/src/wallet/aa/BundlerClient.ts +274 -0
- package/src/wallet/aa/DualNonceManager.ts +173 -0
- package/src/wallet/aa/PaymasterClient.ts +174 -0
- package/src/wallet/aa/TransactionBatcher.ts +240 -0
- package/src/wallet/aa/UserOpBuilder.ts +246 -0
- 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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
662
|
+
sdkLogger.info(
|
|
661
663
|
`Retrying (${attempts}/${retryParams.maxRetries}) after ${retryParams.delayMs}ms...`
|
|
662
664
|
);
|
|
663
665
|
await new Promise((resolve) =>
|
package/src/utils/IPFSClient.ts
CHANGED
|
@@ -74,8 +74,8 @@ export interface IPFSClientConfig {
|
|
|
74
74
|
allowedProtocols?: string[];
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
78
|
-
* Default:
|
|
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 ??
|
|
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 ??
|
|
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('
|
|
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
|
}
|
package/src/utils/retry.ts
CHANGED
package/src/utils/validation.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
+
}
|