@agirails/sdk 2.0.0-beta

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 (154) hide show
  1. package/README.md +183 -0
  2. package/dist/ACTPClient.d.ts +52 -0
  3. package/dist/ACTPClient.d.ts.map +1 -0
  4. package/dist/ACTPClient.js +120 -0
  5. package/dist/ACTPClient.js.map +1 -0
  6. package/dist/abi/ACTPKernel.json +1340 -0
  7. package/dist/abi/ERC20.json +38 -0
  8. package/dist/abi/EscrowVault.json +64 -0
  9. package/dist/builders/DeliveryProofBuilder.d.ts +37 -0
  10. package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -0
  11. package/dist/builders/DeliveryProofBuilder.js +165 -0
  12. package/dist/builders/DeliveryProofBuilder.js.map +1 -0
  13. package/dist/builders/QuoteBuilder.d.ts +68 -0
  14. package/dist/builders/QuoteBuilder.d.ts.map +1 -0
  15. package/dist/builders/QuoteBuilder.js +255 -0
  16. package/dist/builders/QuoteBuilder.js.map +1 -0
  17. package/dist/builders/index.d.ts +3 -0
  18. package/dist/builders/index.d.ts.map +1 -0
  19. package/dist/builders/index.js +10 -0
  20. package/dist/builders/index.js.map +1 -0
  21. package/dist/config/networks.d.ts +27 -0
  22. package/dist/config/networks.d.ts.map +1 -0
  23. package/dist/config/networks.js +103 -0
  24. package/dist/config/networks.js.map +1 -0
  25. package/dist/errors/index.d.ts +38 -0
  26. package/dist/errors/index.d.ts.map +1 -0
  27. package/dist/errors/index.js +87 -0
  28. package/dist/errors/index.js.map +1 -0
  29. package/dist/index.d.ts +19 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +68 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/protocol/ACTPKernel.d.ts +30 -0
  34. package/dist/protocol/ACTPKernel.d.ts.map +1 -0
  35. package/dist/protocol/ACTPKernel.js +261 -0
  36. package/dist/protocol/ACTPKernel.js.map +1 -0
  37. package/dist/protocol/EASHelper.d.ts +23 -0
  38. package/dist/protocol/EASHelper.d.ts.map +1 -0
  39. package/dist/protocol/EASHelper.js +106 -0
  40. package/dist/protocol/EASHelper.js.map +1 -0
  41. package/dist/protocol/EscrowVault.d.ts +24 -0
  42. package/dist/protocol/EscrowVault.d.ts.map +1 -0
  43. package/dist/protocol/EscrowVault.js +114 -0
  44. package/dist/protocol/EscrowVault.js.map +1 -0
  45. package/dist/protocol/EventMonitor.d.ts +18 -0
  46. package/dist/protocol/EventMonitor.d.ts.map +1 -0
  47. package/dist/protocol/EventMonitor.js +92 -0
  48. package/dist/protocol/EventMonitor.js.map +1 -0
  49. package/dist/protocol/MessageSigner.d.ts +23 -0
  50. package/dist/protocol/MessageSigner.d.ts.map +1 -0
  51. package/dist/protocol/MessageSigner.js +178 -0
  52. package/dist/protocol/MessageSigner.js.map +1 -0
  53. package/dist/protocol/ProofGenerator.d.ts +22 -0
  54. package/dist/protocol/ProofGenerator.d.ts.map +1 -0
  55. package/dist/protocol/ProofGenerator.js +64 -0
  56. package/dist/protocol/ProofGenerator.js.map +1 -0
  57. package/dist/protocol/QuoteBuilder.d.ts +2 -0
  58. package/dist/protocol/QuoteBuilder.d.ts.map +1 -0
  59. package/dist/protocol/QuoteBuilder.js +7 -0
  60. package/dist/protocol/QuoteBuilder.js.map +1 -0
  61. package/dist/types/eip712.d.ts +106 -0
  62. package/dist/types/eip712.d.ts.map +1 -0
  63. package/dist/types/eip712.js +84 -0
  64. package/dist/types/eip712.js.map +1 -0
  65. package/dist/types/escrow.d.ts +18 -0
  66. package/dist/types/escrow.d.ts.map +1 -0
  67. package/dist/types/escrow.js +3 -0
  68. package/dist/types/escrow.js.map +1 -0
  69. package/dist/types/index.d.ts +6 -0
  70. package/dist/types/index.d.ts.map +1 -0
  71. package/dist/types/index.js +22 -0
  72. package/dist/types/index.js.map +1 -0
  73. package/dist/types/message.d.ts +109 -0
  74. package/dist/types/message.d.ts.map +1 -0
  75. package/dist/types/message.js +3 -0
  76. package/dist/types/message.js.map +1 -0
  77. package/dist/types/state.d.ts +19 -0
  78. package/dist/types/state.d.ts.map +1 -0
  79. package/dist/types/state.js +49 -0
  80. package/dist/types/state.js.map +1 -0
  81. package/dist/types/transaction.d.ts +36 -0
  82. package/dist/types/transaction.d.ts.map +1 -0
  83. package/dist/types/transaction.js +3 -0
  84. package/dist/types/transaction.js.map +1 -0
  85. package/dist/utils/IPFSClient.d.ts +37 -0
  86. package/dist/utils/IPFSClient.d.ts.map +1 -0
  87. package/dist/utils/IPFSClient.js +128 -0
  88. package/dist/utils/IPFSClient.js.map +1 -0
  89. package/dist/utils/NonceManager.d.ts +34 -0
  90. package/dist/utils/NonceManager.d.ts.map +1 -0
  91. package/dist/utils/NonceManager.js +114 -0
  92. package/dist/utils/NonceManager.js.map +1 -0
  93. package/dist/utils/ReceivedNonceTracker.d.ts +35 -0
  94. package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -0
  95. package/dist/utils/ReceivedNonceTracker.js +196 -0
  96. package/dist/utils/ReceivedNonceTracker.js.map +1 -0
  97. package/dist/utils/canonicalJson.d.ts +4 -0
  98. package/dist/utils/canonicalJson.d.ts.map +1 -0
  99. package/dist/utils/canonicalJson.js +21 -0
  100. package/dist/utils/canonicalJson.js.map +1 -0
  101. package/dist/utils/computeTypeHash.d.ts +3 -0
  102. package/dist/utils/computeTypeHash.d.ts.map +1 -0
  103. package/dist/utils/computeTypeHash.js +30 -0
  104. package/dist/utils/computeTypeHash.js.map +1 -0
  105. package/dist/utils/validation.d.ts +6 -0
  106. package/dist/utils/validation.d.ts.map +1 -0
  107. package/dist/utils/validation.js +46 -0
  108. package/dist/utils/validation.js.map +1 -0
  109. package/package.json +73 -0
  110. package/src/ACTPClient.ts +276 -0
  111. package/src/__tests__/ProofGenerator.test.ts +124 -0
  112. package/src/__tests__/QuoteBuilder.test.ts +516 -0
  113. package/src/__tests__/StateMachine.test.ts +82 -0
  114. package/src/__tests__/builders/DeliveryProofBuilder.test.ts +581 -0
  115. package/src/__tests__/integration/ACTPClient.test.ts +263 -0
  116. package/src/__tests__/integration.test.ts +289 -0
  117. package/src/__tests__/protocol/EASHelper.test.ts +472 -0
  118. package/src/__tests__/protocol/EventMonitor.test.ts +382 -0
  119. package/src/__tests__/security/ACTPKernel.security.test.ts +1167 -0
  120. package/src/__tests__/security/EscrowVault.security.test.ts +570 -0
  121. package/src/__tests__/security/MessageSigner.security.test.ts +286 -0
  122. package/src/__tests__/security/NonceReplay.security.test.ts +501 -0
  123. package/src/__tests__/security/validation.security.test.ts +376 -0
  124. package/src/__tests__/utils/IPFSClient.test.ts +262 -0
  125. package/src/__tests__/utils/NonceManager.test.ts +205 -0
  126. package/src/__tests__/utils/canonicalJson.test.ts +153 -0
  127. package/src/abi/ACTPKernel.json +1340 -0
  128. package/src/abi/ERC20.json +40 -0
  129. package/src/abi/EscrowVault.json +66 -0
  130. package/src/builders/DeliveryProofBuilder.ts +326 -0
  131. package/src/builders/QuoteBuilder.ts +483 -0
  132. package/src/builders/index.ts +17 -0
  133. package/src/config/networks.ts +165 -0
  134. package/src/errors/index.ts +130 -0
  135. package/src/index.ts +108 -0
  136. package/src/protocol/ACTPKernel.ts +625 -0
  137. package/src/protocol/EASHelper.ts +197 -0
  138. package/src/protocol/EscrowVault.ts +237 -0
  139. package/src/protocol/EventMonitor.ts +161 -0
  140. package/src/protocol/MessageSigner.ts +336 -0
  141. package/src/protocol/ProofGenerator.ts +119 -0
  142. package/src/protocol/QuoteBuilder.ts +15 -0
  143. package/src/types/eip712.ts +175 -0
  144. package/src/types/escrow.ts +26 -0
  145. package/src/types/index.ts +10 -0
  146. package/src/types/message.ts +145 -0
  147. package/src/types/state.ts +77 -0
  148. package/src/types/transaction.ts +54 -0
  149. package/src/utils/IPFSClient.ts +248 -0
  150. package/src/utils/NonceManager.ts +293 -0
  151. package/src/utils/ReceivedNonceTracker.ts +397 -0
  152. package/src/utils/canonicalJson.ts +38 -0
  153. package/src/utils/computeTypeHash.ts +50 -0
  154. package/src/utils/validation.ts +82 -0
@@ -0,0 +1,276 @@
1
+ import { ethers, Wallet, Signer } from 'ethers';
2
+ import type { JsonRpcProvider } from 'ethers';
3
+ import { ACTPKernel } from './protocol/ACTPKernel';
4
+ import { EscrowVault } from './protocol/EscrowVault';
5
+ import { EventMonitor } from './protocol/EventMonitor';
6
+ import { ProofGenerator } from './protocol/ProofGenerator';
7
+ import { MessageSigner } from './protocol/MessageSigner';
8
+ import { QuoteBuilder } from './builders/QuoteBuilder';
9
+ import { NetworkConfig, getNetwork } from './config/networks';
10
+ import { NetworkError, ValidationError } from './errors';
11
+ import { EASHelper, EASConfig } from './protocol/EASHelper';
12
+ import { NonceManager, InMemoryNonceManager } from './utils/NonceManager';
13
+ import { IPFSClient } from './utils/IPFSClient';
14
+
15
+ /**
16
+ * ACTPClient configuration
17
+ */
18
+ export interface ACTPClientConfig {
19
+ network: 'base-sepolia' | 'base-mainnet';
20
+ privateKey?: string;
21
+ signer?: Signer;
22
+ provider?: JsonRpcProvider;
23
+ rpcUrl?: string;
24
+ contracts?: {
25
+ actpKernel?: string;
26
+ escrowVault?: string;
27
+ usdc?: string;
28
+ };
29
+ gasSettings?: {
30
+ maxFeePerGas?: bigint;
31
+ maxPriorityFeePerGas?: bigint;
32
+ };
33
+ eas?: EASConfig;
34
+ }
35
+
36
+ /**
37
+ * ACTPClient - Main entry point for ACTP SDK
38
+ *
39
+ * Example:
40
+ * ```typescript
41
+ * const client = await ACTPClient.create({
42
+ * network: 'base-sepolia',
43
+ * privateKey: process.env.PRIVATE_KEY
44
+ * });
45
+ *
46
+ * const txId = await client.kernel.createTransaction({...});
47
+ * ```
48
+ */
49
+ export class ACTPClient {
50
+ public readonly kernel: ACTPKernel;
51
+ public readonly escrow: EscrowVault;
52
+ public readonly events: EventMonitor;
53
+ public readonly proofGenerator: ProofGenerator;
54
+ public readonly messageSigner: MessageSigner;
55
+ public readonly quote: QuoteBuilder;
56
+ public readonly eas?: EASHelper;
57
+
58
+ private readonly provider: JsonRpcProvider;
59
+ private readonly signer: Signer;
60
+ private readonly networkConfig: NetworkConfig;
61
+ private readonly nonceManager: NonceManager;
62
+ private readonly ipfs?: IPFSClient;
63
+
64
+ /**
65
+ * Private constructor - use ACTPClient.create() instead
66
+ */
67
+ private constructor(config: ACTPClientConfig) {
68
+ // Validate config
69
+ this.validateConfig(config);
70
+
71
+ // Get network configuration (already cloned in getNetwork)
72
+ this.networkConfig = getNetwork(config.network);
73
+
74
+ // Apply overrides immutably (create new objects, don't mutate)
75
+ if (config.contracts) {
76
+ this.networkConfig = {
77
+ ...this.networkConfig,
78
+ contracts: {
79
+ ...this.networkConfig.contracts,
80
+ ...config.contracts
81
+ }
82
+ };
83
+ }
84
+
85
+ // Apply gas settings overrides
86
+ if (config.gasSettings) {
87
+ this.networkConfig = {
88
+ ...this.networkConfig,
89
+ gasSettings: {
90
+ ...this.networkConfig.gasSettings,
91
+ ...config.gasSettings
92
+ }
93
+ };
94
+ }
95
+
96
+ // Freeze config to prevent accidental mutation
97
+ Object.freeze(this.networkConfig.contracts);
98
+ Object.freeze(this.networkConfig.gasSettings);
99
+ Object.freeze(this.networkConfig);
100
+
101
+ // Setup provider
102
+ if (config.provider) {
103
+ this.provider = config.provider;
104
+ } else {
105
+ const rpcUrl = config.rpcUrl || this.networkConfig.rpcUrl;
106
+ this.provider = new ethers.JsonRpcProvider(rpcUrl, this.networkConfig.chainId);
107
+ }
108
+
109
+ // Setup signer
110
+ if (config.signer) {
111
+ this.signer = config.signer;
112
+ } else if (config.privateKey) {
113
+ this.signer = new Wallet(config.privateKey, this.provider);
114
+ } else {
115
+ // Attempt to derive signer from provider if possible
116
+ // In ethers v6, getSigner() is async and returns a JsonRpcSigner
117
+ throw new ValidationError('signer', 'Either privateKey or signer must be provided');
118
+ }
119
+
120
+ // Initialize shared utilities
121
+ this.nonceManager = new InMemoryNonceManager();
122
+
123
+ // Initialize IPFS client if configured
124
+ if (config.rpcUrl) {
125
+ // IPFS configuration could be added to ACTPClientConfig in the future
126
+ // For now, QuoteBuilder can work without IPFS (quotes stored only on-chain)
127
+ }
128
+
129
+ // Initialize protocol modules
130
+ this.kernel = new ACTPKernel(
131
+ this.networkConfig.contracts.actpKernel,
132
+ this.signer,
133
+ this.networkConfig.gasSettings
134
+ );
135
+
136
+ this.escrow = new EscrowVault(
137
+ this.networkConfig.contracts.escrowVault,
138
+ this.signer,
139
+ this.networkConfig.gasSettings
140
+ );
141
+
142
+ this.events = new EventMonitor(
143
+ this.kernel['contract'], // Access private contract field
144
+ this.escrow['contract']
145
+ );
146
+
147
+ this.proofGenerator = new ProofGenerator();
148
+
149
+ this.messageSigner = new MessageSigner(this.signer);
150
+
151
+ // Initialize QuoteBuilder (AIP-2)
152
+ this.quote = new QuoteBuilder(this.signer, this.nonceManager, this.ipfs);
153
+
154
+ if (config.eas) {
155
+ this.eas = new EASHelper(this.signer, config.eas);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Create and initialize ACTPClient (async factory pattern)
161
+ * Ensures all async components (EIP-712 domain) are ready before returning
162
+ */
163
+ static async create(config: ACTPClientConfig): Promise<ACTPClient> {
164
+ const client = new ACTPClient(config);
165
+
166
+ // Initialize EIP-712 domain for message signing
167
+ await client.messageSigner.initDomain(client.networkConfig.contracts.actpKernel);
168
+
169
+ return client;
170
+ }
171
+
172
+ /**
173
+ * @deprecated Use ACTPClient.create() instead
174
+ * Initialize async components (must be called after construction)
175
+ */
176
+ async initialize(): Promise<void> {
177
+ await this.messageSigner.initDomain(this.networkConfig.contracts.actpKernel);
178
+ }
179
+
180
+ /**
181
+ * Get signer address
182
+ */
183
+ async getAddress(): Promise<string> {
184
+ return await this.signer.getAddress();
185
+ }
186
+
187
+ /**
188
+ * Get network configuration
189
+ */
190
+ getNetworkConfig(): NetworkConfig {
191
+ return this.networkConfig;
192
+ }
193
+
194
+ /**
195
+ * Get provider
196
+ */
197
+ getProvider(): JsonRpcProvider {
198
+ return this.provider;
199
+ }
200
+
201
+ /**
202
+ * Get current block number
203
+ */
204
+ async getBlockNumber(): Promise<number> {
205
+ try {
206
+ return await this.provider.getBlockNumber();
207
+ } catch (error: any) {
208
+ throw new NetworkError(this.networkConfig.name, error.message);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Get gas price (ethers v6: use getFeeData instead)
214
+ */
215
+ async getGasPrice() {
216
+ try {
217
+ const feeData = await this.provider.getFeeData();
218
+ return feeData.gasPrice || 0n;
219
+ } catch (error: any) {
220
+ throw new NetworkError(this.networkConfig.name, error.message);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Release escrow with automatic attestation verification (recommended for security).
226
+ *
227
+ * SECURITY: This method verifies the attestation belongs to the transaction BEFORE
228
+ * releasing escrow. This protects against malicious providers submitting attestations
229
+ * from different transactions.
230
+ *
231
+ * ACTPKernel V1 contract accepts any attestationUID without on-chain validation.
232
+ * This SDK-side verification is the recommended protection until V2 adds on-chain checks.
233
+ *
234
+ * @param txId - Transaction ID to settle
235
+ * @param attestationUID - EAS attestation UID to verify
236
+ * @throws {Error} If EAS is not configured (client.eas is undefined)
237
+ * @throws {Error} If attestation verification fails (revoked, expired, or txId mismatch)
238
+ * @throws {TransactionRevertedError} If escrow release fails
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * // Get transaction to find attestation UID
243
+ * const tx = await client.kernel.getTransaction(txId);
244
+ *
245
+ * // Verify and release escrow in one call
246
+ * await client.releaseEscrowWithVerification(txId, tx.attestationUID);
247
+ * ```
248
+ */
249
+ async releaseEscrowWithVerification(txId: string, attestationUID: string): Promise<void> {
250
+ // Ensure EAS is configured
251
+ if (!this.eas) {
252
+ throw new Error(
253
+ 'EAS is not configured. Initialize ACTPClient with eas config or use kernel.releaseEscrow() directly (unsafe)'
254
+ );
255
+ }
256
+
257
+ // Step 1: Verify attestation belongs to this transaction
258
+ await this.eas.verifyDeliveryAttestation(txId, attestationUID);
259
+
260
+ // Step 2: Release escrow (verification passed)
261
+ await this.kernel.releaseEscrow(txId);
262
+ }
263
+
264
+ /**
265
+ * Validate configuration
266
+ */
267
+ private validateConfig(config: ACTPClientConfig): void {
268
+ if (!config.network) {
269
+ throw new ValidationError('network', 'Network is required');
270
+ }
271
+
272
+ if (!config.privateKey && !config.signer && !config.provider) {
273
+ throw new ValidationError('auth', 'Provide either privateKey, signer, or provider with signer access');
274
+ }
275
+ }
276
+ }
@@ -0,0 +1,124 @@
1
+ import { ProofGenerator } from '../protocol/ProofGenerator';
2
+
3
+ describe('ProofGenerator', () => {
4
+ let proofGenerator: ProofGenerator;
5
+
6
+ beforeEach(() => {
7
+ proofGenerator = new ProofGenerator();
8
+ });
9
+
10
+ describe('hashContent', () => {
11
+ it('should hash string content', () => {
12
+ const content = 'Hello, ACTP!';
13
+ const hash = proofGenerator.hashContent(content);
14
+
15
+ expect(hash).toMatch(/^0x[a-f0-9]{64}$/);
16
+ expect(hash).toHaveLength(66); // 0x + 64 hex chars
17
+ });
18
+
19
+ it('should hash buffer content', () => {
20
+ const content = Buffer.from('Hello, ACTP!');
21
+ const hash = proofGenerator.hashContent(content);
22
+
23
+ expect(hash).toMatch(/^0x[a-f0-9]{64}$/);
24
+ });
25
+
26
+ it('should produce same hash for same content', () => {
27
+ const content = 'Test content';
28
+ const hash1 = proofGenerator.hashContent(content);
29
+ const hash2 = proofGenerator.hashContent(content);
30
+
31
+ expect(hash1).toBe(hash2);
32
+ });
33
+
34
+ it('should produce different hashes for different content', () => {
35
+ const hash1 = proofGenerator.hashContent('Content A');
36
+ const hash2 = proofGenerator.hashContent('Content B');
37
+
38
+ expect(hash1).not.toBe(hash2);
39
+ });
40
+ });
41
+
42
+ describe('generateDeliveryProof', () => {
43
+ it('should generate valid delivery proof', () => {
44
+ const txId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
45
+ const deliverable = 'Completed translation work';
46
+
47
+ const proof = proofGenerator.generateDeliveryProof({
48
+ txId,
49
+ deliverable,
50
+ metadata: { language: 'es' }
51
+ });
52
+
53
+ expect(proof.txId).toBe(txId);
54
+ expect(proof.contentHash).toMatch(/^0x[a-f0-9]{64}$/);
55
+ expect(proof.timestamp).toBeGreaterThan(0);
56
+ expect(proof.metadata.size).toBeGreaterThan(0);
57
+ expect(proof.metadata.language).toBe('es');
58
+ });
59
+
60
+ it('should include default mimeType', () => {
61
+ const proof = proofGenerator.generateDeliveryProof({
62
+ txId: '0x1234',
63
+ deliverable: 'test'
64
+ });
65
+
66
+ expect(proof.metadata.mimeType).toBe('application/octet-stream');
67
+ });
68
+
69
+ it('should use custom mimeType', () => {
70
+ const proof = proofGenerator.generateDeliveryProof({
71
+ txId: '0x1234',
72
+ deliverable: 'test',
73
+ metadata: { mimeType: 'text/plain' }
74
+ });
75
+
76
+ expect(proof.metadata.mimeType).toBe('text/plain');
77
+ });
78
+ });
79
+
80
+ describe('verifyDeliverable', () => {
81
+ it('should verify matching deliverable', () => {
82
+ const deliverable = 'Test deliverable';
83
+ const hash = proofGenerator.hashContent(deliverable);
84
+
85
+ const isValid = proofGenerator.verifyDeliverable(deliverable, hash);
86
+ expect(isValid).toBe(true);
87
+ });
88
+
89
+ it('should reject non-matching deliverable', () => {
90
+ const deliverable = 'Test deliverable';
91
+ const wrongHash = proofGenerator.hashContent('Wrong content');
92
+
93
+ const isValid = proofGenerator.verifyDeliverable(deliverable, wrongHash);
94
+ expect(isValid).toBe(false);
95
+ });
96
+
97
+ it('should be case-insensitive for hash comparison', () => {
98
+ const deliverable = 'Test';
99
+ const hash = proofGenerator.hashContent(deliverable);
100
+ const upperHash = hash.toUpperCase();
101
+
102
+ const isValid = proofGenerator.verifyDeliverable(deliverable, upperHash);
103
+ expect(isValid).toBe(true);
104
+ });
105
+ });
106
+
107
+ describe('encodeProof and decodeProof', () => {
108
+ it('should encode and decode proof correctly', () => {
109
+ const originalProof = proofGenerator.generateDeliveryProof({
110
+ txId: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
111
+ deliverable: 'test'
112
+ });
113
+
114
+ const encoded = proofGenerator.encodeProof(originalProof);
115
+ const decoded = proofGenerator.decodeProof(encoded);
116
+
117
+ expect(decoded.txId).toBe(originalProof.txId);
118
+ expect(decoded.contentHash).toBe(originalProof.contentHash);
119
+ expect(decoded.timestamp).toBe(originalProof.timestamp);
120
+ });
121
+ });
122
+ });
123
+
124
+