@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.
- package/dist/ACTPClient.d.ts +18 -0
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +67 -22
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +12 -0
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +30 -4
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/adapters/StandardAdapter.d.ts +20 -3
- package/dist/adapters/StandardAdapter.d.ts.map +1 -1
- package/dist/adapters/StandardAdapter.js +45 -11
- package/dist/adapters/StandardAdapter.js.map +1 -1
- package/dist/cli/commands/publish.js +16 -4
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.js +16 -4
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/tx.js +31 -3
- package/dist/cli/commands/tx.js.map +1 -1
- package/dist/cli/utils/client.d.ts.map +1 -1
- package/dist/cli/utils/client.js +1 -0
- package/dist/cli/utils/client.js.map +1 -1
- package/dist/config/networks.d.ts +2 -2
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +27 -22
- package/dist/config/networks.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +2 -1
- package/dist/level0/request.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +11 -5
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/runtime/MockStateManager.d.ts.map +1 -1
- package/dist/runtime/MockStateManager.js +2 -1
- package/dist/runtime/MockStateManager.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -1
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +27 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +52 -18
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/SmartWalletRouter.d.ts +116 -0
- package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
- package/dist/wallet/SmartWalletRouter.js +212 -0
- package/dist/wallet/SmartWalletRouter.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
- package/dist/wallet/aa/DualNonceManager.js +100 -5
- package/dist/wallet/aa/DualNonceManager.js.map +1 -1
- package/package.json +3 -6
- package/src/ACTPClient.ts +0 -1579
- package/src/abi/ACTPKernel.json +0 -1356
- package/src/abi/AgentRegistry.json +0 -915
- package/src/abi/ERC20.json +0 -40
- package/src/abi/EscrowVault.json +0 -134
- package/src/abi/IdentityRegistry.json +0 -316
- package/src/adapters/AdapterRegistry.ts +0 -173
- package/src/adapters/AdapterRouter.ts +0 -416
- package/src/adapters/BaseAdapter.ts +0 -498
- package/src/adapters/BasicAdapter.ts +0 -514
- package/src/adapters/IAdapter.ts +0 -292
- package/src/adapters/StandardAdapter.ts +0 -555
- package/src/adapters/X402Adapter.ts +0 -731
- package/src/adapters/index.ts +0 -60
- package/src/builders/DeliveryProofBuilder.ts +0 -327
- package/src/builders/QuoteBuilder.ts +0 -483
- package/src/builders/index.ts +0 -17
- package/src/cli/commands/balance.ts +0 -110
- package/src/cli/commands/batch.ts +0 -487
- package/src/cli/commands/config.ts +0 -231
- package/src/cli/commands/deploy-check.ts +0 -364
- package/src/cli/commands/deploy-env.ts +0 -120
- package/src/cli/commands/diff.ts +0 -141
- package/src/cli/commands/init.ts +0 -469
- package/src/cli/commands/mint.ts +0 -116
- package/src/cli/commands/pay.ts +0 -113
- package/src/cli/commands/publish.ts +0 -475
- package/src/cli/commands/pull.ts +0 -124
- package/src/cli/commands/register.ts +0 -247
- package/src/cli/commands/simulate.ts +0 -345
- package/src/cli/commands/time.ts +0 -302
- package/src/cli/commands/tx.ts +0 -448
- package/src/cli/commands/watch.ts +0 -211
- package/src/cli/index.ts +0 -134
- package/src/cli/utils/client.ts +0 -251
- package/src/cli/utils/config.ts +0 -389
- package/src/cli/utils/output.ts +0 -465
- package/src/cli/utils/wallet.ts +0 -109
- package/src/config/agirailsmd.ts +0 -262
- package/src/config/networks.ts +0 -275
- package/src/config/pendingPublish.ts +0 -237
- package/src/config/publishPipeline.ts +0 -359
- package/src/config/syncOperations.ts +0 -279
- package/src/erc8004/ERC8004Bridge.ts +0 -462
- package/src/erc8004/ReputationReporter.ts +0 -468
- package/src/erc8004/index.ts +0 -61
- package/src/errors/index.ts +0 -427
- package/src/index.ts +0 -364
- package/src/level0/Provider.ts +0 -117
- package/src/level0/ServiceDirectory.ts +0 -131
- package/src/level0/index.ts +0 -10
- package/src/level0/provide.ts +0 -132
- package/src/level0/request.ts +0 -432
- package/src/level1/Agent.ts +0 -1426
- package/src/level1/index.ts +0 -10
- package/src/level1/pricing/PriceCalculator.ts +0 -255
- package/src/level1/pricing/PricingStrategy.ts +0 -198
- package/src/level1/types/Job.ts +0 -179
- package/src/level1/types/Options.ts +0 -291
- package/src/level1/types/index.ts +0 -8
- package/src/protocol/ACTPKernel.ts +0 -808
- package/src/protocol/AgentRegistry.ts +0 -559
- package/src/protocol/DIDManager.ts +0 -629
- package/src/protocol/DIDResolver.ts +0 -554
- package/src/protocol/EASHelper.ts +0 -378
- package/src/protocol/EscrowVault.ts +0 -255
- package/src/protocol/EventMonitor.ts +0 -204
- package/src/protocol/MessageSigner.ts +0 -510
- package/src/protocol/ProofGenerator.ts +0 -339
- package/src/protocol/QuoteBuilder.ts +0 -15
- package/src/registry/AgentRegistryClient.ts +0 -202
- package/src/runtime/BlockchainRuntime.ts +0 -1015
- package/src/runtime/IACTPRuntime.ts +0 -306
- package/src/runtime/MockRuntime.ts +0 -1298
- package/src/runtime/MockStateManager.ts +0 -576
- package/src/runtime/index.ts +0 -25
- package/src/runtime/types/MockState.ts +0 -237
- package/src/storage/ArchiveBundleBuilder.ts +0 -561
- package/src/storage/ArweaveClient.ts +0 -946
- package/src/storage/FilebaseClient.ts +0 -790
- package/src/storage/index.ts +0 -96
- package/src/storage/types.ts +0 -348
- package/src/types/adapter.ts +0 -310
- package/src/types/agent.ts +0 -79
- package/src/types/did.ts +0 -223
- package/src/types/eip712.ts +0 -175
- package/src/types/erc8004.ts +0 -293
- package/src/types/escrow.ts +0 -27
- package/src/types/index.ts +0 -17
- package/src/types/message.ts +0 -145
- package/src/types/state.ts +0 -87
- package/src/types/transaction.ts +0 -69
- package/src/types/x402.ts +0 -251
- package/src/utils/ErrorRecoveryGuide.ts +0 -676
- package/src/utils/Helpers.ts +0 -688
- package/src/utils/IPFSClient.ts +0 -368
- package/src/utils/Logger.ts +0 -484
- package/src/utils/NonceManager.ts +0 -591
- package/src/utils/RateLimiter.ts +0 -534
- package/src/utils/ReceivedNonceTracker.ts +0 -567
- package/src/utils/SDKLifecycle.ts +0 -416
- package/src/utils/SecureNonce.ts +0 -78
- package/src/utils/Semaphore.ts +0 -276
- package/src/utils/UsedAttestationTracker.ts +0 -385
- package/src/utils/canonicalJson.ts +0 -38
- package/src/utils/circuitBreaker.ts +0 -324
- package/src/utils/computeTypeHash.ts +0 -48
- package/src/utils/fsSafe.ts +0 -80
- package/src/utils/index.ts +0 -80
- package/src/utils/retry.ts +0 -364
- package/src/utils/security.ts +0 -418
- package/src/utils/validation.ts +0 -540
- package/src/wallet/AutoWalletProvider.ts +0 -299
- package/src/wallet/EOAWalletProvider.ts +0 -69
- package/src/wallet/IWalletProvider.ts +0 -135
- package/src/wallet/aa/BundlerClient.ts +0 -274
- package/src/wallet/aa/DualNonceManager.ts +0 -173
- package/src/wallet/aa/PaymasterClient.ts +0 -174
- package/src/wallet/aa/TransactionBatcher.ts +0 -353
- package/src/wallet/aa/UserOpBuilder.ts +0 -246
- package/src/wallet/aa/constants.ts +0 -60
- 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
|
-
}
|