@agirails/sdk 2.0.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +536 -87
  2. package/dist/adapters/BaseAdapter.js +2 -2
  3. package/dist/adapters/BaseAdapter.js.map +1 -1
  4. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  5. package/dist/adapters/BasicAdapter.js +8 -0
  6. package/dist/adapters/BasicAdapter.js.map +1 -1
  7. package/dist/adapters/StandardAdapter.d.ts +10 -5
  8. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  9. package/dist/adapters/StandardAdapter.js +19 -6
  10. package/dist/adapters/StandardAdapter.js.map +1 -1
  11. package/dist/builders/QuoteBuilder.js +1 -1
  12. package/dist/builders/QuoteBuilder.js.map +1 -1
  13. package/dist/cli/commands/config.js +1 -1
  14. package/dist/cli/commands/config.js.map +1 -1
  15. package/dist/cli/commands/simulate.js.map +1 -1
  16. package/dist/cli/commands/time.d.ts.map +1 -1
  17. package/dist/cli/commands/time.js.map +1 -1
  18. package/dist/config/networks.d.ts +9 -0
  19. package/dist/config/networks.d.ts.map +1 -1
  20. package/dist/config/networks.js +25 -10
  21. package/dist/config/networks.js.map +1 -1
  22. package/dist/index.d.ts +6 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +31 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/level0/provide.d.ts.map +1 -1
  27. package/dist/level0/provide.js +2 -1
  28. package/dist/level0/provide.js.map +1 -1
  29. package/dist/level0/request.d.ts.map +1 -1
  30. package/dist/level0/request.js +1 -2
  31. package/dist/level0/request.js.map +1 -1
  32. package/dist/level1/Agent.d.ts.map +1 -1
  33. package/dist/level1/Agent.js +11 -3
  34. package/dist/level1/Agent.js.map +1 -1
  35. package/dist/level1/pricing/PriceCalculator.js +1 -1
  36. package/dist/level1/pricing/PriceCalculator.js.map +1 -1
  37. package/dist/level1/types/Options.d.ts.map +1 -1
  38. package/dist/protocol/ACTPKernel.d.ts.map +1 -1
  39. package/dist/protocol/ACTPKernel.js +7 -5
  40. package/dist/protocol/ACTPKernel.js.map +1 -1
  41. package/dist/protocol/DIDResolver.js +1 -1
  42. package/dist/protocol/DIDResolver.js.map +1 -1
  43. package/dist/protocol/EASHelper.d.ts.map +1 -1
  44. package/dist/protocol/EASHelper.js +2 -3
  45. package/dist/protocol/EASHelper.js.map +1 -1
  46. package/dist/protocol/MessageSigner.d.ts.map +1 -1
  47. package/dist/protocol/MessageSigner.js +9 -9
  48. package/dist/protocol/MessageSigner.js.map +1 -1
  49. package/dist/protocol/ProofGenerator.d.ts.map +1 -1
  50. package/dist/protocol/ProofGenerator.js +1 -0
  51. package/dist/protocol/ProofGenerator.js.map +1 -1
  52. package/dist/runtime/BlockchainRuntime.d.ts +10 -3
  53. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  54. package/dist/runtime/BlockchainRuntime.js +41 -25
  55. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  56. package/dist/runtime/IACTPRuntime.d.ts +15 -0
  57. package/dist/runtime/IACTPRuntime.d.ts.map +1 -1
  58. package/dist/runtime/MockRuntime.d.ts +7 -0
  59. package/dist/runtime/MockRuntime.d.ts.map +1 -1
  60. package/dist/runtime/MockRuntime.js +15 -4
  61. package/dist/runtime/MockRuntime.js.map +1 -1
  62. package/dist/runtime/types/MockState.d.ts +5 -2
  63. package/dist/runtime/types/MockState.d.ts.map +1 -1
  64. package/dist/runtime/types/MockState.js.map +1 -1
  65. package/dist/storage/ArchiveBundleBuilder.d.ts +150 -0
  66. package/dist/storage/ArchiveBundleBuilder.d.ts.map +1 -0
  67. package/dist/storage/ArchiveBundleBuilder.js +468 -0
  68. package/dist/storage/ArchiveBundleBuilder.js.map +1 -0
  69. package/dist/storage/ArweaveClient.d.ts +271 -0
  70. package/dist/storage/ArweaveClient.d.ts.map +1 -0
  71. package/dist/storage/ArweaveClient.js +761 -0
  72. package/dist/storage/ArweaveClient.js.map +1 -0
  73. package/dist/storage/FilebaseClient.d.ts +193 -0
  74. package/dist/storage/FilebaseClient.d.ts.map +1 -0
  75. package/dist/storage/FilebaseClient.js +643 -0
  76. package/dist/storage/FilebaseClient.js.map +1 -0
  77. package/dist/storage/index.d.ts +47 -0
  78. package/dist/storage/index.d.ts.map +1 -0
  79. package/dist/storage/index.js +64 -0
  80. package/dist/storage/index.js.map +1 -0
  81. package/dist/storage/types.d.ts +291 -0
  82. package/dist/storage/types.d.ts.map +1 -0
  83. package/dist/storage/types.js +18 -0
  84. package/dist/storage/types.js.map +1 -0
  85. package/dist/types/state.d.ts +5 -4
  86. package/dist/types/state.d.ts.map +1 -1
  87. package/dist/types/state.js +10 -9
  88. package/dist/types/state.js.map +1 -1
  89. package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -1
  90. package/dist/utils/ErrorRecoveryGuide.js +1 -2
  91. package/dist/utils/ErrorRecoveryGuide.js.map +1 -1
  92. package/dist/utils/IPFSClient.d.ts.map +1 -1
  93. package/dist/utils/IPFSClient.js +5 -2
  94. package/dist/utils/IPFSClient.js.map +1 -1
  95. package/dist/utils/NonceManager.d.ts.map +1 -1
  96. package/dist/utils/NonceManager.js +3 -2
  97. package/dist/utils/NonceManager.js.map +1 -1
  98. package/dist/utils/UsedAttestationTracker.d.ts +1 -1
  99. package/dist/utils/UsedAttestationTracker.d.ts.map +1 -1
  100. package/dist/utils/UsedAttestationTracker.js +4 -4
  101. package/dist/utils/UsedAttestationTracker.js.map +1 -1
  102. package/dist/utils/circuitBreaker.d.ts +136 -0
  103. package/dist/utils/circuitBreaker.d.ts.map +1 -0
  104. package/dist/utils/circuitBreaker.js +253 -0
  105. package/dist/utils/circuitBreaker.js.map +1 -0
  106. package/dist/utils/retry.d.ts +120 -0
  107. package/dist/utils/retry.d.ts.map +1 -0
  108. package/dist/utils/retry.js +260 -0
  109. package/dist/utils/retry.js.map +1 -0
  110. package/dist/utils/validation.d.ts +100 -0
  111. package/dist/utils/validation.d.ts.map +1 -1
  112. package/dist/utils/validation.js +248 -1
  113. package/dist/utils/validation.js.map +1 -1
  114. package/package.json +14 -2
  115. package/src/adapters/BaseAdapter.ts +2 -2
  116. package/src/adapters/BasicAdapter.ts +12 -2
  117. package/src/adapters/StandardAdapter.ts +27 -7
  118. package/src/builders/QuoteBuilder.ts +1 -1
  119. package/src/cli/commands/config.ts +1 -1
  120. package/src/cli/commands/simulate.ts +1 -1
  121. package/src/cli/commands/time.ts +1 -2
  122. package/src/config/networks.ts +34 -10
  123. package/src/index.ts +54 -0
  124. package/src/level0/provide.ts +2 -1
  125. package/src/level0/request.ts +1 -2
  126. package/src/level1/Agent.ts +15 -5
  127. package/src/level1/pricing/PriceCalculator.ts +1 -1
  128. package/src/level1/types/Options.ts +1 -1
  129. package/src/protocol/ACTPKernel.ts +7 -5
  130. package/src/protocol/DIDResolver.ts +1 -1
  131. package/src/protocol/EASHelper.ts +2 -5
  132. package/src/protocol/MessageSigner.ts +9 -15
  133. package/src/protocol/ProofGenerator.ts +1 -0
  134. package/src/runtime/BlockchainRuntime.ts +42 -48
  135. package/src/runtime/IACTPRuntime.ts +16 -0
  136. package/src/runtime/MockRuntime.ts +16 -6
  137. package/src/runtime/types/MockState.ts +5 -2
  138. package/src/storage/ArchiveBundleBuilder.ts +563 -0
  139. package/src/storage/ArweaveClient.ts +945 -0
  140. package/src/storage/FilebaseClient.ts +790 -0
  141. package/src/storage/index.ts +96 -0
  142. package/src/storage/types.ts +348 -0
  143. package/src/types/state.ts +10 -9
  144. package/src/utils/ErrorRecoveryGuide.ts +1 -2
  145. package/src/utils/IPFSClient.ts +5 -4
  146. package/src/utils/NonceManager.ts +3 -2
  147. package/src/utils/UsedAttestationTracker.ts +4 -6
  148. package/src/utils/circuitBreaker.ts +324 -0
  149. package/src/utils/fsSafe.ts +5 -0
  150. package/src/utils/retry.ts +365 -0
  151. package/src/utils/validation.ts +295 -1
@@ -12,6 +12,7 @@ import { JobHandler } from '../level1/types/Job';
12
12
  import { ProvideOptions } from '../level1/types/Options';
13
13
  import { Provider } from './Provider';
14
14
  import { serviceDirectory } from './ServiceDirectory';
15
+ import { sdkLogger } from '../utils/Logger';
15
16
 
16
17
  /**
17
18
  * Provide a service
@@ -83,7 +84,7 @@ export function provide(
83
84
  // Register in service directory after agent has started and has an address
84
85
  serviceDirectory.register(service, agent.address);
85
86
  }).catch((error) => {
86
- console.error(`Failed to start provider for ${service}:`, error);
87
+ sdkLogger.error(`Failed to start provider for ${service}`, { error: error instanceof Error ? error.message : String(error) });
87
88
  });
88
89
 
89
90
  // Return Provider interface (adapter over Agent)
@@ -164,7 +164,6 @@ export async function request(
164
164
 
165
165
  let tx = await client.runtime.getTransaction(txId);
166
166
  let attempts = 0;
167
- let timedOut = false;
168
167
 
169
168
  while (tx && tx.state !== 'DELIVERED' && tx.state !== 'SETTLED' && attempts < maxAttempts) {
170
169
  // Check for terminal states that indicate failure
@@ -190,7 +189,7 @@ export async function request(
190
189
 
191
190
  // Check if we got a result
192
191
  if (!tx || (tx.state !== 'DELIVERED' && tx.state !== 'SETTLED')) {
193
- timedOut = true;
192
+ const _timedOut = true; // Flag for potential future use
194
193
 
195
194
  // SECURITY FIX (H-3): Auto-cancel transaction on timeout if still in early state
196
195
  // This prevents funds from being locked indefinitely if provider never responds
@@ -14,10 +14,10 @@ import * as fs from 'fs';
14
14
  import { ethers } from 'ethers';
15
15
  import { ACTPClient } from '../ACTPClient';
16
16
  import { Job, JobHandler, JobContext } from './types/Job';
17
- import { ProvideOptions, RequestOptions, RequestResult, NetworkOption } from './types/Options';
17
+ import { RequestOptions, RequestResult, NetworkOption } from './types/Options';
18
18
  import { PricingStrategy } from './pricing/PricingStrategy';
19
19
  import { AgentLifecycleError, ServiceConfigError, ValidationError } from '../errors';
20
- import { validateServiceName, validatePath, isValidAddress, LRUCache } from '../utils/security';
20
+ import { validateServiceName, validatePath, LRUCache } from '../utils/security';
21
21
  import { Logger } from '../utils/Logger';
22
22
  import { ServiceHash } from '../utils/Helpers';
23
23
  import { Semaphore } from '../utils/Semaphore';
@@ -1232,7 +1232,7 @@ export class Agent extends EventEmitter {
1232
1232
  result, // Include original result for convenience
1233
1233
  });
1234
1234
 
1235
- // Transition transaction to DELIVERED state
1235
+ // Transition transaction through IN_PROGRESS → DELIVERED states
1236
1236
  if (this._client) {
1237
1237
  // Store delivery proof by directly accessing MockRuntime's state
1238
1238
  // This is a workaround - in production, we'd use a proper method
@@ -1246,8 +1246,18 @@ export class Agent extends EventEmitter {
1246
1246
  });
1247
1247
  }
1248
1248
 
1249
- // Transition to DELIVERED (escrow was already linked in pollForJobs)
1250
- await this._client.runtime.transitionState(job.id, 'DELIVERED');
1249
+ // AUDIT FIX (2026-02): Must transition through IN_PROGRESS before DELIVERED
1250
+ // Contract rejects COMMITTED → DELIVERED direct transition
1251
+ await this._client.runtime.transitionState(job.id, 'IN_PROGRESS');
1252
+
1253
+ // Encode dispute window proof for DELIVERED transition
1254
+ // Use transaction's disputeWindow from metadata, fallback to 2 days (172800s) per Options.ts default
1255
+ const disputeWindowSeconds = job.metadata?.disputeWindow || 172800;
1256
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1257
+ const disputeWindowProof = abiCoder.encode(['uint256'], [disputeWindowSeconds]);
1258
+
1259
+ // Transition to DELIVERED with dispute window proof
1260
+ await this._client.runtime.transitionState(job.id, 'DELIVERED', disputeWindowProof);
1251
1261
  }
1252
1262
 
1253
1263
  // SECURITY FIX (C-2): Remove from active jobs on SUCCESS
@@ -214,7 +214,7 @@ function estimateUnits(job: Job, unit: string): number {
214
214
  *
215
215
  * @internal
216
216
  */
217
- function estimateApiCost(api: string, job: Job): number {
217
+ function estimateApiCost(_api: string, _job: Job): number {
218
218
  // MVP: Returns 0 (API costs tracked externally by agent)
219
219
  // V2: Integrate with OpenAI/Anthropic pricing APIs
220
220
 
@@ -4,7 +4,7 @@
4
4
  * @packageDocumentation
5
5
  */
6
6
 
7
- import { JobHandler, Job } from './Job';
7
+ import { Job } from './Job';
8
8
 
9
9
  /**
10
10
  * Wallet configuration options
@@ -681,16 +681,18 @@ export class ACTPKernel {
681
681
  validateAddress(mediator, 'mediator');
682
682
  }
683
683
 
684
- // Encode resolution proof (128 bytes: 3x uint256 + address)
685
- // Kernel contract will decode this in _decodeResolutionProof and disburse funds
684
+ // Encode resolution proof (128 bytes: 2x uint256 + address + uint256)
685
+ // AUDIT FIX (2026-02): Contract expects: (uint256, uint256, address, uint256)
686
+ // = [requesterAmount, providerAmount, mediator, mediatorAmount]
687
+ // See ACTPKernel.sol _decodeResolutionProof() line 654-655
686
688
  const abiCoder = AbiCoder.defaultAbiCoder();
687
689
  const proofData = abiCoder.encode(
688
- ['uint256', 'uint256', 'uint256', 'address'],
690
+ ['uint256', 'uint256', 'address', 'uint256'],
689
691
  [
690
692
  requesterAmount,
691
693
  providerAmount,
692
- mediatorAmount,
693
- mediator || ethers.getAddress('0x0000000000000000000000000000000000000000')
694
+ mediator || ethers.getAddress('0x0000000000000000000000000000000000000000'),
695
+ mediatorAmount
694
696
  ]
695
697
  );
696
698
 
@@ -115,7 +115,7 @@ export class DIDResolver {
115
115
  network: config.network,
116
116
  chainId: config.chainId || networkConfig.chainId,
117
117
  rpcUrl: config.rpcUrl || networkConfig.rpcUrl,
118
- registryAddress: config.registryAddress || networkConfig.contracts.agentRegistry || ''
118
+ registryAddress: config.registryAddress || networkConfig.contracts.identityRegistry || ''
119
119
  };
120
120
  }
121
121
 
@@ -6,6 +6,7 @@ import {
6
6
  IUsedAttestationTracker,
7
7
  InMemoryUsedAttestationTracker
8
8
  } from '../utils/UsedAttestationTracker';
9
+ import { sdkLogger } from '../utils/Logger';
9
10
 
10
11
  export interface EASConfig {
11
12
  contractAddress: string;
@@ -65,11 +66,7 @@ export class EASHelper {
65
66
  //
66
67
  // Without persistence, attestation replay protection is lost on process restart,
67
68
  // allowing potential double-spend attacks.
68
- console.warn(
69
- '[SECURITY WARNING] EASHelper: Using in-memory attestation tracker. ' +
70
- 'Replay protection will be lost on process restart. ' +
71
- 'For production, provide FileBasedUsedAttestationTracker for persistence.'
72
- );
69
+ sdkLogger.warn('Using in-memory attestation tracker - replay protection lost on restart (use FileBasedUsedAttestationTracker for production)');
73
70
  }
74
71
  this.attestationTracker = attestationTracker || new InMemoryUsedAttestationTracker();
75
72
  }
@@ -10,6 +10,7 @@ import {
10
10
  deliveryProofDataFromProof
11
11
  } from '../types/eip712';
12
12
  import { IReceivedNonceTracker } from '../utils/ReceivedNonceTracker';
13
+ import { sdkLogger } from '../utils/Logger';
13
14
 
14
15
  // Legacy generic ACTP message types moved to types/eip712.ts
15
16
 
@@ -157,7 +158,7 @@ export class MessageSigner {
157
158
  );
158
159
  }
159
160
 
160
- const { type, version, from, to, timestamp, nonce, signature, ...payload } = message;
161
+ const { type, version, from, to, timestamp, nonce, signature: _sig, ...payload } = message;
161
162
 
162
163
  // SECURITY FIX (H-3): Validate nonce format (must be bytes32)
163
164
  if (!nonce || !/^0x[a-fA-F0-9]{64}$/.test(nonce)) {
@@ -175,22 +176,14 @@ export class MessageSigner {
175
176
  const nonceValue = BigInt(nonce);
176
177
  if (nonceValue < 0xFFFFFFFFn) {
177
178
  // Nonce is suspiciously small (< 4 billion = likely sequential)
178
- console.warn(
179
- `[SECURITY WARNING] Nonce ${nonce} appears to be sequential (value < 2^32). ` +
180
- `This makes replay attacks easier. ` +
181
- `Use SecureNonce.generateSecureNonce() for cryptographically secure random nonces.`
182
- );
179
+ sdkLogger.warn('Nonce appears sequential - use SecureNonce.generateSecureNonce()', { nonce });
183
180
  }
184
181
 
185
182
  // Check if nonce has all same digits (e.g., 0x111...111 or 0x000...000)
186
183
  const hexDigits = nonce.slice(2); // Remove '0x'
187
184
  const firstDigit = hexDigits[0];
188
185
  if (hexDigits.split('').every(d => d === firstDigit)) {
189
- console.warn(
190
- `[SECURITY WARNING] Nonce ${nonce} has low entropy (all digits are '${firstDigit}'). ` +
191
- `This is NOT cryptographically secure. ` +
192
- `Use SecureNonce.generateSecureNonce() instead.`
193
- );
186
+ sdkLogger.warn('Nonce has low entropy - use SecureNonce.generateSecureNonce()', { nonce, repeatedDigit: firstDigit });
194
187
  }
195
188
 
196
189
  // Generic ACTPMessage with payload encoding (backward compatible)
@@ -463,10 +456,11 @@ export class MessageSigner {
463
456
  // This prevents cross-chain replay attacks where a message signed for one chain
464
457
  // is replayed on another. For now, we just extract the address but log a warning.
465
458
  if (this.domain && this.domain.chainId !== chainId) {
466
- console.warn(
467
- `[SECURITY WARNING] DID chainId (${chainId}) does not match domain chainId (${this.domain.chainId}). ` +
468
- `This could indicate a cross-chain replay attempt. DID: ${did}`
469
- );
459
+ sdkLogger.warn('DID chainId mismatch - potential cross-chain replay attempt', {
460
+ didChainId: chainId,
461
+ domainChainId: this.domain.chainId,
462
+ did,
463
+ });
470
464
  }
471
465
 
472
466
  return address;
@@ -230,6 +230,7 @@ export class ProofGenerator {
230
230
  throw new Error('Response body is not readable');
231
231
  }
232
232
 
233
+ // eslint-disable-next-line no-constant-condition
233
234
  while (true) {
234
235
  const { done, value } = await reader.read();
235
236
 
@@ -28,6 +28,7 @@ import { ValidationError } from '../errors';
28
28
  import { ServiceHash, DisputeWindow } from '../utils/Helpers';
29
29
  import { IUsedAttestationTracker, createUsedAttestationTracker } from '../utils/UsedAttestationTracker';
30
30
  import { IReceivedNonceTracker, createReceivedNonceTracker } from '../utils/ReceivedNonceTracker';
31
+ import { sdkLogger } from '../utils/Logger';
31
32
 
32
33
  /**
33
34
  * Configuration for BlockchainRuntime
@@ -217,20 +218,17 @@ export class BlockchainRuntime implements IACTPRuntime {
217
218
  );
218
219
  }
219
220
 
220
- console.info(
221
- `BlockchainRuntime: Connected to ${this.networkConfig.name} (chainId: ${connectedChainId})`
222
- );
221
+ sdkLogger.info(`Connected to ${this.networkConfig.name}`, { chainId: connectedChainId });
223
222
  } catch (error) {
224
223
  if (error instanceof Error && error.message.includes('Network mismatch')) {
225
224
  throw error; // Re-throw our validation error
226
225
  }
227
226
  // For other errors (e.g., network issues), log warning but continue
228
227
  // This allows initialization to proceed even if network check fails temporarily
229
- console.warn(
230
- `BlockchainRuntime: Could not verify network chainId. ` +
231
- `Error: ${error instanceof Error ? error.message : String(error)}. ` +
232
- `Proceeding with expected chainId ${this.networkConfig.chainId}.`
233
- );
228
+ sdkLogger.warn('Could not verify network chainId, proceeding with expected value', {
229
+ error: error instanceof Error ? error.message : String(error),
230
+ expectedChainId: this.networkConfig.chainId,
231
+ });
234
232
  }
235
233
 
236
234
  // SECURITY FIX (H-4): Use factory pattern to guarantee domain initialization
@@ -254,10 +252,7 @@ export class BlockchainRuntime implements IACTPRuntime {
254
252
  this.attestationTracker
255
253
  );
256
254
  } else if (this.requireAttestation) {
257
- console.warn(
258
- '[SECURITY WARNING] BlockchainRuntime: requireAttestation is true but no EAS config provided. ' +
259
- 'Attestation verification will fail. Please provide easConfig in BlockchainRuntimeConfig.'
260
- );
255
+ sdkLogger.warn('requireAttestation is true but no EAS config provided - attestation verification will fail');
261
256
  }
262
257
  }
263
258
 
@@ -319,11 +314,12 @@ export class BlockchainRuntime implements IACTPRuntime {
319
314
  this.reconnectAttempts = attempt + 1;
320
315
  const delay = this.baseReconnectDelay * Math.pow(2, attempt);
321
316
 
322
- console.warn(
323
- `Provider connection lost. Attempting reconnection ${attempt + 1}/${this.maxReconnectAttempts} ` +
324
- `after ${delay}ms delay... ` +
325
- `(Error: ${error instanceof Error ? error.message : String(error)})`
326
- );
317
+ sdkLogger.warn('Provider connection lost, attempting reconnection', {
318
+ attempt: attempt + 1,
319
+ maxAttempts: this.maxReconnectAttempts,
320
+ delayMs: delay,
321
+ error: error instanceof Error ? error.message : String(error),
322
+ });
327
323
 
328
324
  // Wait before next attempt
329
325
  await new Promise(resolve => setTimeout(resolve, delay));
@@ -543,7 +539,11 @@ export class BlockchainRuntime implements IACTPRuntime {
543
539
  escrowId: tx.escrowId,
544
540
  createdAt: Number(tx.createdAt),
545
541
  updatedAt: Number(tx.updatedAt),
546
- completedAt: 0, // V2: Track via DELIVERED event timestamp
542
+ // LIMITATION (V2): completedAt requires event indexing to fetch DELIVERED event timestamp.
543
+ // Currently 0, which means SDK-side dispute window check in releaseEscrow() is bypassed.
544
+ // On-chain contract still enforces dispute window correctly via _validateSettlementConditions().
545
+ // V2 will implement EventMonitor to track this properly.
546
+ completedAt: 0,
547
547
  serviceDescription: '', // V2: Decode from on-chain serviceHash
548
548
  deliveryProof: '', // V2: Fetch from EAS attestation
549
549
  events: [], // V2: Populate via EventMonitor.getTransactionEvents()
@@ -562,9 +562,7 @@ export class BlockchainRuntime implements IACTPRuntime {
562
562
  async getAllTransactions(): Promise<MockTransaction[]> {
563
563
  // V2: Implement event-based transaction indexing via EventMonitor
564
564
  // For now, return empty array as this requires off-chain indexer
565
- console.warn(
566
- 'getAllTransactions() not fully implemented for BlockchainRuntime. Use EventMonitor for event-based queries.'
567
- );
565
+ sdkLogger.warn('getAllTransactions() not implemented - use EventMonitor for event-based queries');
568
566
  return [];
569
567
  }
570
568
 
@@ -604,10 +602,7 @@ export class BlockchainRuntime implements IACTPRuntime {
604
602
  if (legacyMatch) {
605
603
  // Legacy SDK format - extract txId
606
604
  txId = legacyMatch[1];
607
- console.warn(
608
- `BlockchainRuntime.releaseEscrow: Using legacy escrowId format. ` +
609
- `Please update to use txId directly as escrowId.`
610
- );
605
+ sdkLogger.warn('Using legacy escrowId format - please update to use txId directly as escrowId');
611
606
  } else {
612
607
  // Standard: escrowId = txId
613
608
  txId = escrowId;
@@ -658,9 +653,7 @@ export class BlockchainRuntime implements IACTPRuntime {
658
653
  // Verify attestation is valid for this transaction
659
654
  try {
660
655
  await this.easHelper.verifyAndRecordForRelease(txId, attestationUID);
661
- console.info(
662
- `BlockchainRuntime.releaseEscrow: Attestation ${attestationUID} verified for transaction ${txId}.`
663
- );
656
+ sdkLogger.info('Attestation verified for release', { txId, attestationUID });
664
657
  } catch (error) {
665
658
  throw new Error(
666
659
  `Cannot release escrow: attestation verification failed for transaction ${txId}. ` +
@@ -671,21 +664,16 @@ export class BlockchainRuntime implements IACTPRuntime {
671
664
  // Even if not required, verify attestation if provided (best effort)
672
665
  try {
673
666
  await this.easHelper.verifyAndRecordForRelease(txId, attestationUID);
674
- console.info(
675
- `BlockchainRuntime.releaseEscrow: Attestation ${attestationUID} verified (optional) for transaction ${txId}.`
676
- );
667
+ sdkLogger.info('Attestation verified (optional)', { txId, attestationUID });
677
668
  } catch (error) {
678
- console.warn(
679
- `BlockchainRuntime.releaseEscrow: Attestation verification failed but not required. ` +
680
- `Proceeding with release. Error: ${error instanceof Error ? error.message : String(error)}`
681
- );
669
+ sdkLogger.warn('Attestation verification failed but not required, proceeding', {
670
+ txId,
671
+ error: error instanceof Error ? error.message : String(error),
672
+ });
682
673
  }
683
674
  } else {
684
675
  // No attestation verification
685
- console.info(
686
- `BlockchainRuntime.releaseEscrow: Settling transaction ${txId}. ` +
687
- `Note: Set requireAttestation=true and provide easConfig for additional security.`
688
- );
676
+ sdkLogger.info('Settling transaction without attestation verification', { txId });
689
677
  }
690
678
 
691
679
  // SECURITY FIX (SETTLEMENT-FLOW): Use transitionState(SETTLED) instead of releaseEscrow()
@@ -731,7 +719,7 @@ export class BlockchainRuntime implements IACTPRuntime {
731
719
  return '0';
732
720
  } catch (error) {
733
721
  // If query fails, return 0
734
- console.warn('BlockchainRuntime.getEscrowBalance: Query failed', error);
722
+ sdkLogger.warn('getEscrowBalance query failed', { error: error instanceof Error ? error.message : String(error) });
735
723
  return '0';
736
724
  }
737
725
  }
@@ -773,6 +761,16 @@ export class BlockchainRuntime implements IACTPRuntime {
773
761
  return this.networkConfig;
774
762
  }
775
763
 
764
+ /**
765
+ * Maximum transaction amount in USDC (human-readable).
766
+ *
767
+ * SECURITY: Limits exposure on unaudited mainnet contracts.
768
+ * Returns undefined if no limit (testnet).
769
+ */
770
+ get maxTransactionAmount(): number | undefined {
771
+ return this.networkConfig.maxTransactionAmount;
772
+ }
773
+
776
774
  /**
777
775
  * Get ACTPKernel instance (for advanced usage)
778
776
  */
@@ -869,11 +867,7 @@ export class BlockchainRuntime implements IACTPRuntime {
869
867
 
870
868
  // SECURITY FIX (CRITICAL): If it's a raw string (legacy format), hash it
871
869
  // This ensures on-chain compatibility with the contract's bytes32 expectation
872
- console.warn(
873
- 'BlockchainRuntime: serviceDescription is not a valid bytes32 hash. ' +
874
- 'Hashing it now. For best practice, use ServiceHash.hash() before calling createTransaction.'
875
- );
876
-
870
+ sdkLogger.warn('serviceDescription is not a valid bytes32 hash - hashing now (use ServiceHash.hash() for best practice)');
877
871
  return keccak256(toUtf8Bytes(serviceDescription));
878
872
  }
879
873
 
@@ -892,7 +886,7 @@ export class BlockchainRuntime implements IACTPRuntime {
892
886
  * @param params - Transaction parameters
893
887
  * @returns Estimated gas limit and cost in wei
894
888
  */
895
- async estimateCreateTransactionGas(params: CreateTransactionParams): Promise<{
889
+ async estimateCreateTransactionGas(_params: CreateTransactionParams): Promise<{
896
890
  gasLimit: bigint;
897
891
  gasCostWei: bigint;
898
892
  gasCostGwei: string;
@@ -921,7 +915,7 @@ export class BlockchainRuntime implements IACTPRuntime {
921
915
  * @param txId - Transaction ID
922
916
  * @returns Estimated gas limit and cost
923
917
  */
924
- async estimateLinkEscrowGas(txId: string): Promise<{
918
+ async estimateLinkEscrowGas(_txId: string): Promise<{
925
919
  gasLimit: bigint;
926
920
  gasCostWei: bigint;
927
921
  gasCostGwei: string;
@@ -949,7 +943,7 @@ export class BlockchainRuntime implements IACTPRuntime {
949
943
  * @param newState - Target state
950
944
  * @returns Estimated gas limit and cost
951
945
  */
952
- async estimateTransitionGas(txId: string, newState: string): Promise<{
946
+ async estimateTransitionGas(_txId: string, _newState: string): Promise<{
953
947
  gasLimit: bigint;
954
948
  gasCostWei: bigint;
955
949
  gasCostGwei: string;
@@ -199,6 +199,22 @@ export interface IACTPRuntime {
199
199
  /** Get current timestamp in seconds */
200
200
  now(): number;
201
201
  };
202
+
203
+ /**
204
+ * Maximum transaction amount in USDC (human-readable, e.g., 100 = $100).
205
+ *
206
+ * SECURITY: Limits exposure on unaudited contracts.
207
+ * Returns undefined if no limit is enforced (testnet/mock mode).
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * const limit = runtime.maxTransactionAmount;
212
+ * if (limit && amount > limit) {
213
+ * throw new Error(`Amount exceeds limit of $${limit}`);
214
+ * }
215
+ * ```
216
+ */
217
+ maxTransactionAmount?: number;
202
218
  }
203
219
 
204
220
  /**
@@ -25,8 +25,6 @@ import {
25
25
  MockEscrow,
26
26
  MockEvent,
27
27
  TransactionState,
28
- TransactionStateValue,
29
- MOCK_STATE_DEFAULTS,
30
28
  } from './types/MockState';
31
29
  import { IACTPRuntime, CreateTransactionParams } from './IACTPRuntime';
32
30
 
@@ -201,23 +199,27 @@ export class DisputeWindowActiveError extends Error {
201
199
  /**
202
200
  * Valid state transitions for the ACTP 8-state machine.
203
201
  *
202
+ * AUDIT FIX (2026-02): Synced with on-chain ACTPKernel contract.
203
+ *
204
204
  * State machine:
205
205
  * - INITIATED -> QUOTED (optional), COMMITTED, CANCELLED
206
206
  * - QUOTED -> COMMITTED, CANCELLED
207
- * - COMMITTED -> IN_PROGRESS (optional), DELIVERED, CANCELLED
207
+ * - COMMITTED -> IN_PROGRESS, CANCELLED (cannot skip to DELIVERED)
208
208
  * - IN_PROGRESS -> DELIVERED, CANCELLED
209
209
  * - DELIVERED -> SETTLED, DISPUTED
210
- * - DISPUTED -> SETTLED
210
+ * - DISPUTED -> SETTLED, CANCELLED (admin/pauser emergency)
211
211
  * - SETTLED (terminal)
212
212
  * - CANCELLED (terminal)
213
213
  */
214
214
  const VALID_TRANSITIONS: Record<TransactionState, TransactionState[]> = {
215
215
  INITIATED: ['QUOTED', 'COMMITTED', 'CANCELLED'],
216
216
  QUOTED: ['COMMITTED', 'CANCELLED'],
217
- COMMITTED: ['IN_PROGRESS', 'DELIVERED', 'CANCELLED'],
217
+ // AUDIT FIX: Cannot skip IN_PROGRESS - must transition through it
218
+ COMMITTED: ['IN_PROGRESS', 'CANCELLED'],
218
219
  IN_PROGRESS: ['DELIVERED', 'CANCELLED'],
219
220
  DELIVERED: ['SETTLED', 'DISPUTED'],
220
- DISPUTED: ['SETTLED'],
221
+ // AUDIT FIX: DISPUTED can also be CANCELLED by admin/pauser
222
+ DISPUTED: ['SETTLED', 'CANCELLED'],
221
223
  SETTLED: [], // Terminal state
222
224
  CANCELLED: [], // Terminal state
223
225
  };
@@ -339,6 +341,14 @@ export class MockRuntime implements IACTPRuntime {
339
341
  };
340
342
  }
341
343
 
344
+ /**
345
+ * Maximum transaction amount - no limit in mock mode.
346
+ *
347
+ * Mock mode is for testing, so we don't enforce limits.
348
+ * Real blockchain runtimes may have limits for security.
349
+ */
350
+ readonly maxTransactionAmount: number | undefined = undefined;
351
+
342
352
  /**
343
353
  * Load events from persisted state file.
344
354
  *
@@ -14,10 +14,13 @@
14
14
  * ACTP Transaction State enum matching the smart contract states.
15
15
  *
16
16
  * State machine:
17
- * - INITIATED (0) -> QUOTED (1, optional) -> COMMITTED (2) -> IN_PROGRESS (3, optional)
17
+ * - INITIATED (0) -> QUOTED (1, optional) -> COMMITTED (2) -> IN_PROGRESS (3, mandatory)
18
18
  * -> DELIVERED (4) -> SETTLED (5)
19
- * - Dispute path: DELIVERED -> DISPUTED (6) -> SETTLED
19
+ * - Dispute path: DELIVERED -> DISPUTED (6) -> SETTLED or CANCELLED (admin)
20
20
  * - Cancel path: Any pre-DELIVERED state -> CANCELLED (7)
21
+ *
22
+ * AUDIT FIX (2026-02): IN_PROGRESS is now mandatory per contract. Direct
23
+ * COMMITTED -> DELIVERED transitions are rejected on-chain.
21
24
  */
22
25
  export type TransactionState =
23
26
  | 'INITIATED'