@agirails/sdk 2.0.1-beta → 2.0.2
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/LICENSE +190 -0
- package/README.md +116 -108
- package/bin/actp +10 -0
- package/dist/ACTPClient.d.ts +456 -33
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +477 -93
- package/dist/ACTPClient.js.map +1 -1
- package/dist/abi/AgentRegistry.json +782 -0
- package/dist/abi/EscrowVault.json +106 -38
- package/dist/abi/IdentityRegistry.json +316 -0
- package/dist/adapters/BaseAdapter.d.ts +231 -0
- package/dist/adapters/BaseAdapter.d.ts.map +1 -0
- package/dist/adapters/BaseAdapter.js +393 -0
- package/dist/adapters/BaseAdapter.js.map +1 -0
- package/dist/adapters/BeginnerAdapter.d.ts +152 -0
- package/dist/adapters/BeginnerAdapter.d.ts.map +1 -0
- package/dist/adapters/BeginnerAdapter.js +168 -0
- package/dist/adapters/BeginnerAdapter.js.map +1 -0
- package/dist/adapters/IntermediateAdapter.d.ts +211 -0
- package/dist/adapters/IntermediateAdapter.d.ts.map +1 -0
- package/dist/adapters/IntermediateAdapter.js +260 -0
- package/dist/adapters/IntermediateAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +15 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +26 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/builders/DeliveryProofBuilder.d.ts +60 -1
- package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -1
- package/dist/builders/DeliveryProofBuilder.js +81 -5
- package/dist/builders/DeliveryProofBuilder.js.map +1 -1
- package/dist/builders/QuoteBuilder.d.ts +101 -0
- package/dist/builders/QuoteBuilder.d.ts.map +1 -1
- package/dist/builders/QuoteBuilder.js +120 -3
- package/dist/builders/QuoteBuilder.js.map +1 -1
- package/dist/builders/index.d.ts +4 -0
- package/dist/builders/index.d.ts.map +1 -1
- package/dist/builders/index.js +4 -0
- package/dist/builders/index.js.map +1 -1
- package/dist/cli/commands/balance.d.ts +13 -0
- package/dist/cli/commands/balance.d.ts.map +1 -0
- package/dist/cli/commands/balance.js +89 -0
- package/dist/cli/commands/balance.js.map +1 -0
- package/dist/cli/commands/batch.d.ts +24 -0
- package/dist/cli/commands/batch.d.ts.map +1 -0
- package/dist/cli/commands/batch.js +424 -0
- package/dist/cli/commands/batch.js.map +1 -0
- package/dist/cli/commands/config.d.ts +13 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +192 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +143 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/mint.d.ts +13 -0
- package/dist/cli/commands/mint.d.ts.map +1 -0
- package/dist/cli/commands/mint.js +91 -0
- package/dist/cli/commands/mint.js.map +1 -0
- package/dist/cli/commands/pay.d.ts +18 -0
- package/dist/cli/commands/pay.d.ts.map +1 -0
- package/dist/cli/commands/pay.js +87 -0
- package/dist/cli/commands/pay.js.map +1 -0
- package/dist/cli/commands/simulate.d.ts +32 -0
- package/dist/cli/commands/simulate.d.ts.map +1 -0
- package/dist/cli/commands/simulate.js +290 -0
- package/dist/cli/commands/simulate.js.map +1 -0
- package/dist/cli/commands/time.d.ts +29 -0
- package/dist/cli/commands/time.d.ts.map +1 -0
- package/dist/cli/commands/time.js +252 -0
- package/dist/cli/commands/time.js.map +1 -0
- package/dist/cli/commands/tx.d.ts +16 -0
- package/dist/cli/commands/tx.d.ts.map +1 -0
- package/dist/cli/commands/tx.js +379 -0
- package/dist/cli/commands/tx.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +20 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +160 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +104 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/client.d.ts +70 -0
- package/dist/cli/utils/client.d.ts.map +1 -0
- package/dist/cli/utils/client.js +240 -0
- package/dist/cli/utils/client.js.map +1 -0
- package/dist/cli/utils/config.d.ts +91 -0
- package/dist/cli/utils/config.d.ts.map +1 -0
- package/dist/cli/utils/config.js +240 -0
- package/dist/cli/utils/config.js.map +1 -0
- package/dist/cli/utils/output.d.ts +174 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +380 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/config/networks.d.ts +28 -0
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +60 -12
- package/dist/config/networks.js.map +1 -1
- package/dist/errors/index.d.ts +165 -2
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +260 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +61 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -36
- package/dist/index.js.map +1 -1
- package/dist/level0/Provider.d.ts +106 -0
- package/dist/level0/Provider.d.ts.map +1 -0
- package/dist/level0/Provider.js +10 -0
- package/dist/level0/Provider.js.map +1 -0
- package/dist/level0/ServiceDirectory.d.ts +74 -0
- package/dist/level0/ServiceDirectory.d.ts.map +1 -0
- package/dist/level0/ServiceDirectory.js +122 -0
- package/dist/level0/ServiceDirectory.js.map +1 -0
- package/dist/level0/index.d.ts +10 -0
- package/dist/level0/index.d.ts.map +1 -0
- package/dist/level0/index.js +15 -0
- package/dist/level0/index.js.map +1 -0
- package/dist/level0/provide.d.ts +51 -0
- package/dist/level0/provide.d.ts.map +1 -0
- package/dist/level0/provide.js +113 -0
- package/dist/level0/provide.js.map +1 -0
- package/dist/level0/request.d.ts +53 -0
- package/dist/level0/request.d.ts.map +1 -0
- package/dist/level0/request.js +462 -0
- package/dist/level0/request.js.map +1 -0
- package/dist/level1/Agent.d.ts +472 -0
- package/dist/level1/Agent.d.ts.map +1 -0
- package/dist/level1/Agent.js +1091 -0
- package/dist/level1/Agent.js.map +1 -0
- package/dist/level1/index.d.ts +10 -0
- package/dist/level1/index.d.ts.map +1 -0
- package/dist/level1/index.js +30 -0
- package/dist/level1/index.js.map +1 -0
- package/dist/level1/pricing/PriceCalculator.d.ts +62 -0
- package/dist/level1/pricing/PriceCalculator.d.ts.map +1 -0
- package/dist/level1/pricing/PriceCalculator.js +237 -0
- package/dist/level1/pricing/PriceCalculator.js.map +1 -0
- package/dist/level1/pricing/PricingStrategy.d.ts +179 -0
- package/dist/level1/pricing/PricingStrategy.d.ts.map +1 -0
- package/dist/level1/pricing/PricingStrategy.js +11 -0
- package/dist/level1/pricing/PricingStrategy.js.map +1 -0
- package/dist/level1/types/Job.d.ts +166 -0
- package/dist/level1/types/Job.d.ts.map +1 -0
- package/dist/level1/types/Job.js +11 -0
- package/dist/level1/types/Job.js.map +1 -0
- package/dist/level1/types/Options.d.ts +258 -0
- package/dist/level1/types/Options.d.ts.map +1 -0
- package/dist/level1/types/Options.js +8 -0
- package/dist/level1/types/Options.js.map +1 -0
- package/dist/level1/types/index.d.ts +8 -0
- package/dist/level1/types/index.d.ts.map +1 -0
- package/dist/level1/types/index.js +8 -0
- package/dist/level1/types/index.js.map +1 -0
- package/dist/protocol/ACTPKernel.d.ts +229 -2
- package/dist/protocol/ACTPKernel.d.ts.map +1 -1
- package/dist/protocol/ACTPKernel.js +367 -33
- package/dist/protocol/ACTPKernel.js.map +1 -1
- package/dist/protocol/AgentRegistry.d.ts +177 -0
- package/dist/protocol/AgentRegistry.d.ts.map +1 -0
- package/dist/protocol/AgentRegistry.js +449 -0
- package/dist/protocol/AgentRegistry.js.map +1 -0
- package/dist/protocol/DIDManager.d.ts +289 -0
- package/dist/protocol/DIDManager.d.ts.map +1 -0
- package/dist/protocol/DIDManager.js +481 -0
- package/dist/protocol/DIDManager.js.map +1 -0
- package/dist/protocol/DIDResolver.d.ts +236 -0
- package/dist/protocol/DIDResolver.d.ts.map +1 -0
- package/dist/protocol/DIDResolver.js +495 -0
- package/dist/protocol/DIDResolver.js.map +1 -0
- package/dist/protocol/EASHelper.d.ts +57 -2
- package/dist/protocol/EASHelper.d.ts.map +1 -1
- package/dist/protocol/EASHelper.js +230 -37
- package/dist/protocol/EASHelper.js.map +1 -1
- package/dist/protocol/EscrowVault.d.ts +93 -2
- package/dist/protocol/EscrowVault.d.ts.map +1 -1
- package/dist/protocol/EscrowVault.js +122 -33
- package/dist/protocol/EscrowVault.js.map +1 -1
- package/dist/protocol/EventMonitor.d.ts +45 -1
- package/dist/protocol/EventMonitor.d.ts.map +1 -1
- package/dist/protocol/EventMonitor.js +64 -8
- package/dist/protocol/EventMonitor.js.map +1 -1
- package/dist/protocol/MessageSigner.d.ts +116 -2
- package/dist/protocol/MessageSigner.d.ts.map +1 -1
- package/dist/protocol/MessageSigner.js +215 -9
- package/dist/protocol/MessageSigner.js.map +1 -1
- package/dist/protocol/ProofGenerator.d.ts +93 -0
- package/dist/protocol/ProofGenerator.d.ts.map +1 -1
- package/dist/protocol/ProofGenerator.js +194 -9
- package/dist/protocol/ProofGenerator.js.map +1 -1
- package/dist/protocol/QuoteBuilder.d.ts +8 -0
- package/dist/protocol/QuoteBuilder.d.ts.map +1 -1
- package/dist/protocol/QuoteBuilder.js +8 -0
- package/dist/protocol/QuoteBuilder.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts +360 -0
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -0
- package/dist/runtime/BlockchainRuntime.js +767 -0
- package/dist/runtime/BlockchainRuntime.js.map +1 -0
- package/dist/runtime/IACTPRuntime.d.ts +271 -0
- package/dist/runtime/IACTPRuntime.d.ts.map +1 -0
- package/dist/runtime/IACTPRuntime.js +15 -0
- package/dist/runtime/IACTPRuntime.js.map +1 -0
- package/dist/runtime/MockRuntime.d.ts +445 -0
- package/dist/runtime/MockRuntime.d.ts.map +1 -0
- package/dist/runtime/MockRuntime.js +1065 -0
- package/dist/runtime/MockRuntime.js.map +1 -0
- package/dist/runtime/MockStateManager.d.ts +233 -0
- package/dist/runtime/MockStateManager.d.ts.map +1 -0
- package/dist/runtime/MockStateManager.js +533 -0
- package/dist/runtime/MockStateManager.js.map +1 -0
- package/dist/runtime/index.d.ts +14 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +42 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/types/MockState.d.ts +167 -0
- package/dist/runtime/types/MockState.d.ts.map +1 -0
- package/dist/runtime/types/MockState.js +43 -0
- package/dist/runtime/types/MockState.js.map +1 -0
- package/dist/types/agent.d.ts +76 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +8 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/did.d.ts +192 -0
- package/dist/types/did.d.ts.map +1 -0
- package/dist/types/did.js +38 -0
- package/dist/types/did.js.map +1 -0
- package/dist/types/eip712.d.ts +34 -0
- package/dist/types/eip712.d.ts.map +1 -1
- package/dist/types/eip712.js +31 -5
- package/dist/types/eip712.js.map +1 -1
- package/dist/types/escrow.d.ts +17 -10
- package/dist/types/escrow.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/message.d.ts +32 -0
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js +4 -0
- package/dist/types/message.js.map +1 -1
- package/dist/types/state.d.ts +28 -0
- package/dist/types/state.d.ts.map +1 -1
- package/dist/types/state.js +37 -6
- package/dist/types/state.js.map +1 -1
- package/dist/types/transaction.d.ts +17 -0
- package/dist/types/transaction.d.ts.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.d.ts +125 -0
- package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -0
- package/dist/utils/ErrorRecoveryGuide.js +579 -0
- package/dist/utils/ErrorRecoveryGuide.js.map +1 -0
- package/dist/utils/Helpers.d.ts +453 -0
- package/dist/utils/Helpers.d.ts.map +1 -0
- package/dist/utils/Helpers.js +623 -0
- package/dist/utils/Helpers.js.map +1 -0
- package/dist/utils/IPFSClient.d.ts +113 -0
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +128 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/utils/Logger.d.ts +195 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +382 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/NonceManager.d.ts +234 -1
- package/dist/utils/NonceManager.d.ts.map +1 -1
- package/dist/utils/NonceManager.js +372 -7
- package/dist/utils/NonceManager.js.map +1 -1
- package/dist/utils/RateLimiter.d.ts +253 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +424 -0
- package/dist/utils/RateLimiter.js.map +1 -0
- package/dist/utils/ReceivedNonceTracker.d.ts +175 -0
- package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -1
- package/dist/utils/ReceivedNonceTracker.js +261 -5
- package/dist/utils/ReceivedNonceTracker.js.map +1 -1
- package/dist/utils/SDKLifecycle.d.ts +156 -0
- package/dist/utils/SDKLifecycle.d.ts.map +1 -0
- package/dist/utils/SDKLifecycle.js +347 -0
- package/dist/utils/SDKLifecycle.js.map +1 -0
- package/dist/utils/SecureNonce.d.ts +57 -0
- package/dist/utils/SecureNonce.d.ts.map +1 -0
- package/dist/utils/SecureNonce.js +80 -0
- package/dist/utils/SecureNonce.js.map +1 -0
- package/dist/utils/Semaphore.d.ts +123 -0
- package/dist/utils/Semaphore.d.ts.map +1 -0
- package/dist/utils/Semaphore.js +247 -0
- package/dist/utils/Semaphore.js.map +1 -0
- package/dist/utils/UsedAttestationTracker.d.ts +167 -0
- package/dist/utils/UsedAttestationTracker.d.ts.map +1 -0
- package/dist/utils/UsedAttestationTracker.js +309 -0
- package/dist/utils/UsedAttestationTracker.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +22 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -1
- package/dist/utils/canonicalJson.js +26 -3
- package/dist/utils/canonicalJson.js.map +1 -1
- package/dist/utils/computeTypeHash.d.ts +14 -0
- package/dist/utils/computeTypeHash.d.ts.map +1 -1
- package/dist/utils/computeTypeHash.js +19 -2
- package/dist/utils/computeTypeHash.js.map +1 -1
- package/dist/utils/fsSafe.d.ts +14 -0
- package/dist/utils/fsSafe.d.ts.map +1 -0
- package/dist/utils/fsSafe.js +89 -0
- package/dist/utils/fsSafe.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +51 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/security.d.ts +147 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +391 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/utils/validation.d.ts +40 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +184 -7
- package/dist/utils/validation.js.map +1 -1
- package/package.json +54 -37
- package/src/ACTPClient.ts +692 -178
- package/src/abi/AgentRegistry.json +782 -0
- package/src/abi/EscrowVault.json +106 -38
- package/src/abi/IdentityRegistry.json +316 -0
- package/src/adapters/BaseAdapter.ts +473 -0
- package/src/adapters/BeginnerAdapter.ts +232 -0
- package/src/adapters/IntermediateAdapter.ts +316 -0
- package/src/adapters/index.ts +25 -0
- package/src/builders/DeliveryProofBuilder.ts +3 -2
- package/src/cli/commands/balance.ts +110 -0
- package/src/cli/commands/batch.ts +487 -0
- package/src/cli/commands/config.ts +231 -0
- package/src/cli/commands/init.ts +161 -0
- package/src/cli/commands/mint.ts +116 -0
- package/src/cli/commands/pay.ts +113 -0
- package/src/cli/commands/simulate.ts +345 -0
- package/src/cli/commands/time.ts +303 -0
- package/src/cli/commands/tx.ts +448 -0
- package/src/cli/commands/watch.ts +211 -0
- package/src/cli/index.ts +116 -0
- package/src/cli/utils/client.ts +249 -0
- package/src/cli/utils/config.ts +282 -0
- package/src/cli/utils/output.ts +465 -0
- package/src/config/networks.ts +32 -9
- package/src/errors/index.ts +298 -1
- package/src/index.ts +207 -71
- package/src/level0/Provider.ts +117 -0
- package/src/level0/ServiceDirectory.ts +131 -0
- package/src/level0/index.ts +10 -0
- package/src/level0/provide.ts +131 -0
- package/src/level0/request.ts +494 -0
- package/src/level1/Agent.ts +1432 -0
- package/src/level1/index.ts +10 -0
- package/src/level1/pricing/PriceCalculator.ts +255 -0
- package/src/level1/pricing/PricingStrategy.ts +198 -0
- package/src/level1/types/Job.ts +179 -0
- package/src/level1/types/Options.ts +291 -0
- package/src/level1/types/index.ts +8 -0
- package/src/protocol/ACTPKernel.ts +175 -23
- package/src/protocol/AgentRegistry.ts +559 -0
- package/src/protocol/DIDManager.ts +629 -0
- package/src/protocol/DIDResolver.ts +554 -0
- package/src/protocol/EASHelper.ts +230 -46
- package/src/protocol/EscrowVault.ts +68 -50
- package/src/protocol/EventMonitor.ts +44 -15
- package/src/protocol/MessageSigner.ts +193 -13
- package/src/protocol/ProofGenerator.ts +223 -4
- package/src/runtime/BlockchainRuntime.ts +993 -0
- package/src/runtime/IACTPRuntime.ts +284 -0
- package/src/runtime/MockRuntime.ts +1244 -0
- package/src/runtime/MockStateManager.ts +576 -0
- package/src/runtime/index.ts +25 -0
- package/src/runtime/types/MockState.ts +227 -0
- package/src/types/agent.ts +79 -0
- package/src/types/did.ts +223 -0
- package/src/types/escrow.ts +12 -11
- package/src/types/index.ts +5 -1
- package/src/types/state.ts +12 -3
- package/src/types/transaction.ts +4 -1
- package/src/utils/ErrorRecoveryGuide.ts +675 -0
- package/src/utils/Helpers.ts +688 -0
- package/src/utils/IPFSClient.ts +122 -5
- package/src/utils/Logger.ts +484 -0
- package/src/utils/NonceManager.ts +305 -8
- package/src/utils/RateLimiter.ts +534 -0
- package/src/utils/ReceivedNonceTracker.ts +170 -0
- package/src/utils/SDKLifecycle.ts +416 -0
- package/src/utils/SecureNonce.ts +78 -0
- package/src/utils/Semaphore.ts +276 -0
- package/src/utils/UsedAttestationTracker.ts +387 -0
- package/src/utils/fsSafe.ts +75 -0
- package/src/utils/index.ts +80 -0
- package/src/utils/security.ts +418 -0
- package/src/utils/validation.ts +164 -0
- package/src/__tests__/ProofGenerator.test.ts +0 -124
- package/src/__tests__/QuoteBuilder.test.ts +0 -516
- package/src/__tests__/StateMachine.test.ts +0 -82
- package/src/__tests__/builders/DeliveryProofBuilder.test.ts +0 -581
- package/src/__tests__/integration/ACTPClient.test.ts +0 -263
- package/src/__tests__/integration.test.ts +0 -289
- package/src/__tests__/protocol/EASHelper.test.ts +0 -472
- package/src/__tests__/protocol/EventMonitor.test.ts +0 -382
- package/src/__tests__/security/ACTPKernel.security.test.ts +0 -1167
- package/src/__tests__/security/EscrowVault.security.test.ts +0 -570
- package/src/__tests__/security/MessageSigner.security.test.ts +0 -286
- package/src/__tests__/security/NonceReplay.security.test.ts +0 -501
- package/src/__tests__/security/validation.security.test.ts +0 -376
- package/src/__tests__/utils/IPFSClient.test.ts +0 -262
- package/src/__tests__/utils/NonceManager.test.ts +0 -205
- package/src/__tests__/utils/canonicalJson.test.ts +0 -153
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseAdapter - Abstract base class for all adapter implementations
|
|
3
|
+
*
|
|
4
|
+
* Provides shared utility methods for parsing user-friendly inputs into
|
|
5
|
+
* protocol-level types. All adapters extend this class to ensure consistent
|
|
6
|
+
* behavior across the Three-Level API.
|
|
7
|
+
*
|
|
8
|
+
* Key Responsibilities:
|
|
9
|
+
* - Amount parsing (string → bigint with 6 decimals for USDC)
|
|
10
|
+
* - Address validation (0x-prefixed hex)
|
|
11
|
+
* - Deadline parsing ("+24h" → Unix timestamp)
|
|
12
|
+
* - User-friendly error messages
|
|
13
|
+
*
|
|
14
|
+
* @module adapters/BaseAdapter
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Constants
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Default dispute window in seconds (2 days).
|
|
23
|
+
* Used when no dispute window is specified in transaction parameters.
|
|
24
|
+
*/
|
|
25
|
+
export const DEFAULT_DISPUTE_WINDOW_SECONDS = 172800;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Minimum dispute window in seconds (1 hour).
|
|
29
|
+
* Ensures requesters have reasonable time to dispute.
|
|
30
|
+
*
|
|
31
|
+
* SECURITY: Prevents providers from setting windows too short
|
|
32
|
+
* to avoid dispute detection.
|
|
33
|
+
*/
|
|
34
|
+
export const MIN_DISPUTE_WINDOW_SECONDS = 3600; // 1 hour
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Maximum dispute window in seconds (30 days).
|
|
38
|
+
* Prevents excessively long fund locks.
|
|
39
|
+
*
|
|
40
|
+
* SECURITY: Prevents DoS via indefinite fund locking.
|
|
41
|
+
*/
|
|
42
|
+
export const MAX_DISPUTE_WINDOW_SECONDS = 30 * 24 * 3600; // 30 days
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Default deadline offset in seconds (24 hours).
|
|
46
|
+
* Used when no deadline is specified in transaction parameters.
|
|
47
|
+
*/
|
|
48
|
+
export const DEFAULT_DEADLINE_SECONDS = 86400;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Minimum transaction amount in USDC wei (6 decimals).
|
|
52
|
+
* $0.05 minimum per AGIRAILS protocol specification.
|
|
53
|
+
*/
|
|
54
|
+
export const MIN_AMOUNT_WEI = 50_000n; // $0.05 USDC
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Maximum deadline in hours (10 years).
|
|
58
|
+
* Prevents integer overflow in deadline calculations.
|
|
59
|
+
*/
|
|
60
|
+
export const MAX_DEADLINE_HOURS = 87600; // 10 years
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Maximum deadline in days (10 years).
|
|
64
|
+
* Prevents integer overflow in deadline calculations.
|
|
65
|
+
*/
|
|
66
|
+
export const MAX_DEADLINE_DAYS = 3650; // 10 years
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Custom error for validation failures.
|
|
70
|
+
*
|
|
71
|
+
* Thrown when user input is invalid (e.g., malformed address, negative amount).
|
|
72
|
+
* Provides descriptive error messages for end-user debugging.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* throw new ValidationError('Invalid amount format: "abc". Expected number like "100" or "100.50"');
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export class ValidationError extends Error {
|
|
80
|
+
constructor(message: string) {
|
|
81
|
+
super(message);
|
|
82
|
+
this.name = 'ValidationError';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Abstract base adapter with shared parsing utilities.
|
|
88
|
+
*
|
|
89
|
+
* Provides common functionality for all adapter implementations:
|
|
90
|
+
* - Amount parsing (USDC has 6 decimals)
|
|
91
|
+
* - Address validation
|
|
92
|
+
* - Deadline parsing (relative time or Unix timestamp)
|
|
93
|
+
* - Amount formatting
|
|
94
|
+
*
|
|
95
|
+
* @abstract
|
|
96
|
+
*/
|
|
97
|
+
export abstract class BaseAdapter {
|
|
98
|
+
/**
|
|
99
|
+
* Creates a new BaseAdapter instance.
|
|
100
|
+
*
|
|
101
|
+
* @param requesterAddress - The requester's Ethereum address
|
|
102
|
+
*/
|
|
103
|
+
constructor(protected requesterAddress: string) {}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Parse user-friendly amount string to bigint (USDC has 6 decimals).
|
|
107
|
+
*
|
|
108
|
+
* Accepts multiple input formats:
|
|
109
|
+
* - "100" → 100_000_000n (100.00 USDC)
|
|
110
|
+
* - "100.50" → 100_500_000n (100.50 USDC)
|
|
111
|
+
* - "100 USDC" → 100_000_000n (strips currency suffix)
|
|
112
|
+
* - "$100" → 100_000_000n (strips $ prefix)
|
|
113
|
+
* - 100 (number) → 100_000_000n
|
|
114
|
+
*
|
|
115
|
+
* Rejects:
|
|
116
|
+
* - "abc" → throws ValidationError
|
|
117
|
+
* - "" → throws ValidationError
|
|
118
|
+
* - "-100" → throws ValidationError (negative amounts)
|
|
119
|
+
* - "100.1234567" → throws ValidationError (too many decimals)
|
|
120
|
+
*
|
|
121
|
+
* @param amount - Amount as string or number
|
|
122
|
+
* @returns Amount as bigint with 6 decimals
|
|
123
|
+
* @throws {ValidationError} If amount format is invalid
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const amount = this.parseAmount("100.50"); // 100_500_000n
|
|
128
|
+
* const amount = this.parseAmount("100 USDC"); // 100_000_000n
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
protected parseAmount(amount: string | number): bigint {
|
|
132
|
+
// Issue #3 Fix: Normalize input - handle all Unicode whitespace
|
|
133
|
+
// Converts all Unicode whitespace to regular spaces, then strip currency symbols
|
|
134
|
+
let normalized = String(amount)
|
|
135
|
+
.replace(/[\s\u00A0\u2000-\u200B\uFEFF]/g, ' ') // Replace all Unicode whitespace with regular space
|
|
136
|
+
.replace(/^[\$]/, '') // Strip leading $
|
|
137
|
+
.replace(/\s*(USDC|usdc)$/, '') // Strip trailing USDC
|
|
138
|
+
.replace(/,/g, '') // Strip thousands separators
|
|
139
|
+
.replace(/\s+/g, '') // Remove ALL whitespace (including normalized Unicode spaces)
|
|
140
|
+
.trim(); // Final trim for edge cases
|
|
141
|
+
|
|
142
|
+
// Check for negative
|
|
143
|
+
if (normalized.startsWith('-')) {
|
|
144
|
+
throw new ValidationError('Amount cannot be negative');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Validate format (integer or decimal with up to 6 decimal places)
|
|
148
|
+
if (!/^\d+(\.\d{1,6})?$/.test(normalized)) {
|
|
149
|
+
throw new ValidationError(
|
|
150
|
+
`Invalid amount format: "${amount}". Expected number like "100" or "100.50"`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Parse to bigint with 6 decimals
|
|
155
|
+
try {
|
|
156
|
+
const parts = normalized.split('.');
|
|
157
|
+
const wholePart = parts[0];
|
|
158
|
+
const decimalPart = (parts[1] || '').padEnd(6, '0'); // Pad to 6 decimals
|
|
159
|
+
|
|
160
|
+
const wholeAmount = BigInt(wholePart) * 1_000_000n;
|
|
161
|
+
const decimalAmount = BigInt(decimalPart);
|
|
162
|
+
|
|
163
|
+
const totalAmount = wholeAmount + decimalAmount;
|
|
164
|
+
|
|
165
|
+
// M1 Fix: Enforce minimum amount ($0.05 USDC = 50,000 wei)
|
|
166
|
+
if (totalAmount < MIN_AMOUNT_WEI) {
|
|
167
|
+
throw new ValidationError(
|
|
168
|
+
`Amount too small: "${amount}". Minimum transaction is $0.05 USDC`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return totalAmount;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// Re-throw ValidationError as-is
|
|
175
|
+
if (error instanceof ValidationError) {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
throw new ValidationError(
|
|
179
|
+
`Failed to parse amount: "${amount}". Please use format like "100" or "100.50"`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Validate Ethereum address format.
|
|
186
|
+
*
|
|
187
|
+
* Checks that address:
|
|
188
|
+
* - Is a string
|
|
189
|
+
* - Starts with "0x"
|
|
190
|
+
* - Is exactly 42 characters (0x + 40 hex chars)
|
|
191
|
+
* - Contains only valid hex characters
|
|
192
|
+
*
|
|
193
|
+
* Note: Does not validate checksum (EIP-55) in mock mode.
|
|
194
|
+
*
|
|
195
|
+
* @param address - Address to validate
|
|
196
|
+
* @param paramName - Parameter name for error message
|
|
197
|
+
* @returns Validated address (unchanged)
|
|
198
|
+
* @throws {ValidationError} If address format is invalid
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const provider = this.validateAddress(params.to, 'to');
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
protected validateAddress(address: string, paramName: string): string {
|
|
206
|
+
if (typeof address !== 'string') {
|
|
207
|
+
throw new ValidationError(
|
|
208
|
+
`Invalid ${paramName} address: expected string, got ${typeof address}`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check 0x prefix
|
|
213
|
+
if (!address.startsWith('0x')) {
|
|
214
|
+
throw new ValidationError(
|
|
215
|
+
`Invalid ${paramName} address: "${address}". Expected 0x-prefixed hex string.`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check length (0x + 40 hex chars = 42 total)
|
|
220
|
+
if (address.length !== 42) {
|
|
221
|
+
throw new ValidationError(
|
|
222
|
+
`Invalid ${paramName} address: "${address}". Expected 42 characters (0x + 40 hex).`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check hex format
|
|
227
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
228
|
+
throw new ValidationError(
|
|
229
|
+
`Invalid ${paramName} address: "${address}". Contains invalid hex characters.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Issue #2 Fix: Normalize to lowercase for consistency
|
|
234
|
+
// This prevents case-sensitivity issues when comparing addresses
|
|
235
|
+
return address.toLowerCase();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Parse deadline from relative time expression or Unix timestamp.
|
|
240
|
+
*
|
|
241
|
+
* Accepts multiple formats:
|
|
242
|
+
* - undefined → now + 24 hours (default)
|
|
243
|
+
* - 1734076400 (number) → passed through as Unix timestamp
|
|
244
|
+
* - "+1h" → now + 1 hour
|
|
245
|
+
* - "+24h" → now + 24 hours
|
|
246
|
+
* - "+7d" → now + 7 days
|
|
247
|
+
*
|
|
248
|
+
* Rejects:
|
|
249
|
+
* - "invalid" → throws ValidationError
|
|
250
|
+
* - "-24h" → throws ValidationError (negative time)
|
|
251
|
+
*
|
|
252
|
+
* @param deadline - Deadline as relative time string, Unix timestamp, or undefined
|
|
253
|
+
* @param currentTime - Current time in seconds (defaults to Date.now() / 1000)
|
|
254
|
+
* @returns Unix timestamp in seconds
|
|
255
|
+
* @throws {ValidationError} If deadline format is invalid
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* const deadline = this.parseDeadline("+24h"); // now + 86400
|
|
260
|
+
* const deadline = this.parseDeadline(1734076400); // 1734076400
|
|
261
|
+
* const deadline = this.parseDeadline(); // now + 86400 (default)
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
protected parseDeadline(deadline?: string | number, currentTime?: number): number {
|
|
265
|
+
const now = currentTime ?? Math.floor(Date.now() / 1000);
|
|
266
|
+
|
|
267
|
+
if (deadline === undefined) {
|
|
268
|
+
// Default: 24 hours from now
|
|
269
|
+
return now + DEFAULT_DEADLINE_SECONDS;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (typeof deadline === 'number') {
|
|
273
|
+
return deadline;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Parse relative time
|
|
277
|
+
const match = deadline.match(/^\+(\d+)(h|d)$/);
|
|
278
|
+
if (!match) {
|
|
279
|
+
throw new ValidationError(
|
|
280
|
+
`Invalid deadline format: "${deadline}". Expected Unix timestamp or relative time (e.g., "+24h", "+7d")`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const [, amountStr, unit] = match;
|
|
285
|
+
const amount = parseInt(amountStr, 10);
|
|
286
|
+
|
|
287
|
+
// H1 Fix: Add bounds check to prevent integer overflow
|
|
288
|
+
if (unit === 'h' && amount > MAX_DEADLINE_HOURS) {
|
|
289
|
+
throw new ValidationError(
|
|
290
|
+
`Deadline too far in future: "${deadline}". Maximum is 10 years (${MAX_DEADLINE_HOURS}h)`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
if (unit === 'd' && amount > MAX_DEADLINE_DAYS) {
|
|
294
|
+
throw new ValidationError(
|
|
295
|
+
`Deadline too far in future: "${deadline}". Maximum is 10 years (${MAX_DEADLINE_DAYS}d)`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const multiplier = unit === 'h' ? 3600 : 86400;
|
|
300
|
+
|
|
301
|
+
return now + amount * multiplier;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Validate and normalize dispute window.
|
|
306
|
+
*
|
|
307
|
+
* SECURITY FIX (L-1): Enforces bounds on dispute window:
|
|
308
|
+
* - Minimum: 1 hour (prevents skipping disputes)
|
|
309
|
+
* - Maximum: 30 days (prevents indefinite fund locking)
|
|
310
|
+
*
|
|
311
|
+
* @param disputeWindow - Dispute window in seconds (undefined uses default)
|
|
312
|
+
* @returns Validated dispute window in seconds
|
|
313
|
+
* @throws {ValidationError} If window is outside allowed bounds
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* this.validateDisputeWindow(3600); // 1 hour - OK
|
|
318
|
+
* this.validateDisputeWindow(86400); // 1 day - OK
|
|
319
|
+
* this.validateDisputeWindow(100); // Too short - throws
|
|
320
|
+
* this.validateDisputeWindow(31 * 86400); // Too long - throws
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
protected validateDisputeWindow(disputeWindow?: number): number {
|
|
324
|
+
if (disputeWindow === undefined) {
|
|
325
|
+
return DEFAULT_DISPUTE_WINDOW_SECONDS;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (disputeWindow < MIN_DISPUTE_WINDOW_SECONDS) {
|
|
329
|
+
throw new ValidationError(
|
|
330
|
+
`Dispute window too short: ${disputeWindow} seconds. ` +
|
|
331
|
+
`Minimum is ${MIN_DISPUTE_WINDOW_SECONDS} seconds (1 hour).`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (disputeWindow > MAX_DISPUTE_WINDOW_SECONDS) {
|
|
336
|
+
throw new ValidationError(
|
|
337
|
+
`Dispute window too long: ${disputeWindow} seconds. ` +
|
|
338
|
+
`Maximum is ${MAX_DISPUTE_WINDOW_SECONDS} seconds (30 days).`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return disputeWindow;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Validate bytes32 hex string format.
|
|
347
|
+
*
|
|
348
|
+
* SECURITY FIX (L-2): Validates that a string is a valid bytes32 hex.
|
|
349
|
+
* Used for transaction IDs, escrow IDs, attestation UIDs, etc.
|
|
350
|
+
*
|
|
351
|
+
* @param value - Value to validate
|
|
352
|
+
* @param paramName - Parameter name for error message
|
|
353
|
+
* @returns Validated bytes32 string (normalized to lowercase)
|
|
354
|
+
* @throws {ValidationError} If format is invalid
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* const txId = this.validateBytes32(id, 'transactionId');
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
protected validateBytes32(value: string, paramName: string): string {
|
|
362
|
+
if (typeof value !== 'string') {
|
|
363
|
+
throw new ValidationError(
|
|
364
|
+
`Invalid ${paramName}: expected string, got ${typeof value}`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Check 0x prefix
|
|
369
|
+
if (!value.startsWith('0x')) {
|
|
370
|
+
throw new ValidationError(
|
|
371
|
+
`Invalid ${paramName}: "${value}". Expected 0x-prefixed bytes32 (66 characters total).`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Check length (0x + 64 hex chars = 66 total)
|
|
376
|
+
if (value.length !== 66) {
|
|
377
|
+
throw new ValidationError(
|
|
378
|
+
`Invalid ${paramName}: "${value}". Expected 66 characters (0x + 64 hex), got ${value.length}.`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Check hex format
|
|
383
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {
|
|
384
|
+
throw new ValidationError(
|
|
385
|
+
`Invalid ${paramName}: "${value}". Contains invalid hex characters.`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Normalize to lowercase
|
|
390
|
+
return value.toLowerCase();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Validate Unix timestamp is reasonable.
|
|
395
|
+
*
|
|
396
|
+
* SECURITY FIX (L-6): Validates timestamps to prevent overflow/underflow.
|
|
397
|
+
*
|
|
398
|
+
* @param timestamp - Unix timestamp in seconds
|
|
399
|
+
* @param paramName - Parameter name for error message
|
|
400
|
+
* @returns Validated timestamp
|
|
401
|
+
* @throws {ValidationError} If timestamp is invalid
|
|
402
|
+
*/
|
|
403
|
+
protected validateTimestamp(timestamp: number, paramName: string): number {
|
|
404
|
+
// Check it's a number
|
|
405
|
+
if (typeof timestamp !== 'number' || isNaN(timestamp)) {
|
|
406
|
+
throw new ValidationError(
|
|
407
|
+
`Invalid ${paramName}: expected number, got ${typeof timestamp}`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Check it's positive
|
|
412
|
+
if (timestamp <= 0) {
|
|
413
|
+
throw new ValidationError(
|
|
414
|
+
`Invalid ${paramName}: timestamp must be positive`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Check it's not in the past (with 1 minute buffer)
|
|
419
|
+
const now = Math.floor(Date.now() / 1000);
|
|
420
|
+
const oneMinuteAgo = now - 60;
|
|
421
|
+
|
|
422
|
+
// Only apply this check if it looks like a deadline/future timestamp
|
|
423
|
+
// (e.g., createdAt can be in the past)
|
|
424
|
+
// For now, just validate it's reasonable
|
|
425
|
+
|
|
426
|
+
// Check for overflow (year 3000 = ~32503680000)
|
|
427
|
+
const year3000 = 32503680000;
|
|
428
|
+
if (timestamp > year3000) {
|
|
429
|
+
throw new ValidationError(
|
|
430
|
+
`Invalid ${paramName}: timestamp ${timestamp} is too far in the future (overflow prevention)`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return timestamp;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Format bigint amount to human-readable string.
|
|
439
|
+
*
|
|
440
|
+
* Converts USDC wei (6 decimals) to formatted string with 2 decimal places.
|
|
441
|
+
* Uses proper rounding (round half up) for display purposes.
|
|
442
|
+
*
|
|
443
|
+
* @param amount - Amount as bigint (6 decimals) or string
|
|
444
|
+
* @returns Formatted string like "100.00 USDC"
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```typescript
|
|
448
|
+
* this.formatAmount(100_000_000n); // "100.00 USDC"
|
|
449
|
+
* this.formatAmount(100_500_000n); // "100.50 USDC"
|
|
450
|
+
* this.formatAmount(100_126_000n); // "100.13 USDC" (rounded)
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
protected formatAmount(amount: bigint | string): string {
|
|
454
|
+
const amountBigInt = typeof amount === 'string' ? BigInt(amount) : amount;
|
|
455
|
+
|
|
456
|
+
// Convert from 6 decimals to decimal string
|
|
457
|
+
const wholePart = amountBigInt / 1_000_000n;
|
|
458
|
+
const decimalPart = amountBigInt % 1_000_000n;
|
|
459
|
+
|
|
460
|
+
// M3 Fix: Proper rounding to 2 decimal places (round half up)
|
|
461
|
+
// decimalPart is 0-999999, we need to round to nearest 10000 (cents)
|
|
462
|
+
const decimalNum = Number(decimalPart);
|
|
463
|
+
const roundedCents = Math.round(decimalNum / 10000); // 6 decimals → 2 decimals
|
|
464
|
+
|
|
465
|
+
// Handle case where rounding causes overflow (e.g., 99.999999 → 100.00)
|
|
466
|
+
if (roundedCents >= 100) {
|
|
467
|
+
return `${wholePart + 1n}.00 USDC`;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const formattedDecimal = roundedCents.toString().padStart(2, '0');
|
|
471
|
+
return `${wholePart}.${formattedDecimal} USDC`;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BeginnerAdapter - High-level, opinionated API for simple use cases
|
|
3
|
+
*
|
|
4
|
+
* Provides the simplest possible interface for creating and checking transactions.
|
|
5
|
+
* Designed for developers who want to "just make it work" without deep protocol knowledge.
|
|
6
|
+
*
|
|
7
|
+
* Key Features:
|
|
8
|
+
* - Smart defaults (24h deadline, 2-day dispute window)
|
|
9
|
+
* - Inferred requester (from constructor)
|
|
10
|
+
* - User-friendly input (strings, no BigInt)
|
|
11
|
+
* - User-friendly output (formatted amounts, ISO dates)
|
|
12
|
+
*
|
|
13
|
+
* @module adapters/BeginnerAdapter
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { BaseAdapter, ValidationError, DEFAULT_DISPUTE_WINDOW_SECONDS } from './BaseAdapter';
|
|
17
|
+
import { IACTPRuntime } from '../runtime/IACTPRuntime';
|
|
18
|
+
import { TransactionState } from '../runtime/types/MockState';
|
|
19
|
+
import { EASHelper } from '../protocol/EASHelper';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parameters for creating a simple payment.
|
|
23
|
+
*
|
|
24
|
+
* This is the most beginner-friendly interface - minimal required fields.
|
|
25
|
+
*/
|
|
26
|
+
export interface BeginnerPayParams {
|
|
27
|
+
/** Recipient address (provider) */
|
|
28
|
+
to: string;
|
|
29
|
+
|
|
30
|
+
/** Amount in user-friendly format ("100", "100.50", "100 USDC", "$100") */
|
|
31
|
+
amount: string | number;
|
|
32
|
+
|
|
33
|
+
/** Optional: Deadline as relative time ("+24h") or Unix timestamp. Defaults to +24h */
|
|
34
|
+
deadline?: string | number;
|
|
35
|
+
|
|
36
|
+
/** Optional: Dispute window in seconds. Defaults to 172800 (2 days) */
|
|
37
|
+
disputeWindow?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Result of creating a payment.
|
|
42
|
+
*
|
|
43
|
+
* Provides user-friendly formatted data (not raw protocol types).
|
|
44
|
+
*/
|
|
45
|
+
export interface BeginnerPayResult {
|
|
46
|
+
/** Transaction ID (bytes32 hex string) */
|
|
47
|
+
txId: string;
|
|
48
|
+
|
|
49
|
+
/** Provider address */
|
|
50
|
+
provider: string;
|
|
51
|
+
|
|
52
|
+
/** Requester address (caller) */
|
|
53
|
+
requester: string;
|
|
54
|
+
|
|
55
|
+
/** Amount in USDC (human-readable, e.g., "100.00 USDC") */
|
|
56
|
+
amount: string;
|
|
57
|
+
|
|
58
|
+
/** Deadline as ISO 8601 timestamp */
|
|
59
|
+
deadline: string;
|
|
60
|
+
|
|
61
|
+
/** Transaction state */
|
|
62
|
+
state: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* BeginnerAdapter - High-level API for simple payment flows.
|
|
67
|
+
*
|
|
68
|
+
* This adapter provides the simplest possible interface:
|
|
69
|
+
* - `pay()` - Create and fund a transaction in one call
|
|
70
|
+
* - `checkStatus()` - Get transaction status with action hints
|
|
71
|
+
*
|
|
72
|
+
* All complexity is hidden behind smart defaults.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const client = await ACTPClient.create({ mode: 'mock' });
|
|
77
|
+
*
|
|
78
|
+
* // Simple payment (all defaults)
|
|
79
|
+
* const result = await client.beginner.pay({
|
|
80
|
+
* to: '0xProvider123',
|
|
81
|
+
* amount: '100',
|
|
82
|
+
* });
|
|
83
|
+
* console.log('Transaction ID:', result.txId);
|
|
84
|
+
* console.log('Amount:', result.amount); // "100.00 USDC"
|
|
85
|
+
*
|
|
86
|
+
* // Check status
|
|
87
|
+
* const status = await client.beginner.checkStatus(result.txId);
|
|
88
|
+
* if (status.canAccept) {
|
|
89
|
+
* console.log('Provider can accept this transaction');
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export class BeginnerAdapter extends BaseAdapter {
|
|
94
|
+
/**
|
|
95
|
+
* Creates a new BeginnerAdapter instance.
|
|
96
|
+
*
|
|
97
|
+
* @param runtime - ACTP runtime implementation (MockRuntime or BlockchainRuntime)
|
|
98
|
+
* @param requesterAddress - The requester's Ethereum address
|
|
99
|
+
* @param easHelper - Optional EAS helper for attestation verification (SECURITY FIX C-4)
|
|
100
|
+
*/
|
|
101
|
+
constructor(
|
|
102
|
+
private runtime: IACTPRuntime,
|
|
103
|
+
requesterAddress: string,
|
|
104
|
+
private easHelper?: EASHelper
|
|
105
|
+
) {
|
|
106
|
+
super(requesterAddress);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a payment transaction with smart defaults.
|
|
111
|
+
*
|
|
112
|
+
* This is the simplest way to create a transaction - just specify
|
|
113
|
+
* recipient and amount. All other parameters use sensible defaults.
|
|
114
|
+
*
|
|
115
|
+
* Smart defaults:
|
|
116
|
+
* - Requester: Inferred from constructor
|
|
117
|
+
* - Deadline: 24 hours from now
|
|
118
|
+
* - Dispute window: 2 days (172800 seconds)
|
|
119
|
+
*
|
|
120
|
+
* Validations performed:
|
|
121
|
+
* - Address format (0x-prefixed hex)
|
|
122
|
+
* - Amount format (positive number)
|
|
123
|
+
* - Deadline in future
|
|
124
|
+
* - Cannot pay yourself
|
|
125
|
+
*
|
|
126
|
+
* @param params - Payment parameters
|
|
127
|
+
* @returns User-friendly payment result
|
|
128
|
+
* @throws {ValidationError} If inputs are invalid
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const result = await adapter.pay({
|
|
133
|
+
* to: '0xProvider123',
|
|
134
|
+
* amount: '100.50',
|
|
135
|
+
* deadline: '+7d', // Optional: 7 days from now
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
async pay(params: BeginnerPayParams): Promise<BeginnerPayResult> {
|
|
140
|
+
// Validate and parse inputs
|
|
141
|
+
const provider = this.validateAddress(params.to, 'to');
|
|
142
|
+
const amount = this.parseAmount(params.amount);
|
|
143
|
+
const currentTime = this.runtime.time.now();
|
|
144
|
+
const deadline = this.parseDeadline(params.deadline, currentTime);
|
|
145
|
+
// SECURITY FIX (L-1): Validate dispute window bounds
|
|
146
|
+
const disputeWindow = this.validateDisputeWindow(params.disputeWindow);
|
|
147
|
+
|
|
148
|
+
const requester = this.requesterAddress;
|
|
149
|
+
|
|
150
|
+
// Additional validations
|
|
151
|
+
if (requester.toLowerCase() === provider.toLowerCase()) {
|
|
152
|
+
throw new ValidationError('Cannot pay yourself (requester equals provider)');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (deadline <= currentTime) {
|
|
156
|
+
throw new ValidationError('Deadline must be in the future');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Create transaction
|
|
160
|
+
const txId = await this.runtime.createTransaction({
|
|
161
|
+
provider,
|
|
162
|
+
requester,
|
|
163
|
+
amount: amount.toString(),
|
|
164
|
+
deadline,
|
|
165
|
+
disputeWindow,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Link escrow (auto-transitions to COMMITTED)
|
|
169
|
+
await this.runtime.linkEscrow(txId, amount.toString());
|
|
170
|
+
|
|
171
|
+
// Fetch transaction details for user-friendly response
|
|
172
|
+
const tx = await this.runtime.getTransaction(txId);
|
|
173
|
+
|
|
174
|
+
if (!tx) {
|
|
175
|
+
throw new Error(`Transaction ${txId} not found after creation`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
txId,
|
|
180
|
+
provider,
|
|
181
|
+
requester,
|
|
182
|
+
amount: this.formatAmount(amount),
|
|
183
|
+
deadline: new Date(deadline * 1000).toISOString(),
|
|
184
|
+
state: tx.state,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Check payment status by transaction ID.
|
|
190
|
+
*
|
|
191
|
+
* Returns current state plus action hints (what can be done next).
|
|
192
|
+
*
|
|
193
|
+
* Action hints:
|
|
194
|
+
* - `canAccept`: Provider can accept (INITIATED state, before deadline)
|
|
195
|
+
* - `canComplete`: Provider can mark as delivered (COMMITTED state)
|
|
196
|
+
* - `canDispute`: Requester can dispute (DELIVERED state, within dispute window)
|
|
197
|
+
*
|
|
198
|
+
* @param txId - Transaction ID to check
|
|
199
|
+
* @returns Status with action hints
|
|
200
|
+
* @throws {Error} If transaction not found
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const status = await adapter.checkStatus(txId);
|
|
205
|
+
* console.log('State:', status.state); // "COMMITTED"
|
|
206
|
+
* if (status.canComplete) {
|
|
207
|
+
* // Provider can deliver now
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
async checkStatus(txId: string): Promise<{
|
|
212
|
+
state: string;
|
|
213
|
+
canAccept: boolean;
|
|
214
|
+
canComplete: boolean;
|
|
215
|
+
canDispute: boolean;
|
|
216
|
+
}> {
|
|
217
|
+
const tx = await this.runtime.getTransaction(txId);
|
|
218
|
+
|
|
219
|
+
if (!tx) {
|
|
220
|
+
throw new Error(`Transaction ${txId} not found`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const now = this.runtime.time.now();
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
state: tx.state,
|
|
227
|
+
canAccept: tx.state === 'INITIATED' && tx.deadline > now,
|
|
228
|
+
canComplete: tx.state === 'COMMITTED' || tx.state === 'IN_PROGRESS',
|
|
229
|
+
canDispute: tx.state === 'DELIVERED' && tx.completedAt !== null && tx.completedAt + tx.disputeWindow > now,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|