@agirails/sdk 2.5.2 → 2.5.4

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 (172) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +67 -22
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +12 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +30 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +45 -11
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/cli/utils/client.d.ts.map +1 -1
  20. package/dist/cli/utils/client.js +1 -0
  21. package/dist/cli/utils/client.js.map +1 -1
  22. package/dist/config/networks.d.ts +2 -2
  23. package/dist/config/networks.d.ts.map +1 -1
  24. package/dist/config/networks.js +27 -22
  25. package/dist/config/networks.js.map +1 -1
  26. package/dist/level0/request.d.ts.map +1 -1
  27. package/dist/level0/request.js +2 -1
  28. package/dist/level0/request.js.map +1 -1
  29. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  30. package/dist/runtime/BlockchainRuntime.js +11 -5
  31. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  32. package/dist/runtime/MockStateManager.d.ts.map +1 -1
  33. package/dist/runtime/MockStateManager.js +2 -1
  34. package/dist/runtime/MockStateManager.js.map +1 -1
  35. package/dist/utils/IPFSClient.d.ts +3 -1
  36. package/dist/utils/IPFSClient.d.ts.map +1 -1
  37. package/dist/utils/IPFSClient.js +27 -7
  38. package/dist/utils/IPFSClient.js.map +1 -1
  39. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  40. package/dist/wallet/AutoWalletProvider.js +52 -18
  41. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  42. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  43. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  44. package/dist/wallet/SmartWalletRouter.js +212 -0
  45. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  46. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  47. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  48. package/dist/wallet/aa/DualNonceManager.js +100 -5
  49. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  50. package/package.json +3 -6
  51. package/src/ACTPClient.ts +0 -1579
  52. package/src/abi/ACTPKernel.json +0 -1356
  53. package/src/abi/AgentRegistry.json +0 -915
  54. package/src/abi/ERC20.json +0 -40
  55. package/src/abi/EscrowVault.json +0 -134
  56. package/src/abi/IdentityRegistry.json +0 -316
  57. package/src/adapters/AdapterRegistry.ts +0 -173
  58. package/src/adapters/AdapterRouter.ts +0 -416
  59. package/src/adapters/BaseAdapter.ts +0 -498
  60. package/src/adapters/BasicAdapter.ts +0 -514
  61. package/src/adapters/IAdapter.ts +0 -292
  62. package/src/adapters/StandardAdapter.ts +0 -555
  63. package/src/adapters/X402Adapter.ts +0 -731
  64. package/src/adapters/index.ts +0 -60
  65. package/src/builders/DeliveryProofBuilder.ts +0 -327
  66. package/src/builders/QuoteBuilder.ts +0 -483
  67. package/src/builders/index.ts +0 -17
  68. package/src/cli/commands/balance.ts +0 -110
  69. package/src/cli/commands/batch.ts +0 -487
  70. package/src/cli/commands/config.ts +0 -231
  71. package/src/cli/commands/deploy-check.ts +0 -364
  72. package/src/cli/commands/deploy-env.ts +0 -120
  73. package/src/cli/commands/diff.ts +0 -141
  74. package/src/cli/commands/init.ts +0 -469
  75. package/src/cli/commands/mint.ts +0 -116
  76. package/src/cli/commands/pay.ts +0 -113
  77. package/src/cli/commands/publish.ts +0 -475
  78. package/src/cli/commands/pull.ts +0 -124
  79. package/src/cli/commands/register.ts +0 -247
  80. package/src/cli/commands/simulate.ts +0 -345
  81. package/src/cli/commands/time.ts +0 -302
  82. package/src/cli/commands/tx.ts +0 -448
  83. package/src/cli/commands/watch.ts +0 -211
  84. package/src/cli/index.ts +0 -134
  85. package/src/cli/utils/client.ts +0 -251
  86. package/src/cli/utils/config.ts +0 -389
  87. package/src/cli/utils/output.ts +0 -465
  88. package/src/cli/utils/wallet.ts +0 -109
  89. package/src/config/agirailsmd.ts +0 -262
  90. package/src/config/networks.ts +0 -275
  91. package/src/config/pendingPublish.ts +0 -237
  92. package/src/config/publishPipeline.ts +0 -359
  93. package/src/config/syncOperations.ts +0 -279
  94. package/src/erc8004/ERC8004Bridge.ts +0 -462
  95. package/src/erc8004/ReputationReporter.ts +0 -468
  96. package/src/erc8004/index.ts +0 -61
  97. package/src/errors/index.ts +0 -427
  98. package/src/index.ts +0 -364
  99. package/src/level0/Provider.ts +0 -117
  100. package/src/level0/ServiceDirectory.ts +0 -131
  101. package/src/level0/index.ts +0 -10
  102. package/src/level0/provide.ts +0 -132
  103. package/src/level0/request.ts +0 -432
  104. package/src/level1/Agent.ts +0 -1426
  105. package/src/level1/index.ts +0 -10
  106. package/src/level1/pricing/PriceCalculator.ts +0 -255
  107. package/src/level1/pricing/PricingStrategy.ts +0 -198
  108. package/src/level1/types/Job.ts +0 -179
  109. package/src/level1/types/Options.ts +0 -291
  110. package/src/level1/types/index.ts +0 -8
  111. package/src/protocol/ACTPKernel.ts +0 -808
  112. package/src/protocol/AgentRegistry.ts +0 -559
  113. package/src/protocol/DIDManager.ts +0 -629
  114. package/src/protocol/DIDResolver.ts +0 -554
  115. package/src/protocol/EASHelper.ts +0 -378
  116. package/src/protocol/EscrowVault.ts +0 -255
  117. package/src/protocol/EventMonitor.ts +0 -204
  118. package/src/protocol/MessageSigner.ts +0 -510
  119. package/src/protocol/ProofGenerator.ts +0 -339
  120. package/src/protocol/QuoteBuilder.ts +0 -15
  121. package/src/registry/AgentRegistryClient.ts +0 -202
  122. package/src/runtime/BlockchainRuntime.ts +0 -1015
  123. package/src/runtime/IACTPRuntime.ts +0 -306
  124. package/src/runtime/MockRuntime.ts +0 -1298
  125. package/src/runtime/MockStateManager.ts +0 -576
  126. package/src/runtime/index.ts +0 -25
  127. package/src/runtime/types/MockState.ts +0 -237
  128. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  129. package/src/storage/ArweaveClient.ts +0 -946
  130. package/src/storage/FilebaseClient.ts +0 -790
  131. package/src/storage/index.ts +0 -96
  132. package/src/storage/types.ts +0 -348
  133. package/src/types/adapter.ts +0 -310
  134. package/src/types/agent.ts +0 -79
  135. package/src/types/did.ts +0 -223
  136. package/src/types/eip712.ts +0 -175
  137. package/src/types/erc8004.ts +0 -293
  138. package/src/types/escrow.ts +0 -27
  139. package/src/types/index.ts +0 -17
  140. package/src/types/message.ts +0 -145
  141. package/src/types/state.ts +0 -87
  142. package/src/types/transaction.ts +0 -69
  143. package/src/types/x402.ts +0 -251
  144. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  145. package/src/utils/Helpers.ts +0 -688
  146. package/src/utils/IPFSClient.ts +0 -368
  147. package/src/utils/Logger.ts +0 -484
  148. package/src/utils/NonceManager.ts +0 -591
  149. package/src/utils/RateLimiter.ts +0 -534
  150. package/src/utils/ReceivedNonceTracker.ts +0 -567
  151. package/src/utils/SDKLifecycle.ts +0 -416
  152. package/src/utils/SecureNonce.ts +0 -78
  153. package/src/utils/Semaphore.ts +0 -276
  154. package/src/utils/UsedAttestationTracker.ts +0 -385
  155. package/src/utils/canonicalJson.ts +0 -38
  156. package/src/utils/circuitBreaker.ts +0 -324
  157. package/src/utils/computeTypeHash.ts +0 -48
  158. package/src/utils/fsSafe.ts +0 -80
  159. package/src/utils/index.ts +0 -80
  160. package/src/utils/retry.ts +0 -364
  161. package/src/utils/security.ts +0 -418
  162. package/src/utils/validation.ts +0 -540
  163. package/src/wallet/AutoWalletProvider.ts +0 -299
  164. package/src/wallet/EOAWalletProvider.ts +0 -69
  165. package/src/wallet/IWalletProvider.ts +0 -135
  166. package/src/wallet/aa/BundlerClient.ts +0 -274
  167. package/src/wallet/aa/DualNonceManager.ts +0 -173
  168. package/src/wallet/aa/PaymasterClient.ts +0 -174
  169. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  170. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  171. package/src/wallet/aa/constants.ts +0 -60
  172. package/src/wallet/keystore.ts +0 -240
@@ -1,808 +0,0 @@
1
- import { Contract, Signer, BytesLike, ethers, AbiCoder } from 'ethers';
2
- import ACTPKernelABI from '../abi/ACTPKernel.json';
3
- import {
4
- State,
5
- StateMachine,
6
- Transaction,
7
- CreateTransactionParams,
8
- DisputeResolution,
9
- EconomicParams
10
- } from '../types';
11
- import {
12
- TransactionNotFoundError,
13
- TransactionRevertedError,
14
- InvalidStateTransitionError,
15
- ValidationError
16
- } from '../errors';
17
- import {
18
- validateAddress,
19
- validateAmount,
20
- validateDeadline,
21
- validateDisputeWindow,
22
- validateTxId
23
- } from '../utils/validation';
24
-
25
- /**
26
- * Gas options for transactions
27
- */
28
- interface GasOptions {
29
- maxFeePerGas?: bigint;
30
- maxPriorityFeePerGas?: bigint;
31
- }
32
-
33
- /**
34
- * ACTPKernel - Smart contract wrapper
35
- * Reference: Yellow Paper §3 (ACTP Kernel Specification)
36
- */
37
- export class ACTPKernel {
38
- private contract: Contract;
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;
46
-
47
- constructor(
48
- private readonly address: string,
49
- signer: Signer,
50
- gasSettings?: GasOptions,
51
- confirmations: number = 2
52
- ) {
53
- if (confirmations < 1) {
54
- throw new Error(`confirmations must be >= 1, got ${confirmations}`);
55
- }
56
- this.contract = new Contract(address, ACTPKernelABI, signer);
57
- this.gasSettings = gasSettings;
58
- this.confirmations = confirmations;
59
- }
60
-
61
- /**
62
- * Get kernel contract address
63
- */
64
- getAddress(): string {
65
- return this.address;
66
- }
67
-
68
- /**
69
- * Get the underlying ethers Contract instance.
70
- *
71
- * SECURITY FIX (C-3): Provides public access to contract for EventMonitor
72
- * instead of accessing private field via bracket notation.
73
- *
74
- * @returns ethers Contract instance
75
- */
76
- getContract(): Contract {
77
- return this.contract;
78
- }
79
-
80
- /**
81
- * Get gas buffer multiplier based on operation complexity
82
- * V6 Security Enhancement: Operation-specific gas buffers
83
- * Reference: SDK_SECURITY_ANALYSIS-Ultra-Think.md Lines 326-337
84
- */
85
- private getGasBufferMultiplier(operation: string): number {
86
- const buffers: Record<string, number> = {
87
- 'createTransaction': 1.15, // 15% - Simple state initialization
88
- 'transitionState': 1.20, // 20% - Standard state change
89
- 'releaseEscrow': 1.30, // 30% - Multi-recipient disbursement
90
- 'raiseDispute': 1.25, // 25% - Large proof data handling
91
- 'resolveDispute': 1.30, // 30% - Complex multi-party settlement
92
- 'cancelTransaction': 1.15, // 15% - Simple state change
93
- 'anchorAttestation': 1.15 // 15% - Simple attestation anchoring
94
- };
95
-
96
- return buffers[operation] || 1.20; // Default 20% for unknown operations
97
- }
98
-
99
- /**
100
- * Build transaction options with gas settings and estimated gas
101
- * V6 Enhancement: Dynamic buffer based on operation type
102
- *
103
- * SECURITY FIX (C-3): Gas estimation manipulation attack protection
104
- * - Enforces operation-specific minimum gas floors (not global 100k)
105
- * - Validates gas limit doesn't exceed block gas limit (DoS prevention)
106
- * - Uses safe BigInt arithmetic with overflow detection
107
- * - Prevents floating-point arithmetic (uses BPS - basis points)
108
- */
109
- private buildTxOptions(estimatedGas: bigint, operation: string = 'default'): any {
110
- // SECURITY FIX (C-3): Operation-specific minimum gas floors
111
- // Malicious contracts could return artificially low gas estimates to cause txs to fail
112
- const MIN_GAS_FLOORS: Record<string, bigint> = {
113
- 'createTransaction': 120000n, // Create + event emission
114
- 'transitionState': 80000n, // State update + event
115
- 'releaseEscrow': 220000n, // Multi-recipient disbursement + events
116
- 'raiseDispute': 100000n, // Large proof data encoding
117
- 'resolveDispute': 250000n, // Complex multi-party settlement
118
- 'cancelTransaction': 60000n, // Simple state change
119
- 'anchorAttestation': 80000n, // Attestation storage
120
- 'default': 100000n // Conservative fallback
121
- };
122
-
123
- const minFloor = MIN_GAS_FLOORS[operation] || MIN_GAS_FLOORS['default'];
124
- const safeEstimate = estimatedGas > minFloor ? estimatedGas : minFloor;
125
-
126
- const bufferMultiplier = this.getGasBufferMultiplier(operation);
127
-
128
- // SECURITY FIX (C-3): Safe BigInt arithmetic using BPS (basis points)
129
- // Multiply by (bufferMultiplier * 10000) and divide by 10000
130
- // Example: 1.15x = (115 * 10000) / 10000 = 11500 / 10000
131
- // This avoids floating-point precision issues entirely
132
- const bufferNumerator = BigInt(Math.floor(bufferMultiplier * 10000));
133
- const bufferDenominator = 10000n;
134
- const gasLimit = (safeEstimate * bufferNumerator) / bufferDenominator;
135
-
136
- // SECURITY FIX (C-3): Overflow detection
137
- // After multiplication and division, result MUST be >= original estimate
138
- if (gasLimit < safeEstimate) {
139
- throw new Error(
140
- `Gas calculation overflow detected for operation "${operation}". ` +
141
- `Estimate: ${safeEstimate}, Buffer: ${bufferMultiplier}x, Result: ${gasLimit}. ` +
142
- `This indicates an arithmetic overflow - please report this bug.`
143
- );
144
- }
145
-
146
- // SECURITY FIX (C-3): Block gas limit check (Base L2 = 30M gas)
147
- // Prevents DoS by requesting excessive gas that can never be included
148
- const MAX_BLOCK_GAS_LIMIT = 30_000_000n;
149
- if (gasLimit > MAX_BLOCK_GAS_LIMIT) {
150
- throw new Error(
151
- `Gas limit ${gasLimit} exceeds maximum block gas limit ${MAX_BLOCK_GAS_LIMIT} for operation "${operation}". ` +
152
- `This transaction cannot be executed on-chain. ` +
153
- `Estimated gas: ${estimatedGas}, Min floor: ${minFloor}, Buffer: ${bufferMultiplier}x.`
154
- );
155
- }
156
-
157
- const options: any = {
158
- gasLimit
159
- };
160
-
161
- if (this.gasSettings?.maxFeePerGas) {
162
- options.maxFeePerGas = this.gasSettings.maxFeePerGas;
163
- }
164
- if (this.gasSettings?.maxPriorityFeePerGas) {
165
- options.maxPriorityFeePerGas = this.gasSettings.maxPriorityFeePerGas;
166
- }
167
-
168
- return options;
169
- }
170
-
171
- /**
172
- * Create a new transaction
173
- * Reference: Yellow Paper §3.4.1
174
- *
175
- * Contract signature: createTransaction(provider, requester, amount, deadline, disputeWindow, serviceHash, agentId)
176
- * Returns: bytes32 transactionId (generated by contract)
177
- */
178
- async createTransaction(params: CreateTransactionParams): Promise<string> {
179
- const {
180
- provider,
181
- requester,
182
- amount,
183
- deadline,
184
- disputeWindow,
185
- metadata = '0x0000000000000000000000000000000000000000000000000000000000000000',
186
- agentId = '0' // Default to 0 (not an ERC-8004 agent)
187
- } = params;
188
-
189
- // Input validation
190
- validateAddress(provider, 'provider');
191
- validateAddress(requester, 'requester');
192
- validateAmount(amount, 'amount');
193
- validateDeadline(deadline, 'deadline');
194
- validateDisputeWindow(disputeWindow, 'disputeWindow');
195
-
196
- try {
197
- // ethers v6: use getFunction() for typed access
198
- const createTxFunc = this.contract.getFunction('createTransaction');
199
-
200
- // Contract signature: createTransaction(provider, requester, amount, deadline, disputeWindow, serviceHash, agentId)
201
- const estimatedGas = await createTxFunc.estimateGas(
202
- provider,
203
- requester,
204
- amount,
205
- deadline,
206
- disputeWindow,
207
- metadata, // serviceHash
208
- BigInt(agentId) // ERC-8004 agent ID (0 = not an agent)
209
- );
210
-
211
- // Build tx options with gas settings (15% buffer for simple state initialization)
212
- const txOptions = this.buildTxOptions(estimatedGas, 'createTransaction');
213
-
214
- // Per ABI: createTransaction returns transactionId directly
215
- // Contract signature: function createTransaction(...) external returns (bytes32 transactionId)
216
- const tx = await createTxFunc(
217
- provider,
218
- requester,
219
- amount,
220
- deadline,
221
- disputeWindow,
222
- metadata, // serviceHash
223
- BigInt(agentId), // ERC-8004 agent ID
224
- txOptions
225
- );
226
-
227
- const receipt = await tx.wait(this.confirmations);
228
- if (!receipt) {
229
- throw new Error('Transaction receipt not available');
230
- }
231
-
232
- // Extract transactionId from TransactionCreated event
233
- // Event signature: TransactionCreated(bytes32 indexed transactionId, address indexed requester, address indexed provider, ...)
234
- //
235
- // DOCUMENTATION (CRITICAL-3): Note parameter order difference:
236
- // - Function: createTransaction(provider, requester, ...) - provider is first
237
- // - Event: TransactionCreated(txId, requester, provider, ...) - requester is first after txId
238
- // This is INTENTIONAL - function names main actor (provider), event logs initiator first (requester)
239
- // SDK correctly uses named args (parsedLog.args.transactionId) to avoid confusion
240
- for (const log of receipt.logs) {
241
- try {
242
- const parsedLog = this.contract.interface.parseLog({
243
- topics: [...log.topics],
244
- data: log.data
245
- });
246
-
247
- if (parsedLog && parsedLog.name === 'TransactionCreated') {
248
- // Use named arg for clarity (avoids index confusion with swapped provider/requester)
249
- return parsedLog.args.transactionId || parsedLog.args[0];
250
- }
251
- } catch (e) {
252
- // Skip logs that don't match our interface
253
- continue;
254
- }
255
- }
256
-
257
- throw new Error('TransactionCreated event not found in receipt');
258
- } catch (error: any) {
259
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
260
- }
261
- }
262
-
263
- /**
264
- * Transition transaction state
265
- * Reference: Yellow Paper §3.2
266
- */
267
- async transitionState(
268
- txId: string,
269
- newState: State,
270
- proof: BytesLike = '0x'
271
- ): Promise<void> {
272
- // Input validation
273
- validateTxId(txId, 'txId');
274
-
275
- // Validate transition
276
- const currentTx = await this.getTransaction(txId);
277
- if (!StateMachine.isValidTransition(currentTx.state, newState)) {
278
- const validStates = StateMachine.getNextValidStates(currentTx.state).map((s) =>
279
- State[s]
280
- );
281
- throw new InvalidStateTransitionError(currentTx.state, newState, validStates);
282
- }
283
-
284
- try {
285
- // ethers v6: use getFunction()
286
- const transitionFunc = this.contract.getFunction('transitionState');
287
-
288
- // Estimate gas with safety buffer (20% for standard state transitions)
289
- const estimatedGas = await transitionFunc.estimateGas(txId, newState, proof);
290
- const txOptions = this.buildTxOptions(estimatedGas, 'transitionState');
291
-
292
- const tx = await transitionFunc(txId, newState, proof, txOptions);
293
-
294
- await tx.wait(this.confirmations);
295
- } catch (error: any) {
296
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
297
- }
298
- }
299
-
300
- /**
301
- * Submit quote for transaction (AIP-2)
302
- * Reference: AIP-2 §4.1 (Provider workflow)
303
- *
304
- * Transitions transaction from INITIATED → QUOTED with quote hash stored on-chain
305
- *
306
- * @param txId - Transaction ID (bytes32)
307
- * @param quoteHash - Keccak256 hash of canonical JSON quote message
308
- */
309
- async submitQuote(txId: string, quoteHash: string): Promise<void> {
310
- // Input validation
311
- validateTxId(txId, 'txId');
312
-
313
- if (!/^0x[a-fA-F0-9]{64}$/.test(quoteHash)) {
314
- throw new ValidationError('quoteHash', 'Must be valid bytes32 hex string');
315
- }
316
-
317
- if (quoteHash === '0x0000000000000000000000000000000000000000000000000000000000000000') {
318
- throw new ValidationError('quoteHash', 'Cannot be zero hash');
319
- }
320
-
321
- // Validate current state is INITIATED
322
- const currentTx = await this.getTransaction(txId);
323
- if (currentTx.state !== State.INITIATED) {
324
- throw new InvalidStateTransitionError(
325
- currentTx.state,
326
- State.QUOTED,
327
- ['INITIATED']
328
- );
329
- }
330
-
331
- // Encode quote hash as bytes proof
332
- const abiCoder = AbiCoder.defaultAbiCoder();
333
- const proof = abiCoder.encode(['bytes32'], [quoteHash]);
334
-
335
- // Transition to QUOTED state with quote hash
336
- await this.transitionState(txId, State.QUOTED, proof);
337
- }
338
-
339
- /**
340
- * Link escrow to transaction
341
- *
342
- * CRITICAL: This is the ONLY way to create escrow per AIP-3 spec.
343
- * SDK should NOT call EscrowVault.createEscrow() directly (onlyKernel modifier).
344
- *
345
- * What happens internally:
346
- * 1. ACTPKernel validates transaction state, permissions, deadline
347
- * 2. Kernel calls IEscrowValidator(escrowContract).createEscrow(...)
348
- * 3. EscrowVault pulls USDC from consumer (must approve USDC first!)
349
- * 4. Events emitted: EscrowLinked
350
- * 5. **State transition behavior varies** (see below)
351
- *
352
- * STATE TRANSITION BEHAVIOR:
353
- * - **AIP-3 Spec (Source Code)**: Should auto-transition INITIATED/QUOTED → COMMITTED
354
- * - **Deployed Contract**: Behavior is INCONSISTENT - sometimes auto-transitions, sometimes doesn't
355
- * - **Recommended Practice**: Always check state after linkEscrow() and manually transition if needed
356
- *
357
- * ```typescript
358
- * await client.kernel.linkEscrow(txId, escrowVault, escrowId);
359
- * let tx = await client.kernel.getTransaction(txId);
360
- * if (tx.state !== State.COMMITTED) {
361
- * await client.kernel.transitionState(txId, State.COMMITTED);
362
- * }
363
- * ```
364
- *
365
- * Prerequisites:
366
- * - Transaction in INITIATED or QUOTED state
367
- * - Consumer has approved USDC to EscrowVault address
368
- * (use EscrowVault.approveToken() before calling this)
369
- *
370
- * @param txId - Transaction ID (bytes32)
371
- * @param escrowContract - EscrowVault contract address
372
- * @param escrowId - Unique escrow identifier (bytes32, consumer-generated)
373
- * @throws {ValidationError} If inputs invalid
374
- * @throws {TransactionRevertedError} If state invalid, deadline passed, or insufficient USDC
375
- *
376
- * @example
377
- * ```typescript
378
- * // Step 1: Approve USDC to EscrowVault (NOT to Kernel!)
379
- * await client.escrow.approveToken(BASE_SEPOLIA.contracts.usdc, amount);
380
- *
381
- * // Step 2: Generate unique escrow ID
382
- * const escrowId = ethers.id(`escrow-${Date.now()}`);
383
- *
384
- * // Step 3: Link escrow (creates escrow + auto-transitions to COMMITTED)
385
- * await client.kernel.linkEscrow(txId, escrowVaultAddress, escrowId);
386
- *
387
- * // Step 4: Verify state is COMMITTED (auto-transitioned, no manual call needed)
388
- * const tx = await client.kernel.getTransaction(txId);
389
- * expect(tx.state).to.equal(State.COMMITTED);
390
- * ```
391
- *
392
- * Reference: Yellow Paper §3.4.2, AIP-3 §3.2 (ACTPKernel.sol lines 244-276)
393
- */
394
- async linkEscrow(
395
- txId: string,
396
- escrowContract: string,
397
- escrowId: string
398
- ): Promise<void> {
399
- // Input validation
400
- validateTxId(txId, 'txId');
401
- validateAddress(escrowContract, 'escrowContract');
402
- validateTxId(escrowId, 'escrowId'); // escrowId is also bytes32
403
-
404
- try {
405
- // ethers v6: use getFunction()
406
- const linkEscrowFunc = this.contract.getFunction('linkEscrow');
407
-
408
- // Estimate gas with safety buffer (20% for linking escrow)
409
- const estimatedGas = await linkEscrowFunc.estimateGas(txId, escrowContract, escrowId);
410
- const txOptions = this.buildTxOptions(estimatedGas, 'transitionState');
411
-
412
- const tx = await linkEscrowFunc(txId, escrowContract, escrowId, txOptions);
413
-
414
- await tx.wait(this.confirmations);
415
- } catch (error: any) {
416
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
417
- }
418
- }
419
-
420
- /**
421
- * Release milestone payment
422
- *
423
- * SECURITY FIX (CRITICAL-2): Contract ABI has only 2 params (txId, amount), not 3.
424
- * The milestoneId is NOT part of the current ACTPKernel V1 contract.
425
- * Per ABI: releaseMilestone(bytes32 transactionId, uint256 amount)
426
- *
427
- * @param txId - Transaction ID (bytes32)
428
- * @param amount - Amount to release (uint256)
429
- * @deprecated milestoneId parameter - removed as contract doesn't support it
430
- */
431
- async releaseMilestone(
432
- txId: string,
433
- amount: bigint
434
- ): Promise<void> {
435
- // Input validation
436
- validateTxId(txId, 'txId');
437
- validateAmount(amount, 'amount');
438
-
439
- try {
440
- // ethers v6: use getFunction()
441
- const releaseMilestoneFunc = this.contract.getFunction('releaseMilestone');
442
-
443
- // SECURITY FIX (CRITICAL-2): Contract only takes 2 params (txId, amount)
444
- // Estimate gas with safety buffer (30% for escrow release operations)
445
- const estimatedGas = await releaseMilestoneFunc.estimateGas(txId, amount);
446
- const txOptions = this.buildTxOptions(estimatedGas, 'releaseEscrow');
447
-
448
- const tx = await releaseMilestoneFunc(txId, amount, txOptions);
449
-
450
- await tx.wait(this.confirmations);
451
- } catch (error: any) {
452
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
453
- }
454
- }
455
-
456
- /**
457
- * Release full escrow (settle transaction)
458
- *
459
- * ⚠️ CRITICAL SECURITY WARNING (C-2): Attestation UID Validation Bypass
460
- *
461
- * **DO NOT call this method directly from your application code!**
462
- *
463
- * ACTPKernel V1 contract accepts any attestationUID without validation.
464
- * A malicious provider can:
465
- * - Submit an attestation from a different transaction
466
- * - Re-use an old attestation (replay attack)
467
- * - Submit a forged attestation with fake delivery proof
468
- *
469
- * **REQUIRED: Use secure wrapper methods instead:**
470
- *
471
- * 1. **BasicAdapter.completePayment()** (recommended for most users)
472
- * - Automatically verifies attestation before release
473
- * - Validates attestation belongs to this transaction
474
- * - Checks attestation hasn't been used before
475
- * - Handles all state transitions
476
- *
477
- * 2. **StandardAdapter.releaseEscrow()** (for more control)
478
- * - Explicitly requires attestation verification
479
- * - Throws error if attestation invalid or missing
480
- * - Allows custom verification logic
481
- *
482
- * 3. **Manual verification** (advanced users only):
483
- * ```typescript
484
- * // Step 1: Get transaction details
485
- * const tx = await kernel.getTransaction(txId);
486
- *
487
- * // Step 2: Verify attestation if EAS is configured
488
- * if (easHelper && tx.attestationUID && tx.attestationUID !== '0x0...0') {
489
- * const isValid = await easHelper.verifyDeliveryAttestation(
490
- * tx.attestationUID,
491
- * tx.requester
492
- * );
493
- * if (!isValid) {
494
- * throw new Error('Invalid or fraudulent delivery attestation');
495
- * }
496
- * }
497
- *
498
- * // Step 3: Only now is it safe to release
499
- * await kernel.releaseEscrow(txId);
500
- * ```
501
- *
502
- * **Why this matters:**
503
- * - Without verification, you risk paying for work never delivered
504
- * - Provider can steal funds by re-using attestations from other transactions
505
- * - No on-chain enforcement (contract V1 limitation, fixed in V2)
506
- *
507
- * **For testnet/mainnet deployments:**
508
- * - MUST configure easConfig in ACTPClient
509
- * - MUST use wrapper methods (Basic/Standard adapters)
510
- * - NEVER call this method directly unless attestation verified
511
- *
512
- * @param txId - Transaction ID to settle
513
- * @throws {ValidationError} If txId is invalid
514
- * @throws {TransactionRevertedError} If contract reverts
515
- *
516
- * @see {@link BasicAdapter.completePayment} Recommended method with built-in verification
517
- * @see {@link StandardAdapter.releaseEscrow} Explicit verification method
518
- * @see {@link EASHelper.verifyDeliveryAttestation} Manual verification helper
519
- */
520
- async releaseEscrow(txId: string): Promise<void> {
521
- // Input validation
522
- validateTxId(txId, 'txId');
523
-
524
- try {
525
- // ethers v6: use getFunction()
526
- const releaseEscrowFunc = this.contract.getFunction('releaseEscrow');
527
-
528
- // Estimate gas with safety buffer (30% for escrow release operations)
529
- const estimatedGas = await releaseEscrowFunc.estimateGas(txId);
530
- const txOptions = this.buildTxOptions(estimatedGas, 'releaseEscrow');
531
-
532
- const tx = await releaseEscrowFunc(txId, txOptions);
533
-
534
- await tx.wait(this.confirmations);
535
- } catch (error: any) {
536
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
537
- }
538
- }
539
-
540
- /**
541
- * Get transaction by ID
542
- */
543
- async getTransaction(txId: string): Promise<Transaction> {
544
- let txData: any;
545
- try {
546
- txData = await this.contract.getTransaction(txId);
547
- } catch (error: any) {
548
- const reason = error?.reason || error?.shortMessage || error?.message || '';
549
-
550
- // Deployed kernel reverts on missing transactions (e.g., "Tx missing")
551
- if (typeof reason === 'string' && reason.toLowerCase().includes('tx missing')) {
552
- throw new TransactionNotFoundError(txId);
553
- }
554
-
555
- throw new Error(
556
- `Failed to fetch transaction ${txId}: ${typeof reason === 'string' ? reason : String(reason)}`
557
- );
558
- }
559
-
560
- // Check if transaction exists (createdAt !== 0)
561
- if (txData.createdAt === 0 || txData.createdAt === 0n) {
562
- throw new TransactionNotFoundError(txId);
563
- }
564
-
565
- // Parse agentId - convert BigInt to string, 0n means "not an ERC-8004 agent"
566
- const agentIdValue = typeof txData.agentId === 'bigint'
567
- ? txData.agentId.toString()
568
- : txData.agentId?.toString();
569
-
570
- return {
571
- txId: txData.transactionId,
572
- requester: txData.requester,
573
- provider: txData.provider,
574
- amount: txData.amount,
575
- state: (typeof txData.state === 'bigint' ? Number(txData.state) : txData.state) as State,
576
- createdAt: typeof txData.createdAt === 'bigint' ? Number(txData.createdAt) : txData.createdAt,
577
- updatedAt: typeof txData.updatedAt === 'bigint' ? Number(txData.updatedAt) : txData.updatedAt,
578
- deadline: typeof txData.deadline === 'bigint' ? Number(txData.deadline) : txData.deadline,
579
- disputeWindow: typeof txData.disputeWindow === 'bigint' ? Number(txData.disputeWindow) : txData.disputeWindow,
580
- escrowContract: txData.escrowContract,
581
- escrowId: txData.escrowId,
582
- serviceHash: txData.serviceHash,
583
- attestationUID: txData.attestationUID,
584
- // Use metadata field (quote hash for QUOTED state) if available, fallback to serviceHash
585
- metadata: txData.metadata || txData.serviceHash,
586
- platformFeeBpsLocked:
587
- typeof txData.platformFeeBpsLocked === 'bigint'
588
- ? Number(txData.platformFeeBpsLocked)
589
- : txData.platformFeeBpsLocked,
590
- // ERC-8004 agent ID (undefined or '0' means not an ERC-8004 agent)
591
- agentId: agentIdValue && agentIdValue !== '0' ? agentIdValue : undefined
592
- };
593
- }
594
-
595
- /**
596
- * Get economic parameters (fee structure)
597
- *
598
- * SECURITY FIX (CRITICAL-4): Contract doesn't have getEconomicParams() function.
599
- * Must call individual getters: platformFeeBps(), requesterPenaltyBps(), feeRecipient()
600
- * Per ACTPKernel.json ABI lines 576-586, 619-630, 351-361
601
- */
602
- async getEconomicParams(): Promise<EconomicParams> {
603
- // SECURITY FIX (CRITICAL-4): Call individual view functions in parallel
604
- // Contract ABI has: platformFeeBps(), requesterPenaltyBps(), feeRecipient()
605
- // NOT a combined getEconomicParams() function
606
- const [platformFeeBps, requesterPenaltyBps, feeRecipient] = await Promise.all([
607
- this.contract.platformFeeBps(),
608
- this.contract.requesterPenaltyBps(),
609
- this.contract.feeRecipient()
610
- ]);
611
-
612
- return {
613
- baseFeeNumerator: Number(platformFeeBps),
614
- baseFeeDenominator: 10000, // BPS is always out of 10000
615
- feeRecipient: feeRecipient,
616
- requesterPenaltyBps: Number(requesterPenaltyBps),
617
- providerPenaltyBps: 0 // Not in current contract ABI, will be added in future version
618
- };
619
- }
620
-
621
- /**
622
- * Estimate gas for transaction creation
623
- */
624
- async estimateCreateTransaction(params: CreateTransactionParams): Promise<bigint> {
625
- const {
626
- provider,
627
- requester,
628
- amount,
629
- deadline,
630
- disputeWindow,
631
- metadata = '0x0000000000000000000000000000000000000000000000000000000000000000',
632
- agentId = '0'
633
- } = params;
634
-
635
- // ethers v6: use getFunction()
636
- const createTxFunc = this.contract.getFunction('createTransaction');
637
- return await createTxFunc.estimateGas(
638
- provider,
639
- requester,
640
- amount,
641
- deadline,
642
- disputeWindow,
643
- metadata,
644
- BigInt(agentId)
645
- );
646
- }
647
-
648
- /**
649
- * Raise dispute on delivered transaction
650
- * Reference: Yellow Paper §3.4 (Dispute Management)
651
- */
652
- async raiseDispute(txId: string, reason: string, evidence: string): Promise<void> {
653
- validateTxId(txId, 'txId');
654
-
655
- // Encode dispute proof with reason and evidence (IPFS hash)
656
- const abiCoder = AbiCoder.defaultAbiCoder();
657
- const proofData = abiCoder.encode(
658
- ['string', 'string'],
659
- [reason, evidence]
660
- );
661
-
662
- try {
663
- // ethers v6: use getFunction()
664
- const transitionFunc = this.contract.getFunction('transitionState');
665
-
666
- // Estimate gas with safety buffer (25% for large proof data)
667
- const estimatedGas = await transitionFunc.estimateGas(
668
- txId,
669
- State.DISPUTED,
670
- proofData
671
- );
672
-
673
- const txOptions = this.buildTxOptions(estimatedGas, 'raiseDispute');
674
-
675
- const tx = await transitionFunc(
676
- txId,
677
- State.DISPUTED,
678
- proofData,
679
- txOptions
680
- );
681
-
682
- await tx.wait(this.confirmations);
683
- } catch (error: any) {
684
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
685
- }
686
- }
687
-
688
- /**
689
- * Resolve/settle dispute with payment split
690
- * Reference: Yellow Paper §3.4
691
- *
692
- * Disputes are settled via transitionState(SETTLED, proof) per §3.2
693
- * The kernel contract decodes the proof and handles escrow disbursement
694
- */
695
- async resolveDispute(txId: string, resolution: DisputeResolution): Promise<void> {
696
- validateTxId(txId, 'txId');
697
-
698
- const { requesterAmount, providerAmount, mediatorAmount, mediator } = resolution;
699
-
700
- // Validate amounts are non-negative
701
- if (requesterAmount < 0n || providerAmount < 0n || mediatorAmount < 0n) {
702
- throw new Error('Dispute resolution amounts cannot be negative');
703
- }
704
-
705
- // Validate mediator address if mediator amount > 0
706
- if (mediatorAmount > 0n) {
707
- if (!mediator) {
708
- throw new Error('Mediator address required when mediator amount > 0');
709
- }
710
- validateAddress(mediator, 'mediator');
711
- }
712
-
713
- // Encode resolution proof (128 bytes: 2x uint256 + address + uint256)
714
- // AUDIT FIX (2026-02): Contract expects: (uint256, uint256, address, uint256)
715
- // = [requesterAmount, providerAmount, mediator, mediatorAmount]
716
- // See ACTPKernel.sol _decodeResolutionProof() line 654-655
717
- const abiCoder = AbiCoder.defaultAbiCoder();
718
- const proofData = abiCoder.encode(
719
- ['uint256', 'uint256', 'address', 'uint256'],
720
- [
721
- requesterAmount,
722
- providerAmount,
723
- mediator || ethers.getAddress('0x0000000000000000000000000000000000000000'),
724
- mediatorAmount
725
- ]
726
- );
727
-
728
- try {
729
- // ethers v6: use getFunction()
730
- const transitionFunc = this.contract.getFunction('transitionState');
731
-
732
- // Settle dispute via state transition to SETTLED with resolution proof (30% buffer)
733
- const estimatedGas = await transitionFunc.estimateGas(
734
- txId,
735
- State.SETTLED,
736
- proofData
737
- );
738
-
739
- const txOptions = this.buildTxOptions(estimatedGas, 'resolveDispute');
740
-
741
- const tx = await transitionFunc(
742
- txId,
743
- State.SETTLED,
744
- proofData,
745
- txOptions
746
- );
747
-
748
- await tx.wait(this.confirmations);
749
- } catch (error: any) {
750
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
751
- }
752
- }
753
-
754
- /**
755
- * Settle disputed transaction (alias for resolveDispute)
756
- */
757
- async settleDispute(txId: string, resolution: DisputeResolution): Promise<void> {
758
- return this.resolveDispute(txId, resolution);
759
- }
760
-
761
- /**
762
- * Anchor an EAS attestation UID to a transaction (delivery proof)
763
- * Reference: AIP-4 (Delivery Proof and EAS Attestation Standard)
764
- *
765
- * @param txId - Transaction ID
766
- * @param attestationUID - EAS attestation UID from provider
767
- * @throws {ValidationError} If inputs are invalid
768
- * @throws {TransactionRevertedError} If contract reverts
769
- *
770
- * @example
771
- * ```typescript
772
- * const easHelper = new EASHelper(signer, easConfig);
773
- * const attestation = await easHelper.attestDeliveryProof(proof, recipient);
774
- * await kernel.anchorAttestation(txId, attestation.uid);
775
- * ```
776
- */
777
- async anchorAttestation(txId: string, attestationUID: string): Promise<void> {
778
- validateTxId(txId, 'txId');
779
-
780
- // Validate attestationUID format (32-byte hex string)
781
- if (!attestationUID || !/^0x[a-fA-F0-9]{64}$/.test(attestationUID)) {
782
- throw new ValidationError('attestationUID', 'Must be 32-byte hex string (0x...)');
783
- }
784
-
785
- try {
786
- // ethers v6: use getFunction()
787
- const anchorFunc = this.contract.getFunction('anchorAttestation');
788
-
789
- const estimatedGas = await anchorFunc.estimateGas(
790
- txId,
791
- attestationUID
792
- );
793
-
794
- const txOptions = this.buildTxOptions(estimatedGas, 'anchorAttestation');
795
-
796
- const tx = await anchorFunc(
797
- txId,
798
- attestationUID,
799
- txOptions
800
- );
801
-
802
- await tx.wait(this.confirmations);
803
- } catch (error: any) {
804
- throw new TransactionRevertedError(error.transactionHash, error.reason || error.message);
805
- }
806
- }
807
-
808
- }