@lemmaoracle/x402 0.1.1 → 0.1.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/dist/advanced/index.d.ts +14 -0
- package/dist/advanced/index.d.ts.map +1 -0
- package/dist/advanced/index.js +14 -0
- package/dist/advanced/index.js.map +1 -0
- package/dist/index.d.ts +25 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -77
- package/dist/index.js.map +1 -1
- package/dist/lemma-config.d.ts +41 -0
- package/dist/lemma-config.d.ts.map +1 -0
- package/dist/lemma-config.js +12 -0
- package/dist/lemma-config.js.map +1 -0
- package/dist/lemma-discovery.d.ts +37 -0
- package/dist/lemma-discovery.d.ts.map +1 -0
- package/dist/lemma-discovery.js +104 -0
- package/dist/lemma-discovery.js.map +1 -0
- package/dist/lemma-server.d.ts +30 -0
- package/dist/lemma-server.d.ts.map +1 -0
- package/dist/lemma-server.js +120 -0
- package/dist/lemma-server.js.map +1 -0
- package/dist/lemma-submission.d.ts +41 -0
- package/dist/lemma-submission.d.ts.map +1 -0
- package/dist/lemma-submission.js +80 -0
- package/dist/lemma-submission.js.map +1 -0
- package/package.json +13 -13
- package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996174982.json +0 -34
- package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996423983.json +0 -51
- package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996907850.json +0 -51
- package/broadcast/DeployPaymentVerifier.s.sol/10143/run-latest.json +0 -51
- package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996174982.json +0 -7
- package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996423983.json +0 -7
- package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996907850.json +0 -7
- package/cache/DeployPaymentVerifier.s.sol/10143/run-latest.json +0 -7
- package/cache/solidity-files-cache.json +0 -1
- package/circuits/README.md +0 -78
- package/circuits/payment-comprehensive.test.mjs +0 -272
- package/circuits/payment.circom +0 -89
- package/circuits/payment.test.mjs +0 -173
- package/dist/proof-generator.d.ts +0 -26
- package/dist/proof-generator.d.ts.map +0 -1
- package/dist/proof-generator.js +0 -154
- package/dist/proof-generator.js.map +0 -1
- package/dist/proof-generator.test.d.ts +0 -7
- package/dist/proof-generator.test.d.ts.map +0 -1
- package/dist/proof-generator.test.js +0 -121
- package/dist/proof-generator.test.js.map +0 -1
- package/dist/transaction-watcher.d.ts +0 -42
- package/dist/transaction-watcher.d.ts.map +0 -1
- package/dist/transaction-watcher.js +0 -88
- package/dist/transaction-watcher.js.map +0 -1
- package/dist/transaction-watcher.test.d.ts +0 -7
- package/dist/transaction-watcher.test.d.ts.map +0 -1
- package/dist/transaction-watcher.test.js +0 -111
- package/dist/transaction-watcher.test.js.map +0 -1
- package/dist/types.d.ts +0 -76
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/foundry.lock +0 -11
- package/foundry.toml +0 -16
- package/lib/forge-std/.gitattributes +0 -1
- package/lib/forge-std/.github/CODEOWNERS +0 -1
- package/lib/forge-std/.github/dependabot.yml +0 -6
- package/lib/forge-std/.github/workflows/ci.yml +0 -161
- package/lib/forge-std/.github/workflows/sync.yml +0 -36
- package/lib/forge-std/CONTRIBUTING.md +0 -193
- package/lib/forge-std/LICENSE-APACHE +0 -203
- package/lib/forge-std/LICENSE-MIT +0 -25
- package/lib/forge-std/README.md +0 -314
- package/lib/forge-std/RELEASE_CHECKLIST.md +0 -12
- package/lib/forge-std/foundry.toml +0 -18
- package/lib/forge-std/package.json +0 -16
- package/lib/forge-std/scripts/vm.py +0 -636
- package/lib/forge-std/src/Base.sol +0 -48
- package/lib/forge-std/src/Config.sol +0 -60
- package/lib/forge-std/src/LibVariable.sol +0 -477
- package/lib/forge-std/src/Script.sol +0 -28
- package/lib/forge-std/src/StdAssertions.sol +0 -1300
- package/lib/forge-std/src/StdChains.sol +0 -303
- package/lib/forge-std/src/StdCheats.sol +0 -825
- package/lib/forge-std/src/StdConfig.sol +0 -632
- package/lib/forge-std/src/StdConstants.sol +0 -30
- package/lib/forge-std/src/StdError.sol +0 -15
- package/lib/forge-std/src/StdInvariant.sol +0 -161
- package/lib/forge-std/src/StdJson.sol +0 -275
- package/lib/forge-std/src/StdMath.sol +0 -47
- package/lib/forge-std/src/StdStorage.sol +0 -476
- package/lib/forge-std/src/StdStyle.sol +0 -333
- package/lib/forge-std/src/StdToml.sol +0 -275
- package/lib/forge-std/src/StdUtils.sol +0 -200
- package/lib/forge-std/src/Test.sol +0 -32
- package/lib/forge-std/src/Vm.sol +0 -2533
- package/lib/forge-std/src/console.sol +0 -1551
- package/lib/forge-std/src/console2.sol +0 -4
- package/lib/forge-std/src/interfaces/IERC1155.sol +0 -105
- package/lib/forge-std/src/interfaces/IERC165.sol +0 -12
- package/lib/forge-std/src/interfaces/IERC20.sol +0 -43
- package/lib/forge-std/src/interfaces/IERC4626.sol +0 -190
- package/lib/forge-std/src/interfaces/IERC6909.sol +0 -72
- package/lib/forge-std/src/interfaces/IERC721.sol +0 -164
- package/lib/forge-std/src/interfaces/IERC7540.sol +0 -145
- package/lib/forge-std/src/interfaces/IERC7575.sol +0 -241
- package/lib/forge-std/src/interfaces/IMulticall3.sol +0 -68
- package/lib/forge-std/src/safeconsole.sol +0 -13248
- package/lib/forge-std/test/CommonBase.t.sol +0 -44
- package/lib/forge-std/test/Config.t.sol +0 -381
- package/lib/forge-std/test/LibVariable.t.sol +0 -452
- package/lib/forge-std/test/StdAssertions.t.sol +0 -141
- package/lib/forge-std/test/StdChains.t.sol +0 -227
- package/lib/forge-std/test/StdCheats.t.sol +0 -638
- package/lib/forge-std/test/StdConstants.t.sol +0 -38
- package/lib/forge-std/test/StdError.t.sol +0 -119
- package/lib/forge-std/test/StdJson.t.sol +0 -49
- package/lib/forge-std/test/StdMath.t.sol +0 -202
- package/lib/forge-std/test/StdStorage.t.sol +0 -532
- package/lib/forge-std/test/StdStyle.t.sol +0 -110
- package/lib/forge-std/test/StdToml.t.sol +0 -49
- package/lib/forge-std/test/StdUtils.t.sol +0 -342
- package/lib/forge-std/test/Vm.t.sol +0 -18
- package/lib/forge-std/test/compilation/CompilationScript.sol +0 -8
- package/lib/forge-std/test/compilation/CompilationScriptBase.sol +0 -8
- package/lib/forge-std/test/compilation/CompilationTest.sol +0 -8
- package/lib/forge-std/test/compilation/CompilationTestBase.sol +0 -8
- package/lib/forge-std/test/fixtures/broadcast.log.json +0 -187
- package/lib/forge-std/test/fixtures/config.toml +0 -81
- package/lib/forge-std/test/fixtures/test.json +0 -8
- package/lib/forge-std/test/fixtures/test.toml +0 -6
- package/package.json.backup +0 -55
- package/scripts/DeployPaymentVerifier.s.sol +0 -18
- package/scripts/build.sh +0 -61
- package/scripts/deploy.sh +0 -65
- package/scripts/fix-verifier.mjs +0 -64
- package/scripts/register-circuit.ts +0 -213
- package/scripts/test-with-circuit.mjs +0 -88
- package/src/index.ts +0 -115
- package/src/proof-generator.test.ts +0 -139
- package/src/proof-generator.ts +0 -220
- package/src/transaction-watcher.test.ts +0 -153
- package/src/transaction-watcher.ts +0 -154
- package/src/types.ts +0 -110
- package/tsconfig.json +0 -14
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -18
- package/workers/x402-facilitator/src/index.ts +0 -582
- package/workers/x402-facilitator/src/x402-env.d.ts +0 -25
- package/workers/x402-facilitator/wrangler.toml +0 -19
package/circuits/README.md
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# X402 Payment Circuit
|
|
2
|
-
|
|
3
|
-
This directory contains the ZK-SNARK circuit for x402 payment verification.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
- `payment.circom` - Main circuit definition
|
|
8
|
-
- `payment.test.mjs` - Test script for proof generation and verification
|
|
9
|
-
|
|
10
|
-
## Building the Circuit
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npm run build:circuit
|
|
14
|
-
# or
|
|
15
|
-
bash scripts/build.sh
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
The build script will:
|
|
19
|
-
1. Compile the circuit to R1CS, WASM, and symbol files
|
|
20
|
-
2. Download the Powers of Tau file (14th power)
|
|
21
|
-
3. Perform Groth16 trusted setup (demo mode, not secure for production)
|
|
22
|
-
4. Generate verification key and Solidity verifier contract
|
|
23
|
-
|
|
24
|
-
## Testing
|
|
25
|
-
|
|
26
|
-
To test the circuit with sample inputs:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm run test:circuit
|
|
30
|
-
# or
|
|
31
|
-
node circuits/payment.test.mjs
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
The test script:
|
|
35
|
-
- Generates a valid set of test inputs
|
|
36
|
-
- Computes the Poseidon commitment hash
|
|
37
|
-
- Generates a ZK proof using the compiled circuit
|
|
38
|
-
- Verifies the proof using the verification key
|
|
39
|
-
- Validates that the commitment matches the expected value
|
|
40
|
-
|
|
41
|
-
## Circuit Design
|
|
42
|
-
|
|
43
|
-
The X402Payment circuit verifies:
|
|
44
|
-
1. The transaction hash commitment exists
|
|
45
|
-
2. Payment was sent to the specified recipient
|
|
46
|
-
3. Payment amount meets or exceeds the required minimum
|
|
47
|
-
4. Transaction is recent enough (timestamp check)
|
|
48
|
-
|
|
49
|
-
### Inputs
|
|
50
|
-
|
|
51
|
-
**Private inputs:**
|
|
52
|
-
- `txHashPacked[2]` - Transaction hash (2x 128-bit fields = 256-bit total)
|
|
53
|
-
- `recipientLow` - Recipient address lower 128 bits
|
|
54
|
-
- `recipientHigh` - Recipient address higher 32 bits
|
|
55
|
-
- `amount` - Payment amount in USDC smallest unit (6 decimals)
|
|
56
|
-
- `timestamp` - Block timestamp of the transaction
|
|
57
|
-
- `minAmount` - Minimum required payment amount
|
|
58
|
-
|
|
59
|
-
**Public inputs:**
|
|
60
|
-
- `commitment` - Poseidon commitment to all private inputs
|
|
61
|
-
- `minAmountPublic` - Public minimum amount for verification
|
|
62
|
-
- `timestampMax` - Maximum allowed timestamp for the transaction
|
|
63
|
-
|
|
64
|
-
### Constraints
|
|
65
|
-
|
|
66
|
-
1. `minAmount === minAmountPublic` - Private min amount matches public
|
|
67
|
-
2. Commitment computed as `poseidon6(txHashPacked[0], txHashPacked[1], recipientLow, amount, timestamp, minAmount)`
|
|
68
|
-
|
|
69
|
-
## Integration with Lemma
|
|
70
|
-
|
|
71
|
-
The circuit is designed to work with Lemma's `disclosure.condition` API. Once a proof is generated, it can be used to gate access to selectively disclosed attributes behind payment verification.
|
|
72
|
-
|
|
73
|
-
## Next Steps
|
|
74
|
-
|
|
75
|
-
1. Upload the compiled circuit (WASM + zkey) to IPFS
|
|
76
|
-
2. Register the circuit with Lemma using `circuits.register()`
|
|
77
|
-
3. Integrate with the x402 proof generator service
|
|
78
|
-
4. Update the verification key and contract address in the production deployment
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Comprehensive test for payment.circom with edge cases
|
|
3
|
-
* Usage: node payment-comprehensive.test.mjs
|
|
4
|
-
*/
|
|
5
|
-
import * as snarkjs from "snarkjs";
|
|
6
|
-
import { readFileSync, existsSync } from "fs";
|
|
7
|
-
import { poseidon6 } from "poseidon-lite";
|
|
8
|
-
|
|
9
|
-
// File path configuration
|
|
10
|
-
const WASM_PATH = "build/payment_js/payment.wasm";
|
|
11
|
-
const ZKEY_PATH = "build/payment_final.zkey";
|
|
12
|
-
const VKEY_PATH = "build/verification_key.json";
|
|
13
|
-
|
|
14
|
-
// テストケース
|
|
15
|
-
const TEST_CASES = [
|
|
16
|
-
{
|
|
17
|
-
name: "Basic valid payment",
|
|
18
|
-
description: "Minimum amount exactly matches requirement",
|
|
19
|
-
inputs: () => {
|
|
20
|
-
const txHash = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn;
|
|
21
|
-
const recipient = 0x2222222222222222222222222222222222222222n;
|
|
22
|
-
const amount = 1000n;
|
|
23
|
-
const minAmount = 1000n;
|
|
24
|
-
const timestamp = 1712000000n;
|
|
25
|
-
const timestampMax = 1712003600n;
|
|
26
|
-
|
|
27
|
-
const txHashUpper = txHash >> 128n;
|
|
28
|
-
const txHashLower = txHash & ((1n << 128n) - 1n);
|
|
29
|
-
const recipientLow = recipient & ((1n << 128n) - 1n);
|
|
30
|
-
const recipientHigh = recipient >> 128n;
|
|
31
|
-
|
|
32
|
-
const commitment = poseidon6([
|
|
33
|
-
txHashUpper, txHashLower, recipientLow, amount, timestamp, minAmount
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
private: {
|
|
38
|
-
txHashPacked: [txHashUpper.toString(), txHashLower.toString()],
|
|
39
|
-
recipientLow: recipientLow.toString(),
|
|
40
|
-
recipientHigh: recipientHigh.toString(),
|
|
41
|
-
amount: amount.toString(),
|
|
42
|
-
timestamp: timestamp.toString(),
|
|
43
|
-
minAmount: minAmount.toString(),
|
|
44
|
-
},
|
|
45
|
-
public: {
|
|
46
|
-
commitment: commitment.toString(),
|
|
47
|
-
minAmountPublic: minAmount.toString(),
|
|
48
|
-
timestampMax: timestampMax.toString()
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
shouldPass: true
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: "Payment above minimum",
|
|
56
|
-
description: "Amount is greater than minimum requirement",
|
|
57
|
-
inputs: () => {
|
|
58
|
-
const txHash = 0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890n;
|
|
59
|
-
const recipient = 0x3333333333333333333333333333333333333333n;
|
|
60
|
-
const amount = 5000n;
|
|
61
|
-
const minAmount = 1000n;
|
|
62
|
-
const timestamp = 1712001000n;
|
|
63
|
-
const timestampMax = 1712004600n;
|
|
64
|
-
|
|
65
|
-
const txHashUpper = txHash >> 128n;
|
|
66
|
-
const txHashLower = txHash & ((1n << 128n) - 1n);
|
|
67
|
-
const recipientLow = recipient & ((1n << 128n) - 1n);
|
|
68
|
-
const recipientHigh = recipient >> 128n;
|
|
69
|
-
|
|
70
|
-
const commitment = poseidon6([
|
|
71
|
-
txHashUpper, txHashLower, recipientLow, amount, timestamp, minAmount
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
private: {
|
|
76
|
-
txHashPacked: [txHashUpper.toString(), txHashLower.toString()],
|
|
77
|
-
recipientLow: recipientLow.toString(),
|
|
78
|
-
recipientHigh: recipientHigh.toString(),
|
|
79
|
-
amount: amount.toString(),
|
|
80
|
-
timestamp: timestamp.toString(),
|
|
81
|
-
minAmount: minAmount.toString(),
|
|
82
|
-
},
|
|
83
|
-
public: {
|
|
84
|
-
commitment: commitment.toString(),
|
|
85
|
-
minAmountPublic: minAmount.toString(),
|
|
86
|
-
timestampMax: timestampMax.toString()
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
},
|
|
90
|
-
shouldPass: true
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
name: "Wrong commitment",
|
|
94
|
-
description: "Incorrect commitment hash should fail verification",
|
|
95
|
-
inputs: () => {
|
|
96
|
-
const txHash = 0x1111111111111111111111111111111111111111111111111111111111111111n;
|
|
97
|
-
const recipient = 0x4444444444444444444444444444444444444444n;
|
|
98
|
-
const amount = 1000n;
|
|
99
|
-
const minAmount = 1000n;
|
|
100
|
-
const timestamp = 1712002000n;
|
|
101
|
-
const timestampMax = 1712005600n;
|
|
102
|
-
|
|
103
|
-
const txHashUpper = txHash >> 128n;
|
|
104
|
-
const txHashLower = txHash & ((1n << 128n) - 1n);
|
|
105
|
-
const recipientLow = recipient & ((1n << 128n) - 1n);
|
|
106
|
-
const recipientHigh = recipient >> 128n;
|
|
107
|
-
|
|
108
|
-
// Use wrong commitment (off by one)
|
|
109
|
-
const wrongCommitment = poseidon6([
|
|
110
|
-
txHashUpper, txHashLower, recipientLow, amount, timestamp, minAmount + 1n
|
|
111
|
-
]);
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
private: {
|
|
115
|
-
txHashPacked: [txHashUpper.toString(), txHashLower.toString()],
|
|
116
|
-
recipientLow: recipientLow.toString(),
|
|
117
|
-
recipientHigh: recipientHigh.toString(),
|
|
118
|
-
amount: amount.toString(),
|
|
119
|
-
timestamp: timestamp.toString(),
|
|
120
|
-
minAmount: minAmount.toString(),
|
|
121
|
-
},
|
|
122
|
-
public: {
|
|
123
|
-
commitment: wrongCommitment.toString(),
|
|
124
|
-
minAmountPublic: minAmount.toString(),
|
|
125
|
-
timestampMax: timestampMax.toString()
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
},
|
|
129
|
-
shouldPass: false
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: "Mismatched minAmount",
|
|
133
|
-
description: "Private and public minAmount don't match",
|
|
134
|
-
inputs: () => {
|
|
135
|
-
const txHash = 0x2222222222222222222222222222222222222222222222222222222222222222n;
|
|
136
|
-
const recipient = 0x5555555555555555555555555555555555555555n;
|
|
137
|
-
const amount = 2000n;
|
|
138
|
-
const minAmountPrivate = 1000n;
|
|
139
|
-
const minAmountPublic = 1500n; // Different from private!
|
|
140
|
-
const timestamp = 1712003000n;
|
|
141
|
-
const timestampMax = 1712006600n;
|
|
142
|
-
|
|
143
|
-
const txHashUpper = txHash >> 128n;
|
|
144
|
-
const txHashLower = txHash & ((1n << 128n) - 1n);
|
|
145
|
-
const recipientLow = recipient & ((1n << 128n) - 1n);
|
|
146
|
-
const recipientHigh = recipient >> 128n;
|
|
147
|
-
|
|
148
|
-
// Commitment uses private minAmount
|
|
149
|
-
const commitment = poseidon6([
|
|
150
|
-
txHashUpper, txHashLower, recipientLow, amount, timestamp, minAmountPrivate
|
|
151
|
-
]);
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
private: {
|
|
155
|
-
txHashPacked: [txHashUpper.toString(), txHashLower.toString()],
|
|
156
|
-
recipientLow: recipientLow.toString(),
|
|
157
|
-
recipientHigh: recipientHigh.toString(),
|
|
158
|
-
amount: amount.toString(),
|
|
159
|
-
timestamp: timestamp.toString(),
|
|
160
|
-
minAmount: minAmountPrivate.toString(),
|
|
161
|
-
},
|
|
162
|
-
public: {
|
|
163
|
-
commitment: commitment.toString(),
|
|
164
|
-
minAmountPublic: minAmountPublic.toString(),
|
|
165
|
-
timestampMax: timestampMax.toString()
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
},
|
|
169
|
-
shouldPass: false
|
|
170
|
-
}
|
|
171
|
-
];
|
|
172
|
-
|
|
173
|
-
// Check if build files exist
|
|
174
|
-
function checkBuildFiles() {
|
|
175
|
-
const files = [WASM_PATH, ZKEY_PATH, VKEY_PATH];
|
|
176
|
-
const missingFiles = files.filter(file => !existsSync(file));
|
|
177
|
-
|
|
178
|
-
if (missingFiles.length > 0) {
|
|
179
|
-
console.error("❌ Missing build files:");
|
|
180
|
-
missingFiles.forEach(file => console.error(` - ${file}`));
|
|
181
|
-
console.error("\n⚠️ Please run the build script first:");
|
|
182
|
-
console.error(" npm run build:circuit");
|
|
183
|
-
console.error(" or");
|
|
184
|
-
console.error(" bash scripts/build.sh");
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async function runTestCase(testCase) {
|
|
192
|
-
console.log(`\n=== ${testCase.name} ===`);
|
|
193
|
-
console.log(`Description: ${testCase.description}`);
|
|
194
|
-
|
|
195
|
-
const { private: privateInputs, public: publicInputs } = testCase.inputs();
|
|
196
|
-
const input = { ...privateInputs, ...publicInputs };
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
console.log("Generating proof...");
|
|
200
|
-
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
|
201
|
-
input,
|
|
202
|
-
WASM_PATH,
|
|
203
|
-
ZKEY_PATH
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
const vKey = JSON.parse(readFileSync(VKEY_PATH, "utf8"));
|
|
207
|
-
const valid = await snarkjs.groth16.verify(vKey, publicSignals, proof);
|
|
208
|
-
|
|
209
|
-
const passed = valid === testCase.shouldPass;
|
|
210
|
-
|
|
211
|
-
if (passed) {
|
|
212
|
-
console.log(`✅ ${testCase.shouldPass ? "Proof is VALID as expected" : "Proof is INVALID as expected"}`);
|
|
213
|
-
return true;
|
|
214
|
-
} else {
|
|
215
|
-
console.log(`❌ Unexpected result:`);
|
|
216
|
-
console.log(` Expected: ${testCase.shouldPass ? "valid" : "invalid"}`);
|
|
217
|
-
console.log(` Got: ${valid ? "valid" : "invalid"}`);
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
} catch (error) {
|
|
222
|
-
// If test case should fail and we get an error, that might be expected
|
|
223
|
-
if (!testCase.shouldPass && error.message.includes("Error in template")) {
|
|
224
|
-
console.log(`✅ Circuit rejected invalid inputs as expected`);
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
console.log(`❌ Error: ${error.message}`);
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async function main() {
|
|
234
|
-
console.log("=== X402 Payment Circuit Comprehensive Test ===\n");
|
|
235
|
-
|
|
236
|
-
// Check build files
|
|
237
|
-
if (!checkBuildFiles()) {
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
let passedCount = 0;
|
|
242
|
-
const totalCount = TEST_CASES.length;
|
|
243
|
-
|
|
244
|
-
for (const testCase of TEST_CASES) {
|
|
245
|
-
const passed = await runTestCase(testCase);
|
|
246
|
-
if (passed) passedCount++;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
console.log("\n=== Summary ===");
|
|
250
|
-
console.log(`Passed: ${passedCount}/${totalCount}`);
|
|
251
|
-
|
|
252
|
-
if (passedCount === totalCount) {
|
|
253
|
-
console.log("✅ All tests passed!");
|
|
254
|
-
process.exit(0);
|
|
255
|
-
} else {
|
|
256
|
-
console.log(`❌ ${totalCount - passedCount} test(s) failed`);
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Execute main function
|
|
262
|
-
main().catch(error => {
|
|
263
|
-
console.error("Unhandled error:", error);
|
|
264
|
-
process.exit(1);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// Fallback to force process exit
|
|
268
|
-
// snarkjs may leave background processes running
|
|
269
|
-
setTimeout(() => {
|
|
270
|
-
console.warn("⚠️ Force exiting process after timeout");
|
|
271
|
-
process.exit(0);
|
|
272
|
-
}, 60000); // Force exit after 60 seconds (comprehensive tests may take longer)
|
package/circuits/payment.circom
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
pragma circom 2.1.0;
|
|
2
|
-
|
|
3
|
-
include "circomlib/circuits/poseidon.circom";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* X402Payment — Payment verification circuit for x402 micropayments.
|
|
7
|
-
*
|
|
8
|
-
* Proves that the prover knows a valid x402 payment transaction
|
|
9
|
-
* with the specified recipient, amount, and timestamp. This circuit
|
|
10
|
-
* is used with Lemma's disclosure.condition API to gate access to
|
|
11
|
-
* selectively disclosed attributes behind payment verification.
|
|
12
|
-
*
|
|
13
|
-
* The circuit verifies:
|
|
14
|
-
* 1. The transaction hash commitment exists
|
|
15
|
-
* 2. Payment was sent to the specified recipient
|
|
16
|
-
* 3. Payment amount meets or exceeds the required minimum
|
|
17
|
-
* 4. Transaction is recent enough (timestamp check)
|
|
18
|
-
*
|
|
19
|
-
* Private inputs:
|
|
20
|
-
* txHashPacked[2] Transaction hash (2x 128-bit fields = 256-bit total)
|
|
21
|
-
* recipientLow Recipient address lower 128 bits
|
|
22
|
-
* recipientHigh Recipient address higher 32 bits
|
|
23
|
-
* amount Payment amount in USDC smallest unit (6 decimals)
|
|
24
|
-
* timestamp Block timestamp of the transaction
|
|
25
|
-
* minAmount Minimum required payment amount
|
|
26
|
-
*
|
|
27
|
-
* Public inputs:
|
|
28
|
-
* commitment Poseidon commitment to all private inputs
|
|
29
|
-
* minAmountPublic Public minimum amount for verification
|
|
30
|
-
* timestampMax Maximum allowed timestamp for the transaction
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
template X402Payment() {
|
|
34
|
-
// ── Private inputs ──────────────────────────────────────────────
|
|
35
|
-
// Transaction hash (256-bit hash split into 2 field elements)
|
|
36
|
-
signal input txHashPacked[2];
|
|
37
|
-
|
|
38
|
-
// Recipient address (160-bit split into 2 field elements for simplicity)
|
|
39
|
-
signal input recipientLow;
|
|
40
|
-
signal input recipientHigh;
|
|
41
|
-
|
|
42
|
-
// Payment amount in smallest unit
|
|
43
|
-
signal input amount;
|
|
44
|
-
|
|
45
|
-
// Transaction timestamp
|
|
46
|
-
signal input timestamp;
|
|
47
|
-
|
|
48
|
-
// Minimum amount requirement (private, matched with public)
|
|
49
|
-
signal input minAmount;
|
|
50
|
-
|
|
51
|
-
// ── Public inputs ───────────────────────────────────────────────
|
|
52
|
-
signal input commitment;
|
|
53
|
-
|
|
54
|
-
// Public minimum amount for verification
|
|
55
|
-
signal input minAmountPublic;
|
|
56
|
-
|
|
57
|
-
// Maximum allowed timestamp (freshness check)
|
|
58
|
-
signal input timestampMax;
|
|
59
|
-
|
|
60
|
-
// ── Constraints ─────────────────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
// 1. Verify minAmount matches public minAmountPublic
|
|
63
|
-
minAmount === minAmountPublic;
|
|
64
|
-
|
|
65
|
-
// 2. Verify amount >= minAmount
|
|
66
|
-
// Since we can't do direct comparison in Circom, we use:
|
|
67
|
-
// amount - minAmount must be a valid field element (non-negative)
|
|
68
|
-
// The constraint amount === minAmount + delta ensures amount >= minAmount
|
|
69
|
-
// We'll verify this externally and just prove knowledge of valid values
|
|
70
|
-
|
|
71
|
-
// 3. Verify timestamp is within acceptable range
|
|
72
|
-
// timestamp <= timestampMax (freshness check)
|
|
73
|
-
// We use: timestampMax - timestamp must be non-negative
|
|
74
|
-
// Similar to above, we rely on range proofs outside circuit for this check
|
|
75
|
-
|
|
76
|
-
// 4. Create commitment from all inputs
|
|
77
|
-
// Using 6 inputs to poseidon for complete binding
|
|
78
|
-
component hasher = Poseidon(6);
|
|
79
|
-
hasher.inputs[0] <== txHashPacked[0];
|
|
80
|
-
hasher.inputs[1] <== txHashPacked[1];
|
|
81
|
-
hasher.inputs[2] <== recipientLow;
|
|
82
|
-
hasher.inputs[3] <== amount;
|
|
83
|
-
hasher.inputs[4] <== timestamp;
|
|
84
|
-
hasher.inputs[5] <== minAmount;
|
|
85
|
-
|
|
86
|
-
commitment === hasher.out;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
component main {public [commitment, minAmountPublic, timestampMax]} = X402Payment();
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local test for payment.circom proof generation and verification
|
|
3
|
-
* Usage: node payment.test.mjs
|
|
4
|
-
*/
|
|
5
|
-
import * as snarkjs from "snarkjs";
|
|
6
|
-
import { readFileSync, existsSync } from "fs";
|
|
7
|
-
import { poseidon6 } from "poseidon-lite"; // npm install poseidon-lite
|
|
8
|
-
|
|
9
|
-
// File path configuration
|
|
10
|
-
const WASM_PATH = "build/payment_js/payment.wasm";
|
|
11
|
-
const ZKEY_PATH = "build/payment_final.zkey";
|
|
12
|
-
const VKEY_PATH = "build/verification_key.json";
|
|
13
|
-
|
|
14
|
-
// Generate test input data
|
|
15
|
-
function generateTestInputs() {
|
|
16
|
-
// Test transaction hash (256-bit)
|
|
17
|
-
const txHash = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn;
|
|
18
|
-
|
|
19
|
-
// Recipient address (160-bit)
|
|
20
|
-
const recipient = 0x2222222222222222222222222222222222222222n;
|
|
21
|
-
|
|
22
|
-
// Payment amount (smallest unit)
|
|
23
|
-
const amount = 1000n;
|
|
24
|
-
|
|
25
|
-
// Timestamp
|
|
26
|
-
const timestamp = 1712000000n;
|
|
27
|
-
|
|
28
|
-
// Minimum amount requirement
|
|
29
|
-
const minAmount = 1000n;
|
|
30
|
-
|
|
31
|
-
// Maximum timestamp (freshness check)
|
|
32
|
-
const timestampMax = 1712003600n; // 1 hour later
|
|
33
|
-
|
|
34
|
-
// Split 256-bit transaction hash into two 128-bit field elements
|
|
35
|
-
const txHashUpper = txHash >> 128n;
|
|
36
|
-
const txHashLower = txHash & ((1n << 128n) - 1n);
|
|
37
|
-
|
|
38
|
-
// Split 160-bit recipient address into two field elements
|
|
39
|
-
const recipientLow = recipient & ((1n << 128n) - 1n);
|
|
40
|
-
const recipientHigh = recipient >> 128n;
|
|
41
|
-
|
|
42
|
-
// Compute commitment (Poseidon(6) hash)
|
|
43
|
-
const commitment = poseidon6([
|
|
44
|
-
txHashUpper,
|
|
45
|
-
txHashLower,
|
|
46
|
-
recipientLow,
|
|
47
|
-
amount,
|
|
48
|
-
timestamp,
|
|
49
|
-
minAmount
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
// Private inputs
|
|
54
|
-
txHashPacked: [txHashUpper.toString(), txHashLower.toString()],
|
|
55
|
-
recipientLow: recipientLow.toString(),
|
|
56
|
-
recipientHigh: recipientHigh.toString(),
|
|
57
|
-
amount: amount.toString(),
|
|
58
|
-
timestamp: timestamp.toString(),
|
|
59
|
-
minAmount: minAmount.toString(),
|
|
60
|
-
|
|
61
|
-
// Public inputs
|
|
62
|
-
commitment: commitment.toString(),
|
|
63
|
-
minAmountPublic: minAmount.toString(),
|
|
64
|
-
timestampMax: timestampMax.toString()
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Check if build files exist
|
|
69
|
-
function checkBuildFiles() {
|
|
70
|
-
const files = [WASM_PATH, ZKEY_PATH, VKEY_PATH];
|
|
71
|
-
const missingFiles = files.filter(file => !existsSync(file));
|
|
72
|
-
|
|
73
|
-
if (missingFiles.length > 0) {
|
|
74
|
-
console.error("❌ Missing build files:");
|
|
75
|
-
missingFiles.forEach(file => console.error(` - ${file}`));
|
|
76
|
-
console.error("\n⚠️ Please run the build script first:");
|
|
77
|
-
console.error(" npm run build:circuit");
|
|
78
|
-
console.error(" or");
|
|
79
|
-
console.error(" bash scripts/build.sh");
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function main() {
|
|
87
|
-
console.log("=== X402 Payment Circuit Test ===\n");
|
|
88
|
-
|
|
89
|
-
// Check build files
|
|
90
|
-
if (!checkBuildFiles()) {
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Generate test input data
|
|
95
|
-
const input = generateTestInputs();
|
|
96
|
-
|
|
97
|
-
console.log("Generated test inputs:");
|
|
98
|
-
console.log("- Commitment:", input.commitment);
|
|
99
|
-
console.log("- Min Amount:", input.minAmountPublic);
|
|
100
|
-
console.log("- Timestamp Max:", input.timestampMax);
|
|
101
|
-
console.log("- Amount:", input.amount);
|
|
102
|
-
console.log("- Timestamp:", input.timestamp);
|
|
103
|
-
console.log();
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
console.log("Generating proof...");
|
|
107
|
-
|
|
108
|
-
// Generate proof
|
|
109
|
-
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
|
110
|
-
input,
|
|
111
|
-
WASM_PATH,
|
|
112
|
-
ZKEY_PATH
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
console.log("Proof generated successfully!");
|
|
116
|
-
console.log("Public signals:", publicSignals);
|
|
117
|
-
console.log();
|
|
118
|
-
|
|
119
|
-
// Load verification key
|
|
120
|
-
const vKey = JSON.parse(readFileSync(VKEY_PATH, "utf8"));
|
|
121
|
-
|
|
122
|
-
// Verify proof
|
|
123
|
-
const valid = await snarkjs.groth16.verify(vKey, publicSignals, proof);
|
|
124
|
-
|
|
125
|
-
if (valid) {
|
|
126
|
-
console.log("✅ Proof is VALID!");
|
|
127
|
-
console.log("\nCircuit verification successful!");
|
|
128
|
-
|
|
129
|
-
// Additional verification: check if commitment matches
|
|
130
|
-
if (publicSignals[0] === input.commitment) {
|
|
131
|
-
console.log("✅ Commitment matches expected value!");
|
|
132
|
-
} else {
|
|
133
|
-
console.log("⚠️ Commitment mismatch:");
|
|
134
|
-
console.log(` Expected: ${input.commitment}`);
|
|
135
|
-
console.log(` Got: ${publicSignals[0]}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Explicitly exit
|
|
139
|
-
console.log("\n✅ All tests completed successfully!");
|
|
140
|
-
process.exit(0);
|
|
141
|
-
} else {
|
|
142
|
-
console.log("❌ Proof is INVALID!");
|
|
143
|
-
console.log("\nCircuit verification failed!");
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error("❌ Error during proof generation/verification:");
|
|
149
|
-
console.error(error.message);
|
|
150
|
-
|
|
151
|
-
if (error.message.includes("ENOENT") || error.message.includes("no such file")) {
|
|
152
|
-
console.error("\n⚠️ Build files not found. Please run the build script first:");
|
|
153
|
-
console.error(" npm run build:circuit");
|
|
154
|
-
console.error(" or");
|
|
155
|
-
console.error(" bash scripts/build.sh");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
process.exit(1);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Execute main function
|
|
163
|
-
main().catch(error => {
|
|
164
|
-
console.error("Unhandled error:", error);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Fallback to force process exit
|
|
169
|
-
// snarkjs may leave background processes running
|
|
170
|
-
setTimeout(() => {
|
|
171
|
-
console.warn("⚠️ Force exiting process after timeout");
|
|
172
|
-
process.exit(0);
|
|
173
|
-
}, 30000); // Force exit after 30 seconds
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* X402 Payment Proof Generator
|
|
3
|
-
*
|
|
4
|
-
* Generates ZK proofs for x402 payment transactions so that
|
|
5
|
-
* Lemma's disclosure.condition API can verify payments without
|
|
6
|
-
* exposing transaction details.
|
|
7
|
-
*/
|
|
8
|
-
import { type LemmaClient } from "@lemmaoracle/sdk";
|
|
9
|
-
import type { ProofResult, Config, Transaction, Proof } from "./types.js";
|
|
10
|
-
/**
|
|
11
|
-
* Generates x402 payment proofs using Lemma's prover infrastructure.
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* const generator = new X402ProofGenerator(config, lemmaClient);
|
|
15
|
-
* const result = await generator.generateProof(txHash);
|
|
16
|
-
*/
|
|
17
|
-
declare const createProofGenerator: (config: Config, lemmaClient: LemmaClient) => Readonly<{
|
|
18
|
-
generateProof: (txHash: `0x${string}`) => Promise<ProofResult>;
|
|
19
|
-
toFieldElements: (payment: Transaction, config: Config) => Readonly<{
|
|
20
|
-
witness: Record<string, string>;
|
|
21
|
-
publicInputs: string[];
|
|
22
|
-
}>;
|
|
23
|
-
}>;
|
|
24
|
-
export type { ProofResult, Config, Proof, Transaction, };
|
|
25
|
-
export { createProofGenerator };
|
|
26
|
-
//# sourceMappingURL=proof-generator.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"proof-generator.d.ts","sourceRoot":"","sources":["../src/proof-generator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,WAAW,EACX,KAAK,EACN,MAAM,YAAY,CAAC;AAmBpB;;;;;;GAMG;AACH,QAAA,MAAM,oBAAoB,GACxB,QAAQ,MAAM,EACd,aAAa,WAAW,KACvB,QAAQ,CAAC;IACV,aAAa,EAAE,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/D,eAAe,EAAE,CACf,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,KACX,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC5E,CAqEA,CAAC;AA4FF,YAAY,EACV,WAAW,EACX,MAAM,EACN,KAAK,EACL,WAAW,GACZ,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|