@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,378 +0,0 @@
1
- import { AbiCoder, Signer } from 'ethers';
2
- import { EAS } from '@ethereum-attestation-service/eas-sdk';
3
- import { DeliveryProof } from '../types';
4
- import { deliveryProofDataFromProof } from '../types/eip712';
5
- import {
6
- IUsedAttestationTracker,
7
- InMemoryUsedAttestationTracker
8
- } from '../utils/UsedAttestationTracker';
9
- import { sdkLogger } from '../utils/Logger';
10
-
11
- export interface EASConfig {
12
- contractAddress: string;
13
- deliveryProofSchemaId: string;
14
- }
15
-
16
- export interface AttestationResponse {
17
- uid: string;
18
- transactionHash: string;
19
- }
20
-
21
- /**
22
- * EASHelper - utility wrapper for Ethereum Attestation Service interactions
23
- */
24
- export class EASHelper {
25
- private readonly eas: EAS;
26
- private readonly attestationTracker: IUsedAttestationTracker;
27
-
28
- /**
29
- * Create EASHelper instance
30
- *
31
- * @param signer - Ethers signer for signing attestations
32
- * @param config - EAS configuration
33
- * @param attestationTracker - Optional tracker for replay attack prevention (C-1 fix)
34
- *
35
- * SECURITY FIX (NEW-M-2): Validates schema UID format in constructor
36
- */
37
- constructor(
38
- signer: Signer,
39
- private readonly config: EASConfig,
40
- attestationTracker?: IUsedAttestationTracker
41
- ) {
42
- // SECURITY FIX (NEW-M-2): Validate schema UID format
43
- if (!config.deliveryProofSchemaId || !/^0x[a-fA-F0-9]{64}$/.test(config.deliveryProofSchemaId)) {
44
- throw new Error(
45
- `Invalid deliveryProofSchemaId: must be bytes32 hex string (0x...). ` +
46
- `Got: ${config.deliveryProofSchemaId}`
47
- );
48
- }
49
-
50
- if (config.deliveryProofSchemaId === '0x0000000000000000000000000000000000000000000000000000000000000000') {
51
- throw new Error('deliveryProofSchemaId cannot be zero bytes32');
52
- }
53
-
54
- // Use official EAS SDK wrapper to ensure ABI correctness across deployments
55
- // (includes fields like revocationTime that are required for security checks).
56
- this.eas = new EAS(config.contractAddress);
57
- this.eas.connect(signer as any);
58
- // SECURITY FIX (C-1): Use provided tracker or create new in-memory one
59
- if (!attestationTracker) {
60
- // SECURITY WARNING (HIGH-5): In-memory tracker does not persist across restarts!
61
- // For production use, provide a FileBasedUsedAttestationTracker with a stateDirectory:
62
- //
63
- // import { FileBasedUsedAttestationTracker } from '../utils/UsedAttestationTracker';
64
- // const tracker = new FileBasedUsedAttestationTracker('/path/to/state');
65
- // const easHelper = new EASHelper(signer, config, tracker);
66
- //
67
- // Without persistence, attestation replay protection is lost on process restart,
68
- // allowing potential double-spend attacks.
69
- sdkLogger.warn('Using in-memory attestation tracker - replay protection lost on restart (use FileBasedUsedAttestationTracker for production)');
70
- }
71
- this.attestationTracker = attestationTracker || new InMemoryUsedAttestationTracker();
72
- }
73
-
74
- /**
75
- * Get the attestation tracker for external use
76
- */
77
- getAttestationTracker(): IUsedAttestationTracker {
78
- return this.attestationTracker;
79
- }
80
-
81
- /**
82
- * Create an attestation for a delivery proof. Returns the attestation UID and transaction hash.
83
- */
84
- async attestDeliveryProof(
85
- proof: DeliveryProof,
86
- recipient: string,
87
- options?: { expirationTime?: number; revocable?: boolean }
88
- ): Promise<AttestationResponse> {
89
- const { expirationTime = 0, revocable = true } = options || {};
90
- const proofData = deliveryProofDataFromProof(proof);
91
-
92
- const abiCoder = AbiCoder.defaultAbiCoder();
93
- const encodedData = abiCoder.encode(
94
- ['bytes32', 'bytes32', 'uint256', 'string', 'uint256', 'string'],
95
- [
96
- proofData.txId,
97
- proofData.contentHash,
98
- proofData.timestamp,
99
- proofData.deliveryUrl || '',
100
- proofData.size,
101
- proofData.mimeType
102
- ]
103
- );
104
-
105
- const tx = await this.eas.attest({
106
- schema: this.config.deliveryProofSchemaId,
107
- data: {
108
- recipient,
109
- expirationTime: BigInt(expirationTime),
110
- revocable,
111
- refUID: proof.txId,
112
- data: encodedData,
113
- // For Base EAS, value is typically 0. Keep explicit to avoid ambiguity.
114
- value: 0n
115
- }
116
- });
117
-
118
- const uid = await tx.wait();
119
- if (!uid || !/^0x[a-fA-F0-9]{64}$/.test(uid)) {
120
- throw new Error(`Failed to obtain attestation UID from EAS transaction. Got: ${String(uid)}`);
121
- }
122
- if (uid === '0x0000000000000000000000000000000000000000000000000000000000000000') {
123
- throw new Error('EAS returned zero UID for attestation (unexpected).');
124
- }
125
-
126
- return {
127
- uid,
128
- transactionHash: tx.tx.hash
129
- };
130
- }
131
-
132
- /**
133
- * Revoke a previously issued attestation by UID.
134
- */
135
- async revokeAttestation(uid: string): Promise<string> {
136
- if (!uid || !/^0x[a-fA-F0-9]{64}$/.test(uid)) {
137
- throw new Error(`Invalid attestation UID format: ${uid}`);
138
- }
139
- const tx = await this.eas.revoke({
140
- schema: this.config.deliveryProofSchemaId,
141
- data: { uid }
142
- });
143
- await tx.wait();
144
- return tx.tx.hash;
145
- }
146
-
147
- /**
148
- * Fetch attestation data from the EAS contract.
149
- */
150
- async getAttestation(uid: string) {
151
- if (!uid || !/^0x[a-fA-F0-9]{64}$/.test(uid)) {
152
- throw new Error(`Invalid attestation UID format: ${uid}`);
153
- }
154
- return await this.eas.getAttestation(uid);
155
- }
156
-
157
- /**
158
- * Verify that a delivery attestation belongs to the specified transaction.
159
- *
160
- * SECURITY: ACTPKernel V1 accepts any bytes32 as attestationUID without validation.
161
- * This means a malicious provider could submit attestation from Transaction A
162
- * for Transaction B. This method provides SDK-side protection by verifying:
163
- *
164
- * 1. Attestation exists and is not revoked
165
- * 2. Attestation uses the canonical delivery schema UID
166
- * 3. Attestation's txId matches the expected transaction ID
167
- * 4. Attestation has not expired
168
- *
169
- * @param txId - Expected transaction ID (bytes32)
170
- * @param attestationUID - Attestation UID to verify (bytes32)
171
- * @returns true if attestation is valid for this transaction, false otherwise
172
- * @throws Error if attestation is revoked, expired, schema mismatch, or txId mismatch
173
- */
174
- async verifyDeliveryAttestation(
175
- txId: string,
176
- attestationUID: string
177
- ): Promise<boolean> {
178
- if (!txId || !/^0x[a-fA-F0-9]{64}$/.test(txId)) {
179
- throw new Error(`Invalid txId format (expected bytes32): ${txId}`);
180
- }
181
- if (!attestationUID || !/^0x[a-fA-F0-9]{64}$/.test(attestationUID)) {
182
- throw new Error(`Invalid attestationUID format (expected bytes32): ${attestationUID}`);
183
- }
184
-
185
- // 1. Fetch attestation from EAS contract
186
- const attestation = await this.eas.getAttestation(attestationUID);
187
-
188
- // 2. Check if attestation exists (uid should match)
189
- if (attestation.uid !== attestationUID) {
190
- throw new Error(`Attestation not found: ${attestationUID}`);
191
- }
192
-
193
- // 3. Check schema UID matches canonical delivery schema (B2 blocker fix)
194
- // This prevents accepting attestations from unrelated EAS schemas
195
- if (attestation.schema !== this.config.deliveryProofSchemaId) {
196
- throw new Error(
197
- `Schema UID mismatch: expected canonical delivery schema ${this.config.deliveryProofSchemaId}, ` +
198
- `got ${attestation.schema}. Attestation may be from a different schema!`
199
- );
200
- }
201
-
202
- // 4. Check revocation - EAS uses revocationTime field (not revoked boolean)
203
- // revocationTime = 0 means not revoked
204
- // revocationTime > 0 means revoked at that timestamp
205
- const revocationTime = BigInt((attestation as any).revocationTime ?? 0);
206
- const isRevoked = revocationTime > 0n;
207
-
208
- if (isRevoked) {
209
- throw new Error(
210
- `Attestation has been revoked: ${attestationUID} (revoked at timestamp ${revocationTime})`
211
- );
212
- }
213
-
214
- // 5. Check expiration
215
- // expirationTime = 0 means no expiration
216
- // expirationTime > 0 means expires at that timestamp
217
- const expirationTime = BigInt((attestation as any).expirationTime ?? 0);
218
- if (expirationTime > 0n) {
219
- const now = Math.floor(Date.now() / 1000);
220
- if (Number(expirationTime) < now) {
221
- throw new Error(
222
- `Attestation has expired: ${attestationUID} (expired at ${expirationTime})`
223
- );
224
- }
225
- }
226
-
227
- // 6. Decode attestation data to extract txId
228
- let attestedTxId: string = '';
229
-
230
- try {
231
- const abiCoder = AbiCoder.defaultAbiCoder();
232
- const rawData: string = (attestation as any).data;
233
-
234
- // Prefer AIP-6 decoding (current DX Playground schema)
235
- // - Official AIP-6: bytes32 txId,string resultCID,bytes32 resultHash,uint256 deliveredAt
236
- // - Test schema: + uint256 testTimestamp
237
- try {
238
- const decoded = abiCoder.decode(
239
- ['bytes32', 'string', 'bytes32', 'uint256', 'uint256'],
240
- rawData
241
- );
242
- attestedTxId = decoded[0];
243
- const resultCID: string = decoded[1];
244
- const resultHash: string = decoded[2];
245
- const deliveredAt: bigint = decoded[3];
246
-
247
- if (!attestedTxId || !/^0x[a-fA-F0-9]{64}$/.test(attestedTxId)) {
248
- throw new Error(`Decoded txId is not valid bytes32: ${attestedTxId}`);
249
- }
250
- if (typeof resultCID !== 'string' || resultCID.length === 0 || resultCID.length > 2048) {
251
- throw new Error(`Decoded resultCID invalid length: ${resultCID?.length}`);
252
- }
253
- if (!resultHash || !/^0x[a-fA-F0-9]{64}$/.test(resultHash)) {
254
- throw new Error(`Decoded resultHash is not valid bytes32: ${resultHash}`);
255
- }
256
- const now = BigInt(Math.floor(Date.now() / 1000));
257
- if (deliveredAt > now + 86400n) {
258
- throw new Error(`Decoded deliveredAt is in far future: ${deliveredAt.toString()}`);
259
- }
260
- } catch (_e) {
261
- // Fallback: official AIP-6 schema without testTimestamp
262
- try {
263
- const decoded = abiCoder.decode(
264
- ['bytes32', 'string', 'bytes32', 'uint256'],
265
- rawData
266
- );
267
- attestedTxId = decoded[0];
268
- const resultCID: string = decoded[1];
269
- const resultHash: string = decoded[2];
270
- const deliveredAt: bigint = decoded[3];
271
-
272
- if (!attestedTxId || !/^0x[a-fA-F0-9]{64}$/.test(attestedTxId)) {
273
- throw new Error(`Decoded txId is not valid bytes32: ${attestedTxId}`);
274
- }
275
- if (typeof resultCID !== 'string' || resultCID.length === 0 || resultCID.length > 2048) {
276
- throw new Error(`Decoded resultCID invalid length: ${resultCID?.length}`);
277
- }
278
- if (!resultHash || !/^0x[a-fA-F0-9]{64}$/.test(resultHash)) {
279
- throw new Error(`Decoded resultHash is not valid bytes32: ${resultHash}`);
280
- }
281
- const now = BigInt(Math.floor(Date.now() / 1000));
282
- if (deliveredAt > now + 86400n) {
283
- throw new Error(`Decoded deliveredAt is in far future: ${deliveredAt.toString()}`);
284
- }
285
- } catch (__e) {
286
- // Final fallback: legacy AIP-4 schema
287
- // Schema: bytes32 txId, bytes32 contentHash, uint256 timestamp, string deliveryUrl, uint256 size, string mimeType
288
- const decoded = abiCoder.decode(
289
- ['bytes32', 'bytes32', 'uint256', 'string', 'uint256', 'string'],
290
- rawData
291
- );
292
- attestedTxId = decoded[0];
293
- const contentHash: string = decoded[1];
294
- const timestamp: bigint = decoded[2];
295
- const deliveryUrl: string = decoded[3];
296
- const size: bigint = decoded[4];
297
- const mimeType: string = decoded[5];
298
-
299
- if (!attestedTxId || !/^0x[a-fA-F0-9]{64}$/.test(attestedTxId)) {
300
- throw new Error(`Decoded txId is not valid bytes32: ${attestedTxId}`);
301
- }
302
- if (!contentHash || !/^0x[a-fA-F0-9]{64}$/.test(contentHash)) {
303
- throw new Error(`Decoded contentHash is not valid bytes32: ${contentHash}`);
304
- }
305
- const now = BigInt(Math.floor(Date.now() / 1000));
306
- if (timestamp > now + 86400n) {
307
- throw new Error(`Decoded timestamp is in far future: ${timestamp.toString()}`);
308
- }
309
- if (typeof deliveryUrl !== 'string' || deliveryUrl.length > 2048) {
310
- throw new Error('Decoded deliveryUrl too long');
311
- }
312
- if (size < 0n) {
313
- throw new Error(`Decoded size is negative: ${size}`);
314
- }
315
- if (typeof mimeType !== 'string' || mimeType.length > 256) {
316
- throw new Error('Decoded mimeType too long');
317
- }
318
- }
319
- }
320
-
321
- } catch (error: any) {
322
- throw new Error(
323
- `Attestation data format mismatch: cannot decode attestation ${attestationUID}. ` +
324
- `Expected AIP-6 (preferred) or AIP-4 (legacy) delivery proof schema format. ` +
325
- `Original error: ${error.message}`
326
- );
327
- }
328
-
329
- // 7. Verify attestation txId matches expected transaction ID
330
- if (attestedTxId.toLowerCase() !== txId.toLowerCase()) {
331
- throw new Error(
332
- `Attestation txId mismatch: expected ${txId}, got ${attestedTxId}. ` +
333
- `Provider may be attempting to use attestation from different transaction!`
334
- );
335
- }
336
-
337
- // SECURITY FIX (C-1): Check if attestation has been used for a different transaction
338
- if (!this.attestationTracker.isValidForTransaction(attestationUID, txId)) {
339
- const usedFor = this.attestationTracker.getUsageForAttestation(attestationUID);
340
- throw new Error(
341
- `Attestation replay attack detected: attestation ${attestationUID} ` +
342
- `was already used for transaction ${usedFor}. ` +
343
- `Cannot reuse for transaction ${txId}.`
344
- );
345
- }
346
-
347
- // Record this attestation as used for this transaction
348
- const recorded = await this.attestationTracker.recordUsage(attestationUID, txId);
349
- if (!recorded) {
350
- const usedFor = this.attestationTracker.getUsageForAttestation(attestationUID);
351
- throw new Error(
352
- `Attestation replay attack detected: attestation ${attestationUID} ` +
353
- `was already used for transaction ${usedFor}. Cannot reuse for ${txId}.`
354
- );
355
- }
356
-
357
- // All checks passed
358
- return true;
359
- }
360
-
361
- /**
362
- * Verify attestation for escrow release with mandatory replay protection
363
- *
364
- * SECURITY FIX (C-4): This method MUST be called before releaseEscrow()
365
- * to prevent attestation replay attacks.
366
- *
367
- * @param txId - Expected transaction ID (bytes32)
368
- * @param attestationUID - Attestation UID to verify (bytes32)
369
- * @throws Error if verification fails or replay attack detected
370
- */
371
- async verifyAndRecordForRelease(
372
- txId: string,
373
- attestationUID: string
374
- ): Promise<void> {
375
- // Verify the attestation
376
- await this.verifyDeliveryAttestation(txId, attestationUID);
377
- }
378
- }
@@ -1,255 +0,0 @@
1
- import { Contract, Signer } from 'ethers';
2
- import EscrowVaultABI from '../abi/EscrowVault.json';
3
- import ERC20ABI from '../abi/ERC20.json';
4
- import { Escrow } from '../types';
5
- import { TransactionRevertedError, ValidationError } from '../errors';
6
- import {
7
- validateAddress,
8
- validateAmount,
9
- validateTxId
10
- } from '../utils/validation';
11
-
12
- /**
13
- * Gas options for transactions
14
- */
15
- interface GasOptions {
16
- maxFeePerGas?: bigint;
17
- maxPriorityFeePerGas?: bigint;
18
- }
19
-
20
- /**
21
- * EscrowVault - Escrow contract wrapper
22
- *
23
- * IMPORTANT:
24
- * - Escrow creation happens atomically inside `ACTPKernel.linkEscrow()`.
25
- * - Payout/refund functions are `onlyKernel` on-chain and MUST NOT be called by users.
26
- *
27
- * This module provides:
28
- * - Helper methods for USDC approvals (requester → EscrowVault allowance)
29
- * - Read-only access to escrow state (`escrows()` / `remaining()`)
30
- *
31
- * Workflow (per AIP-3):
32
- * 1. Consumer approves USDC to EscrowVault address (use approveToken)
33
- * 2. Consumer calls ACTPKernel.linkEscrow(txId, escrowVault, escrowId)
34
- * 3. Kernel internally calls IEscrowValidator.createEscrow(escrowId, requester, provider, amount)
35
- * 4. Escrow pulls USDC from requester
36
- *
37
- * Reference: AIP-3 §3.2 (Escrow Linking Workflow), lines 258-336
38
- */
39
- export class EscrowVault {
40
- private contract: Contract;
41
- private readonly gasSettings?: GasOptions;
42
-
43
- constructor(
44
- private readonly address: string,
45
- private readonly signer: Signer,
46
- gasSettings?: GasOptions
47
- ) {
48
- this.contract = new Contract(address, EscrowVaultABI, signer);
49
- this.gasSettings = gasSettings;
50
- }
51
-
52
- /**
53
- * Get gas buffer multiplier based on operation complexity
54
- * V6 Security Enhancement: Operation-specific gas buffers
55
- * Reference: SDK_SECURITY_ANALYSIS-Ultra-Think.md Lines 326-337
56
- */
57
- private getGasBufferMultiplier(operation: string): number {
58
- const buffers: Record<string, number> = {
59
- 'approveToken': 1.20 // 20% - Standard ERC20 approval
60
- };
61
-
62
- return buffers[operation] || 1.20; // Default 20% for unknown operations
63
- }
64
-
65
- /**
66
- * Build transaction options with gas settings and estimated gas
67
- * V6 Enhancement: Dynamic buffer based on operation type
68
- *
69
- * SECURITY FIX (NEW-C-1): Gas estimation manipulation attack protection
70
- * - Enforces minimum gas floor regardless of estimate
71
- * - Uses safe BigInt arithmetic with overflow detection
72
- */
73
- private buildTxOptions(estimatedGas: bigint, operation: string = 'default'): any {
74
- // SECURITY FIX (NEW-C-1): Minimum gas floor to prevent manipulation
75
- // Malicious contracts could return artificially low gas estimates
76
- const MIN_GAS_FLOOR = 100000n;
77
- const safeEstimate = estimatedGas > MIN_GAS_FLOOR ? estimatedGas : MIN_GAS_FLOOR;
78
-
79
- const bufferMultiplier = this.getGasBufferMultiplier(operation);
80
-
81
- // SECURITY FIX (NEW-H-1): Safe BigInt arithmetic with overflow check
82
- // Use 10000 denominator to avoid floating point precision issues
83
- const bufferNumerator = BigInt(Math.floor(bufferMultiplier * 10000));
84
- const bufferDenominator = 10000n;
85
- const gasLimit = (safeEstimate * bufferNumerator) / bufferDenominator;
86
-
87
- // Overflow detection: result should always be >= original estimate
88
- if (gasLimit < safeEstimate) {
89
- throw new Error(
90
- `Gas calculation overflow detected for operation ${operation}. ` +
91
- `Estimate: ${safeEstimate}, Buffer: ${bufferMultiplier}x, Result: ${gasLimit}`
92
- );
93
- }
94
-
95
- const options: any = {
96
- gasLimit
97
- };
98
-
99
- if (this.gasSettings?.maxFeePerGas) {
100
- options.maxFeePerGas = this.gasSettings.maxFeePerGas;
101
- }
102
- if (this.gasSettings?.maxPriorityFeePerGas) {
103
- options.maxPriorityFeePerGas = this.gasSettings.maxPriorityFeePerGas;
104
- }
105
-
106
- return options;
107
- }
108
-
109
- /**
110
- * Get escrow vault address
111
- */
112
- getAddress(): string {
113
- return this.address;
114
- }
115
-
116
- /**
117
- * Get the underlying ethers Contract instance.
118
- *
119
- * SECURITY FIX (C-3): Provides public access to contract for EventMonitor
120
- * instead of accessing private field via bracket notation.
121
- *
122
- * @returns ethers Contract instance
123
- */
124
- getContract(): Contract {
125
- return this.contract;
126
- }
127
-
128
- /**
129
- * Approve USDC token for escrow creation
130
- *
131
- * IMPORTANT: Call this BEFORE ACTPKernel.linkEscrow()
132
- * The consumer must approve EscrowVault to pull USDC when linkEscrow() is called
133
- *
134
- * @param tokenAddress - USDC contract address
135
- * @param amount - Amount to approve (in USDC wei, 6 decimals)
136
- * @throws {ValidationError} If inputs are invalid
137
- * @throws {TransactionRevertedError} If approval fails
138
- *
139
- * @example
140
- * ```typescript
141
- * // Approve 100 USDC for escrow
142
- * const amount = ethers.parseUnits('100', 6);
143
- * await client.escrow.approveToken(BASE_SEPOLIA.contracts.usdc, amount);
144
- *
145
- * // Now call linkEscrow via Kernel
146
- * const escrowId = ethers.id(`escrow-${Date.now()}`);
147
- * await client.kernel.linkEscrow(txId, escrowVault, escrowId);
148
- * ```
149
- */
150
- async approveToken(tokenAddress: string, amount: bigint): Promise<void> {
151
- validateAddress(tokenAddress, 'tokenAddress');
152
- validateAmount(amount, 'amount');
153
-
154
- const tokenContract = new Contract(tokenAddress, ERC20ABI, this.signer);
155
-
156
- try {
157
- // Check current allowance
158
- const currentAllowance = await tokenContract.allowance(
159
- await this.signer.getAddress(),
160
- this.address
161
- );
162
-
163
- // Only approve if needed
164
- if (currentAllowance < amount) {
165
- const approveFunc = tokenContract.getFunction('approve');
166
-
167
- // USDC-compatible approval pattern:
168
- // If any residual allowance exists, reset to zero first
169
- if (currentAllowance > 0n) {
170
- const resetGas = await approveFunc.estimateGas(this.address, 0);
171
- const resetTx = await approveFunc(this.address, 0, this.buildTxOptions(resetGas, 'approveToken'));
172
- await resetTx.wait();
173
- }
174
-
175
- // Now set the new allowance
176
- const approveGas = await approveFunc.estimateGas(this.address, amount);
177
- const approveTx = await approveFunc(this.address, amount, this.buildTxOptions(approveGas, 'approveToken'));
178
- await approveTx.wait();
179
- }
180
- } catch (error: any) {
181
- throw new TransactionRevertedError(
182
- error.transactionHash,
183
- `Token approval failed: ${error.reason || error.message}`
184
- );
185
- }
186
- }
187
-
188
- /**
189
- * Get escrow details
190
- */
191
- async getEscrow(escrowId: string): Promise<Escrow> {
192
- validateTxId(escrowId, 'escrowId');
193
- const escrowData = await this.contract.escrows(escrowId);
194
-
195
- return {
196
- escrowId,
197
- requester: escrowData.requester,
198
- provider: escrowData.provider,
199
- amount: escrowData.amount,
200
- releasedAmount: escrowData.releasedAmount,
201
- active: escrowData.active
202
- };
203
- }
204
-
205
- /**
206
- * Get escrow remaining balance (amount - releasedAmount)
207
- */
208
- async getEscrowBalance(escrowId: string): Promise<bigint> {
209
- validateTxId(escrowId, 'escrowId');
210
- return await this.contract.remaining(escrowId);
211
- }
212
-
213
- /**
214
- * @deprecated
215
- *
216
- * Payouts/refunds are executed by ACTPKernel (on-chain) as part of state transitions.
217
- * EscrowVault disbursement methods are `onlyKernel` and cannot be called by EOAs.
218
- *
219
- * Use:
220
- * - `BlockchainRuntime.releaseEscrow(txId, attestationUID?)` (recommended)
221
- * - or `ACTPKernel.transitionState(txId, State.SETTLED, proof)` (advanced)
222
- */
223
- async releaseEscrow(
224
- escrowId: string,
225
- _recipients: string[],
226
- _amounts: bigint[]
227
- ): Promise<void> {
228
- validateTxId(escrowId, 'escrowId');
229
- throw new ValidationError(
230
- 'EscrowVault.releaseEscrow',
231
- 'Escrow payouts are performed by ACTPKernel (onlyKernel). ' +
232
- 'Use BlockchainRuntime.releaseEscrow(txId, attestationUID?) or ACTPKernel.transitionState(txId, SETTLED).'
233
- );
234
- }
235
-
236
- /**
237
- * Check token balance
238
- */
239
- async getTokenBalance(tokenAddress: string, account: string): Promise<bigint> {
240
- const tokenContract = new Contract(tokenAddress, ERC20ABI, this.signer);
241
- return await tokenContract.balanceOf(account);
242
- }
243
-
244
- /**
245
- * Check token allowance
246
- */
247
- async getTokenAllowance(
248
- tokenAddress: string,
249
- owner: string,
250
- spender: string
251
- ): Promise<bigint> {
252
- const tokenContract = new Contract(tokenAddress, ERC20ABI, this.signer);
253
- return await tokenContract.allowance(owner, spender);
254
- }
255
- }