@lemmaoracle/x402 0.1.1

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 (126) hide show
  1. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996174982.json +34 -0
  2. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996423983.json +51 -0
  3. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996907850.json +51 -0
  4. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-latest.json +51 -0
  5. package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996174982.json +7 -0
  6. package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996423983.json +7 -0
  7. package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996907850.json +7 -0
  8. package/cache/DeployPaymentVerifier.s.sol/10143/run-latest.json +7 -0
  9. package/cache/solidity-files-cache.json +1 -0
  10. package/circuits/README.md +78 -0
  11. package/circuits/payment-comprehensive.test.mjs +272 -0
  12. package/circuits/payment.circom +89 -0
  13. package/circuits/payment.test.mjs +173 -0
  14. package/dist/index.d.ts +48 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +83 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/proof-generator.d.ts +26 -0
  19. package/dist/proof-generator.d.ts.map +1 -0
  20. package/dist/proof-generator.js +154 -0
  21. package/dist/proof-generator.js.map +1 -0
  22. package/dist/proof-generator.test.d.ts +7 -0
  23. package/dist/proof-generator.test.d.ts.map +1 -0
  24. package/dist/proof-generator.test.js +121 -0
  25. package/dist/proof-generator.test.js.map +1 -0
  26. package/dist/transaction-watcher.d.ts +42 -0
  27. package/dist/transaction-watcher.d.ts.map +1 -0
  28. package/dist/transaction-watcher.js +88 -0
  29. package/dist/transaction-watcher.js.map +1 -0
  30. package/dist/transaction-watcher.test.d.ts +7 -0
  31. package/dist/transaction-watcher.test.d.ts.map +1 -0
  32. package/dist/transaction-watcher.test.js +111 -0
  33. package/dist/transaction-watcher.test.js.map +1 -0
  34. package/dist/types.d.ts +76 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +2 -0
  37. package/dist/types.js.map +1 -0
  38. package/foundry.lock +11 -0
  39. package/foundry.toml +16 -0
  40. package/lib/forge-std/.gitattributes +1 -0
  41. package/lib/forge-std/.github/CODEOWNERS +1 -0
  42. package/lib/forge-std/.github/dependabot.yml +6 -0
  43. package/lib/forge-std/.github/workflows/ci.yml +161 -0
  44. package/lib/forge-std/.github/workflows/sync.yml +36 -0
  45. package/lib/forge-std/CONTRIBUTING.md +193 -0
  46. package/lib/forge-std/LICENSE-APACHE +203 -0
  47. package/lib/forge-std/LICENSE-MIT +25 -0
  48. package/lib/forge-std/README.md +314 -0
  49. package/lib/forge-std/RELEASE_CHECKLIST.md +12 -0
  50. package/lib/forge-std/foundry.toml +18 -0
  51. package/lib/forge-std/package.json +16 -0
  52. package/lib/forge-std/scripts/vm.py +636 -0
  53. package/lib/forge-std/src/Base.sol +48 -0
  54. package/lib/forge-std/src/Config.sol +60 -0
  55. package/lib/forge-std/src/LibVariable.sol +477 -0
  56. package/lib/forge-std/src/Script.sol +28 -0
  57. package/lib/forge-std/src/StdAssertions.sol +1300 -0
  58. package/lib/forge-std/src/StdChains.sol +303 -0
  59. package/lib/forge-std/src/StdCheats.sol +825 -0
  60. package/lib/forge-std/src/StdConfig.sol +632 -0
  61. package/lib/forge-std/src/StdConstants.sol +30 -0
  62. package/lib/forge-std/src/StdError.sol +15 -0
  63. package/lib/forge-std/src/StdInvariant.sol +161 -0
  64. package/lib/forge-std/src/StdJson.sol +275 -0
  65. package/lib/forge-std/src/StdMath.sol +47 -0
  66. package/lib/forge-std/src/StdStorage.sol +476 -0
  67. package/lib/forge-std/src/StdStyle.sol +333 -0
  68. package/lib/forge-std/src/StdToml.sol +275 -0
  69. package/lib/forge-std/src/StdUtils.sol +200 -0
  70. package/lib/forge-std/src/Test.sol +32 -0
  71. package/lib/forge-std/src/Vm.sol +2533 -0
  72. package/lib/forge-std/src/console.sol +1551 -0
  73. package/lib/forge-std/src/console2.sol +4 -0
  74. package/lib/forge-std/src/interfaces/IERC1155.sol +105 -0
  75. package/lib/forge-std/src/interfaces/IERC165.sol +12 -0
  76. package/lib/forge-std/src/interfaces/IERC20.sol +43 -0
  77. package/lib/forge-std/src/interfaces/IERC4626.sol +190 -0
  78. package/lib/forge-std/src/interfaces/IERC6909.sol +72 -0
  79. package/lib/forge-std/src/interfaces/IERC721.sol +164 -0
  80. package/lib/forge-std/src/interfaces/IERC7540.sol +145 -0
  81. package/lib/forge-std/src/interfaces/IERC7575.sol +241 -0
  82. package/lib/forge-std/src/interfaces/IMulticall3.sol +68 -0
  83. package/lib/forge-std/src/safeconsole.sol +13248 -0
  84. package/lib/forge-std/test/CommonBase.t.sol +44 -0
  85. package/lib/forge-std/test/Config.t.sol +381 -0
  86. package/lib/forge-std/test/LibVariable.t.sol +452 -0
  87. package/lib/forge-std/test/StdAssertions.t.sol +141 -0
  88. package/lib/forge-std/test/StdChains.t.sol +227 -0
  89. package/lib/forge-std/test/StdCheats.t.sol +638 -0
  90. package/lib/forge-std/test/StdConstants.t.sol +38 -0
  91. package/lib/forge-std/test/StdError.t.sol +119 -0
  92. package/lib/forge-std/test/StdJson.t.sol +49 -0
  93. package/lib/forge-std/test/StdMath.t.sol +202 -0
  94. package/lib/forge-std/test/StdStorage.t.sol +532 -0
  95. package/lib/forge-std/test/StdStyle.t.sol +110 -0
  96. package/lib/forge-std/test/StdToml.t.sol +49 -0
  97. package/lib/forge-std/test/StdUtils.t.sol +342 -0
  98. package/lib/forge-std/test/Vm.t.sol +18 -0
  99. package/lib/forge-std/test/compilation/CompilationScript.sol +8 -0
  100. package/lib/forge-std/test/compilation/CompilationScriptBase.sol +8 -0
  101. package/lib/forge-std/test/compilation/CompilationTest.sol +8 -0
  102. package/lib/forge-std/test/compilation/CompilationTestBase.sol +8 -0
  103. package/lib/forge-std/test/fixtures/broadcast.log.json +187 -0
  104. package/lib/forge-std/test/fixtures/config.toml +81 -0
  105. package/lib/forge-std/test/fixtures/test.json +8 -0
  106. package/lib/forge-std/test/fixtures/test.toml +6 -0
  107. package/package.json +55 -0
  108. package/package.json.backup +55 -0
  109. package/scripts/DeployPaymentVerifier.s.sol +18 -0
  110. package/scripts/build.sh +61 -0
  111. package/scripts/deploy.sh +65 -0
  112. package/scripts/fix-verifier.mjs +64 -0
  113. package/scripts/register-circuit.ts +213 -0
  114. package/scripts/test-with-circuit.mjs +88 -0
  115. package/src/index.ts +115 -0
  116. package/src/proof-generator.test.ts +139 -0
  117. package/src/proof-generator.ts +220 -0
  118. package/src/transaction-watcher.test.ts +153 -0
  119. package/src/transaction-watcher.ts +154 -0
  120. package/src/types.ts +110 -0
  121. package/tsconfig.json +14 -0
  122. package/tsconfig.tsbuildinfo +1 -0
  123. package/vitest.config.ts +18 -0
  124. package/workers/x402-facilitator/src/index.ts +582 -0
  125. package/workers/x402-facilitator/src/x402-env.d.ts +25 -0
  126. package/workers/x402-facilitator/wrangler.toml +19 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Tests for x402 transaction watcher.
3
+ *
4
+ * Test files are exempt from FP rules.
5
+ */
6
+
7
+ import { describe, it, expect, vi } from "vitest";
8
+ import {
9
+ createTransactionWatcher,
10
+ isValidPayment,
11
+ extractPaymentDetails,
12
+ } from "./transaction-watcher.js";
13
+ import type { Config } from "./types.js";
14
+
15
+ const mockConfig: Config = {
16
+ payToAddress: "0x2222222222222222222222222222222222222222" as `0x${string}`,
17
+ network: "eip155:84532",
18
+ price: "$0.001",
19
+ ethereumRpcUrl: "https://sepolia.base.org",
20
+ chainId: 84532,
21
+ lemmaApiBase: "https://workers.lemma.workers.dev",
22
+ circuitId: "x402-payment-v1",
23
+ minAmount: 1000n,
24
+ requiredConfirmations: 6,
25
+ maxProofAge: 3600,
26
+ ipfsGateway: "https://ipfs.io/ipfs/",
27
+ };
28
+
29
+ const mockTx = {
30
+ hash: "0xabc123",
31
+ from: "0x1111111111111111111111111111111111111111",
32
+ to: "0x2222222222222222222222222222222222222222",
33
+ value: 2000n,
34
+ blockNumber: 1000n,
35
+ blockTimestamp: 1712000000n,
36
+ gasUsed: 21000n,
37
+ effectiveGasPrice: 1000000000n,
38
+ };
39
+
40
+ const mockReceipt = {
41
+ status: "success" as const,
42
+ blockNumber: 1000n,
43
+ confirmations: 6,
44
+ gasUsed: 21000n,
45
+ effectiveGasPrice: 1000000000n,
46
+ };
47
+
48
+ describe("Transaction Watcher", () => {
49
+ it("should create a watcher", () => {
50
+ const watcher = createTransactionWatcher(mockConfig, {});
51
+
52
+ expect(watcher).toBeDefined();
53
+ expect(typeof watcher.start).toBe("function");
54
+ expect(typeof watcher.stop).toBe("function");
55
+ expect(typeof watcher.onPaymentDetected).toBe("function");
56
+ expect(typeof watcher.getState).toBe("function");
57
+ });
58
+
59
+ it("should register payment detected callbacks", async () => {
60
+ const watcher = createTransactionWatcher(mockConfig, {});
61
+ const callback = vi.fn();
62
+
63
+ // Callbacks return Promises
64
+ const promise = watcher.onPaymentDetected(callback);
65
+
66
+ expect(promise).toBeInstanceOf(Promise);
67
+ await promise;
68
+ });
69
+
70
+ it("should return watcher state", () => {
71
+ const watcher = createTransactionWatcher(mockConfig, {});
72
+ const state = watcher.getState();
73
+
74
+ expect(state.isRunning).toBe(false);
75
+ expect(Array.isArray(state.detectedPayments)).toBe(true);
76
+ });
77
+ });
78
+
79
+ describe("isValidPayment", () => {
80
+ it("should validate a correct payment", () => {
81
+ const currentBlockNumber = 1010n; // Sufficient confirmations (10 confirmations)
82
+ const result = isValidPayment(mockTx as any, mockReceipt as any, mockConfig, currentBlockNumber);
83
+
84
+ expect(result).toBe(true);
85
+ });
86
+
87
+ it("should reject payment to wrong recipient", () => {
88
+ const badTx = { ...mockTx, to: "0x3333333333333333333333333333333333333333" };
89
+ const currentBlockNumber = 1010n;
90
+
91
+ const result = isValidPayment(badTx as any, mockReceipt as any, mockConfig, currentBlockNumber);
92
+
93
+ expect(result).toBe(false);
94
+ });
95
+
96
+ it("should reject payment below minimum amount", () => {
97
+ const lowTx = { ...mockTx, value: 500n };
98
+ const currentBlockNumber = 1010n;
99
+
100
+ const result = isValidPayment(lowTx as any, mockReceipt as any, mockConfig, currentBlockNumber);
101
+
102
+ expect(result).toBe(false);
103
+ });
104
+
105
+ it("should reject failed transaction", () => {
106
+ const failedReceipt = { ...mockReceipt, status: "reverted" as const };
107
+ const currentBlockNumber = 1010n;
108
+
109
+ const result = isValidPayment(mockTx as any, failedReceipt as any, mockConfig, currentBlockNumber);
110
+
111
+ expect(result).toBe(false);
112
+ });
113
+
114
+ it("should reject payment with insufficient confirmations", () => {
115
+ const currentBlockNumber = 1002n; // Only 2 confirmations (1002 - 1000 = 2)
116
+ const result = isValidPayment(mockTx as any, mockReceipt as any, mockConfig, currentBlockNumber);
117
+
118
+ expect(result).toBe(false);
119
+ });
120
+ });
121
+
122
+ describe("extractPaymentDetails", () => {
123
+ it("should extract payment details from transaction and receipt", () => {
124
+ const currentBlockNumber = 1010n;
125
+
126
+ const details = extractPaymentDetails(
127
+ mockTx as any,
128
+ mockReceipt as any,
129
+ currentBlockNumber,
130
+ );
131
+
132
+ expect(details.txHash).toBe("0xabc123");
133
+ expect(details.from).toBe("0x1111111111111111111111111111111111111111");
134
+ expect(details.to).toBe("0x2222222222222222222222222222222222222222");
135
+ expect(details.amount).toBe(2000n);
136
+ expect(details.timestamp).toBe(1712000000); // Retrieved from blockTimestamp
137
+ expect(details.blockNumber).toBe(1000n);
138
+ expect(details.confirmations).toBe(10);
139
+ });
140
+
141
+ it("should handle missing blockTimestamp", () => {
142
+ const txNoTimestamp = { ...mockTx, blockTimestamp: undefined };
143
+ const currentBlockNumber = 1010n;
144
+
145
+ const details = extractPaymentDetails(
146
+ txNoTimestamp as any,
147
+ mockReceipt as any,
148
+ currentBlockNumber,
149
+ );
150
+
151
+ expect(details.timestamp).toBe(0);
152
+ });
153
+ });
@@ -0,0 +1,154 @@
1
+ /**
2
+ * X402 Transaction Watcher
3
+ *
4
+ * Monitors the blockchain for x402 payment transactions to the configured
5
+ * recipient address and triggers proof generation when payments are detected.
6
+ */
7
+
8
+ import type {
9
+ Transaction as ViemTransaction,
10
+ TransactionReceipt,
11
+ } from "viem";
12
+ import * as R from "ramda";
13
+ import type { Config, Transaction } from "./types.js";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Types
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /** Callback invoked when a valid payment is detected */
20
+ type PaymentDetectedCallback = (
21
+ payment: Transaction,
22
+ ) => Promise<void>;
23
+
24
+ /** State of the transaction watcher */
25
+ type WatcherState = Readonly<{
26
+ isRunning: boolean;
27
+ lastCheckedBlock: bigint;
28
+ detectedPayments: ReadonlyArray<PaymentDetectedCallback>;
29
+ }>;
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Watcher Implementation
33
+ // ---------------------------------------------------------------------------
34
+
35
+ const initialState: WatcherState = {
36
+ isRunning: false,
37
+ lastCheckedBlock: 0n,
38
+ detectedPayments: [],
39
+ };
40
+
41
+ /**
42
+ * Creates a transaction watcher that monitors for payments to the configured address.
43
+ *
44
+ * Uses an immutable state cell (single-element tuple) as a controlled mutable reference.
45
+ */
46
+ const createTransactionWatcher = (
47
+ _config: Config,
48
+ _publicClient: unknown,
49
+ ): Readonly<{
50
+ start: (_?: undefined) => Promise<void>;
51
+ stop: (_?: undefined) => void;
52
+ onPaymentDetected: (callback: PaymentDetectedCallback) => Promise<void>;
53
+ getState: (_?: undefined) => WatcherState;
54
+ }> => {
55
+ const stateCell = { current: initialState };
56
+
57
+ const start = (_?: undefined): Promise<void> => {
58
+ // eslint-disable-next-line functional/no-expression-statements, functional/immutable-data
59
+ stateCell.current = {
60
+ ...stateCell.current,
61
+ isRunning: true,
62
+ lastCheckedBlock: 0n,
63
+ };
64
+
65
+ return Promise.resolve();
66
+ };
67
+
68
+ const stop = (_?: undefined): void => {
69
+ // eslint-disable-next-line functional/no-expression-statements, functional/immutable-data
70
+ stateCell.current = R.assoc("isRunning", false, stateCell.current);
71
+ };
72
+
73
+ const onPaymentDetected = (
74
+ callback: PaymentDetectedCallback,
75
+ ): Promise<void> => {
76
+ // eslint-disable-next-line functional/no-expression-statements, functional/immutable-data
77
+ stateCell.current = {
78
+ ...stateCell.current,
79
+ detectedPayments: R.append(callback, stateCell.current.detectedPayments),
80
+ };
81
+ return Promise.resolve();
82
+ };
83
+
84
+ const getState = (_?: undefined): WatcherState => stateCell.current;
85
+
86
+ return {
87
+ start,
88
+ stop,
89
+ onPaymentDetected,
90
+ getState,
91
+ };
92
+ };
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Utility Functions
96
+ // ---------------------------------------------------------------------------
97
+
98
+ /**
99
+ * Validate that a transaction is a valid x402 payment:
100
+ * - Transaction was successful
101
+ * - Sent to the correct recipient
102
+ * - Amount meets the minimum requirement
103
+ * - Has sufficient confirmations
104
+ */
105
+ const isValidPayment = (
106
+ tx: ViemTransaction,
107
+ receipt: TransactionReceipt,
108
+ config: Config,
109
+ currentBlockNumber: bigint = 0n,
110
+ ): boolean =>
111
+ R.allPass([
112
+ R.always(tx.to === config.payToAddress),
113
+ R.always(tx.value >= config.minAmount),
114
+ R.always(receipt.status === "success"),
115
+ R.always(
116
+ currentBlockNumber > 0n
117
+ ? Number(currentBlockNumber - receipt.blockNumber) >= config.requiredConfirmations
118
+ : true,
119
+ ),
120
+ ])();
121
+
122
+ /**
123
+ * Extract payment details from transaction and receipt.
124
+ */
125
+ const extractPaymentDetails = (
126
+ tx: ViemTransaction,
127
+ receipt: TransactionReceipt,
128
+ currentBlockNumber: bigint,
129
+ ): Transaction => {
130
+ const txRecord = tx as unknown as Readonly<Record<string, unknown>>;
131
+ const rawTimestamp = txRecord["blockTimestamp"];
132
+ const timestamp = typeof rawTimestamp === "bigint" ? Number(rawTimestamp) : 0;
133
+
134
+ return R.always({
135
+ txHash: tx.hash,
136
+ from: tx.from,
137
+ to: tx.to ?? ("0x" as `0x${string}`),
138
+ amount: tx.value,
139
+ timestamp,
140
+ blockNumber: receipt.blockNumber,
141
+ confirmations: Number(currentBlockNumber - receipt.blockNumber),
142
+ })();
143
+ };
144
+
145
+ export type {
146
+ PaymentDetectedCallback,
147
+ WatcherState,
148
+ };
149
+
150
+ export {
151
+ createTransactionWatcher,
152
+ isValidPayment,
153
+ extractPaymentDetails,
154
+ };
package/src/types.ts ADDED
@@ -0,0 +1,110 @@
1
+ /** Configuration for payment facilitator */
2
+ type Config = Readonly<{
3
+ /** x402 facilitator URL (for HTTPFacilitatorClient compatibility) */
4
+ facilitatorUrl?: string;
5
+
6
+ /** Address to receive payments */
7
+ payToAddress: `0x${string}`;
8
+
9
+ /** Network identifier (e.g., "eip155:84532" for Base Sepolia) */
10
+ network: string;
11
+
12
+ /** Price for accessing verified attributes (e.g., "$0.001") */
13
+ price: string;
14
+
15
+ /** Ethereum RPC URL for transaction monitoring */
16
+ ethereumRpcUrl: string;
17
+
18
+ /** Chain ID for the network */
19
+ chainId: number;
20
+
21
+ /** Lemma API base URL */
22
+ lemmaApiBase: string;
23
+
24
+ /** Lemma API key (if required) */
25
+ lemmaApiKey?: string;
26
+
27
+ /** Circuit ID for the payment proof */
28
+ circuitId: string;
29
+
30
+ /** Minimum amount in USDC smallest unit (6 decimals) */
31
+ minAmount: bigint;
32
+
33
+ /** Number of block confirmations before considering payment valid */
34
+ requiredConfirmations: number;
35
+
36
+ /** Maximum age of payment proof in seconds */
37
+ maxProofAge: number;
38
+
39
+ /** IPFS gateway URL for fetching circuit artifacts */
40
+ ipfsGateway?: string;
41
+ }>;
42
+
43
+ /** Parsed transaction details */
44
+ type Transaction = Readonly<{
45
+ /** Transaction hash */
46
+ txHash: `0x${string}`;
47
+
48
+ /** Sender address */
49
+ from: `0x${string}`;
50
+
51
+ /** Recipient address */
52
+ to: `0x${string}`;
53
+
54
+ /** Amount in smallest token unit */
55
+ amount: bigint;
56
+
57
+ /** Block timestamp */
58
+ timestamp: number;
59
+
60
+ /** Block number */
61
+ blockNumber: bigint;
62
+
63
+ /** Number of confirmations */
64
+ confirmations: number;
65
+ }>;
66
+
67
+ /** Generated proof with inputs for Lemma API */
68
+ type Proof = Readonly<{
69
+ /** ZK proof data (base64 encoded) */
70
+ proof: string;
71
+
72
+ /** Public inputs for the circuit */
73
+ inputs: ReadonlyArray<string>;
74
+
75
+ /** Circuit ID used */
76
+ circuitId: string;
77
+
78
+ /** Timestamp when proof was generated */
79
+ generatedAt: number;
80
+ }>;
81
+
82
+ /** Result of proof generation */
83
+ type ProofResult = Readonly<{
84
+ /** Transaction */
85
+ payment: Transaction;
86
+
87
+ /** Generated proof */
88
+ proof: Proof;
89
+ }>;
90
+
91
+ /** Disclosure condition for Lemma API query */
92
+ type DisclosureCondition = Readonly<{
93
+ circuitId: string;
94
+ }>;
95
+
96
+ /** Disclosure object to send with query */
97
+ type Disclosure = Readonly<{
98
+ condition?: DisclosureCondition;
99
+ proof: string;
100
+ inputs: ReadonlyArray<string>;
101
+ }>;
102
+
103
+ export type {
104
+ Config,
105
+ Transaction,
106
+ Proof,
107
+ ProofResult,
108
+ Disclosure,
109
+ DisclosureCondition,
110
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "resolveJsonModule": true
7
+ },
8
+ "include": ["src/**/*"],
9
+ "exclude": ["node_modules", "dist", "workers"],
10
+ "references": [
11
+ { "path": "../spec" },
12
+ { "path": "../sdk" }
13
+ ]
14
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/index.ts","./src/proof-generator.test.ts","./src/proof-generator.ts","./src/transaction-watcher.test.ts","./src/transaction-watcher.ts","./src/types.ts"],"version":"5.8.3"}
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ exclude: [
6
+ '**/node_modules/**',
7
+ '**/dist/**',
8
+ '**/build/**',
9
+ '**/*.test.mjs', // Exclude circuit test files
10
+ '**/*.spec.mjs',
11
+ '**/circuits/**/*.mjs', // Exclude all .mjs files in circuits directory
12
+ ],
13
+ include: [
14
+ '**/*.test.ts',
15
+ '**/*.spec.ts',
16
+ ],
17
+ },
18
+ });