@ilalv3/cli 0.2.9 → 0.2.11
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/README.md +30 -2
- package/dist/commands/credential.d.ts +2 -2
- package/dist/commands/credential.js +22 -12
- package/dist/commands/demo.js +8 -9
- package/dist/commands/deploy.js +2 -4
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +6 -0
- package/dist/commands/liquidity.js +3 -5
- package/dist/commands/mint.d.ts +2 -2
- package/dist/commands/mint.js +35 -22
- package/dist/commands/oracle.js +2 -4
- package/dist/commands/pool.js +2 -6
- package/dist/commands/proof.js +2 -4
- package/dist/commands/prove.d.ts +3 -0
- package/dist/commands/prove.js +103 -20
- package/dist/commands/session.js +2 -6
- package/dist/commands/swap.js +3 -5
- package/dist/config.d.ts +2 -0
- package/dist/config.js +4 -0
- package/dist/index.js +13 -8
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,7 +94,35 @@ PRIVATE_KEY=0x... ilal credential prove \
|
|
|
94
94
|
--expires-at 1800000000
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
The CLI automatically prepares proving artifacts:
|
|
98
|
+
|
|
99
|
+
1. Use `--circuit-dir` when a local `circuits/build` directory exists.
|
|
100
|
+
2. Otherwise use the local cache at `~/.ilal/artifacts/ilal-v1`.
|
|
101
|
+
3. If the cache is empty, download hosted artifacts from the ILAL release CDN.
|
|
102
|
+
|
|
103
|
+
No institution needs to compile Circom in its backend. The flow generates a Groth16 proof locally (~5s), verifies it on-chain, and mints/renews your CNF without revealing identity. If the Merkle root does not match, the issuer/operator must queue the updated root with `ilal oracle propose-root --root <newRoot>` and activate it after the timelock.
|
|
104
|
+
|
|
105
|
+
Advanced artifact controls:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Use a custom enterprise artifact mirror
|
|
109
|
+
PRIVATE_KEY=0x... ilal credential prove \
|
|
110
|
+
--wallet 0xYourWallet \
|
|
111
|
+
--artifact-url https://zk-artifacts.yourdomain.example/ilal-v1
|
|
112
|
+
|
|
113
|
+
# Pre-seeded/offline mode
|
|
114
|
+
PRIVATE_KEY=0x... ilal credential prove \
|
|
115
|
+
--wallet 0xYourWallet \
|
|
116
|
+
--artifact-cache /opt/ilal/artifacts/ilal-v1 \
|
|
117
|
+
--offline
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Equivalent environment variables:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
ILAL_ARTIFACT_BASE_URL=https://zk-artifacts.yourdomain.example/ilal-v1
|
|
124
|
+
ILAL_ARTIFACT_CACHE=/opt/ilal/artifacts/ilal-v1
|
|
125
|
+
```
|
|
98
126
|
|
|
99
127
|
## Command reference
|
|
100
128
|
|
|
@@ -103,7 +131,7 @@ Generates a Groth16 proof locally (~5s), verifies it on-chain, and mints/renews
|
|
|
103
131
|
| `ilal init` | Create `.ilal.json` with contract addresses |
|
|
104
132
|
| `ilal status` | Dashboard: credential · issuer config · pool policy |
|
|
105
133
|
| `ilal credential zk-root` | Operator helper: compute the ZK Merkle root for a demo wallet/expiry |
|
|
106
|
-
| `ilal credential prove` | Trader flow:
|
|
134
|
+
| `ilal credential prove` | Trader flow: hosted/cached ZK artifacts → local proof → mint or renew CNF |
|
|
107
135
|
| `ilal credential mint` | Mint CNF via Coinbase EAS attestation |
|
|
108
136
|
| `ilal credential renew` | Renew CNF via EAS attestation |
|
|
109
137
|
| `ilal swap` | Compliant swap via ILALRouter with optional `--min-amount-out` |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createPublicClient, http, isAddress } from "viem";
|
|
2
2
|
import { base, baseSepolia } from "viem/chains";
|
|
3
3
|
import { fmt, log, die } from "../ui.js";
|
|
4
|
+
import { withConfig } from "../config.js";
|
|
4
5
|
const CNF_ABI = [
|
|
5
6
|
{ name: "isValid", type: "function", stateMutability: "view", inputs: [{ name: "wallet", type: "address" }], outputs: [{ type: "bool" }] },
|
|
6
7
|
{ name: "credentialOf", type: "function", stateMutability: "view", inputs: [{ name: "wallet", type: "address" }], outputs: [{ name: "tokenId", type: "uint256" }] },
|
|
@@ -11,30 +12,39 @@ const CHAINS = {
|
|
|
11
12
|
"84532": baseSepolia,
|
|
12
13
|
};
|
|
13
14
|
export async function credentialStatus(opts) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const cfg = withConfig(opts);
|
|
16
|
+
if (!isAddress(cfg.wallet))
|
|
17
|
+
die(`Invalid wallet address: ${cfg.wallet}`);
|
|
18
|
+
if (!cfg.issuer)
|
|
19
|
+
die("CNFIssuer address required. Use --issuer or run `ilal init`.");
|
|
20
|
+
if (!isAddress(cfg.issuer))
|
|
21
|
+
die(`Invalid issuer address: ${cfg.issuer}`);
|
|
22
|
+
const chain = CHAINS[cfg.chain ?? "84532"] ?? baseSepolia;
|
|
23
|
+
const transport = cfg.rpc ? http(cfg.rpc) : http();
|
|
20
24
|
const client = createPublicClient({ chain, transport });
|
|
21
25
|
console.log();
|
|
22
26
|
console.log(fmt.bold(" ILAL Credential Status"));
|
|
23
27
|
log.line();
|
|
24
|
-
log.kv("wallet",
|
|
25
|
-
log.kv("issuer",
|
|
28
|
+
log.kv("wallet", cfg.wallet);
|
|
29
|
+
log.kv("issuer", cfg.issuer);
|
|
26
30
|
log.kv("chain", chain.name);
|
|
27
31
|
log.line();
|
|
28
32
|
log.step("Querying CNFIssuer on-chain…");
|
|
29
33
|
const [valid, tokenId] = await Promise.all([
|
|
30
|
-
client.readContract({ address:
|
|
31
|
-
client.readContract({ address:
|
|
34
|
+
client.readContract({ address: cfg.issuer, abi: CNF_ABI, functionName: "isValid", args: [cfg.wallet] }),
|
|
35
|
+
client.readContract({ address: cfg.issuer, abi: CNF_ABI, functionName: "credentialOf", args: [cfg.wallet] }),
|
|
32
36
|
]);
|
|
33
37
|
if (tokenId === 0n) {
|
|
34
38
|
log.fail("No credential found for this wallet");
|
|
35
39
|
console.log();
|
|
36
40
|
console.log(fmt.bold(" How to get a CNF credential:"));
|
|
37
41
|
console.log();
|
|
42
|
+
console.log(fmt.bold(" Demo path — issuer-created MockEAS attestation"));
|
|
43
|
+
console.log(` ${fmt.gray("1.")} Ask the issuer/operator to run:`);
|
|
44
|
+
console.log(` ${fmt.cyan("PRIVATE_KEY=<issuer-owner> ilal demo attest --wallet " + cfg.wallet)}`);
|
|
45
|
+
console.log(` ${fmt.gray("2.")} Then mint with your wallet key:`);
|
|
46
|
+
console.log(` ${fmt.cyan("PRIVATE_KEY=<wallet-key> ilal credential mint --attestation <uid>")}`);
|
|
47
|
+
console.log();
|
|
38
48
|
console.log(fmt.bold(" Path A — Coinbase Verifications (EAS)"));
|
|
39
49
|
console.log(` ${fmt.gray("1.")} Complete KYC at ${fmt.cyan("https://coinbase.com/onchain-verify")}`);
|
|
40
50
|
console.log(` ${fmt.gray("2.")} Find your attestation UID on EAS Explorer:`);
|
|
@@ -49,12 +59,12 @@ export async function credentialStatus(opts) {
|
|
|
49
59
|
console.log(` ${fmt.gray("1.")} Issuer/operator adds wallet to the Merkle tree`);
|
|
50
60
|
console.log(` ${fmt.gray("2.")} Operator queues root: ${fmt.cyan("ilal oracle propose-root --root <newMerkleRoot>")}`);
|
|
51
61
|
console.log(` ${fmt.gray("3.")} After timelock, operator activates: ${fmt.cyan("ilal oracle activate-root")}`);
|
|
52
|
-
console.log(` ${fmt.gray("4.")} Trader runs: ${fmt.cyan("ilal credential prove --wallet " +
|
|
62
|
+
console.log(` ${fmt.gray("4.")} Trader runs: ${fmt.cyan("ilal credential prove --wallet " + cfg.wallet)}`);
|
|
53
63
|
console.log();
|
|
54
64
|
return;
|
|
55
65
|
}
|
|
56
66
|
const cred = await client.readContract({
|
|
57
|
-
address:
|
|
67
|
+
address: cfg.issuer,
|
|
58
68
|
abi: CNF_ABI,
|
|
59
69
|
functionName: "getCredential",
|
|
60
70
|
args: [tokenId],
|
package/dist/commands/demo.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createPublicClient, createWalletClient, decodeEventLog, formatUnits, ht
|
|
|
2
2
|
import { privateKeyToAccount } from "viem/accounts";
|
|
3
3
|
import { base, baseSepolia } from "viem/chains";
|
|
4
4
|
import { loadConfig } from "../config.js";
|
|
5
|
-
import { die, fmt, header, log, Spinner } from "../ui.js";
|
|
5
|
+
import { die, fmt, header, log, Spinner, requirePrivateKey } from "../ui.js";
|
|
6
6
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
7
7
|
const POOL_MANAGER = {
|
|
8
8
|
"84532": "0x05E73354cFDd6745C338b50BcFDfA3Aa6fA03408",
|
|
@@ -442,6 +442,11 @@ export async function demoCheck(opts) {
|
|
|
442
442
|
}
|
|
443
443
|
if (wallet) {
|
|
444
444
|
log.command(`ilal status --wallet ${wallet}`);
|
|
445
|
+
if (!credentialReady) {
|
|
446
|
+
log.info("Credential missing: the issuer must create an attestation before the wallet can mint CNF.");
|
|
447
|
+
log.command(`PRIVATE_KEY=<issuer-owner> ilal demo attest --wallet ${wallet}`);
|
|
448
|
+
log.command("PRIVATE_KEY=<wallet-key> ilal credential mint --attestation <uid>");
|
|
449
|
+
}
|
|
445
450
|
log.command(`ilal session sign --pool ${cfg.poolId ?? "<poolId>"} --action swap --hook ${cfg.hook ?? "<hook>"} --issuer ${cfg.issuer ?? "<issuer>"} --caller ${cfg.router ?? "<router>"}`);
|
|
446
451
|
}
|
|
447
452
|
else {
|
|
@@ -454,10 +459,7 @@ export async function demoCheck(opts) {
|
|
|
454
459
|
export async function demoFaucet(opts) {
|
|
455
460
|
const cfg = loadConfig();
|
|
456
461
|
const chain = CHAINS[cfg.chain ?? "84532"] ?? baseSepolia;
|
|
457
|
-
const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
458
|
-
if (!rawKey || !isHex(rawKey) || rawKey.length !== 66) {
|
|
459
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
460
|
-
}
|
|
462
|
+
const rawKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
461
463
|
if (!cfg.tokenA || !cfg.tokenB || !isAddress(cfg.tokenA) || !isAddress(cfg.tokenB)) {
|
|
462
464
|
die("tokenA/tokenB required. Run `ilal init` with demo token addresses first.");
|
|
463
465
|
}
|
|
@@ -492,10 +494,7 @@ export async function demoFaucet(opts) {
|
|
|
492
494
|
export async function demoAttest(opts) {
|
|
493
495
|
const cfg = loadConfig();
|
|
494
496
|
const chain = CHAINS[cfg.chain ?? "84532"] ?? baseSepolia;
|
|
495
|
-
const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
496
|
-
if (!rawKey || !isHex(rawKey) || rawKey.length !== 66) {
|
|
497
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
498
|
-
}
|
|
497
|
+
const rawKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
499
498
|
if (!cfg.issuer || !isAddress(cfg.issuer))
|
|
500
499
|
die("CNFIssuer required. Run `ilal init` first.");
|
|
501
500
|
if (!isAddress(opts.wallet))
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
2
|
import { existsSync } from "fs";
|
|
3
3
|
import { resolve } from "path";
|
|
4
|
-
import { fmt, log, die } from "../ui.js";
|
|
4
|
+
import { fmt, log, die, requirePrivateKey } from "../ui.js";
|
|
5
5
|
import { EAS_ADDRESSES, COINBASE_ATTESTER, COINBASE_SCHEMA_UID } from "../constants.js";
|
|
6
6
|
const POOL_MANAGERS = {
|
|
7
7
|
"8453": "0x498581ff718922c3f8e6a244956af099b2652b2b", // Base mainnet
|
|
@@ -12,9 +12,7 @@ const RPC_URLS = {
|
|
|
12
12
|
"84532": "https://sepolia.base.org",
|
|
13
13
|
};
|
|
14
14
|
export async function deploy(opts) {
|
|
15
|
-
const privateKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
16
|
-
if (!privateKey)
|
|
17
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
15
|
+
const privateKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
18
16
|
const chainId = opts.chain;
|
|
19
17
|
const poolManager = POOL_MANAGERS[chainId];
|
|
20
18
|
if (!poolManager)
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -51,6 +51,8 @@ export async function init(opts) {
|
|
|
51
51
|
tickSpacing: opts.tickSpacing ?? preset["tickSpacing"],
|
|
52
52
|
rpc: opts.rpc ?? preset["rpc"],
|
|
53
53
|
...(opts.circuitDir ? { circuitDir: opts.circuitDir } : {}),
|
|
54
|
+
...(opts.artifactUrl ? { artifactUrl: opts.artifactUrl } : {}),
|
|
55
|
+
...(opts.artifactCache ? { artifactCache: opts.artifactCache } : {}),
|
|
54
56
|
};
|
|
55
57
|
// Validate addresses
|
|
56
58
|
for (const [key, val] of Object.entries(config)) {
|
|
@@ -86,6 +88,10 @@ export async function init(opts) {
|
|
|
86
88
|
log.kv("tickSpacing", config.tickSpacing);
|
|
87
89
|
if (config.rpc)
|
|
88
90
|
log.kv("rpc", config.rpc);
|
|
91
|
+
if (config.artifactUrl)
|
|
92
|
+
log.kv("artifactUrl", config.artifactUrl);
|
|
93
|
+
if (config.artifactCache)
|
|
94
|
+
log.kv("artifactCache", config.artifactCache);
|
|
89
95
|
log.line();
|
|
90
96
|
console.log(` ${fmt.gray("You can now run commands without --issuer and --chain flags:")}`);
|
|
91
97
|
console.log();
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { createPublicClient, createWalletClient, encodeAbiParameters, formatEther, formatUnits, http, isAddress, isHex, parseAbiParameters, } from "viem";
|
|
17
17
|
import { privateKeyToAccount } from "viem/accounts";
|
|
18
18
|
import { base, baseSepolia } from "viem/chains";
|
|
19
|
-
import { fmt, log, header, Spinner, die, dieOnContract } from "../ui.js";
|
|
19
|
+
import { fmt, log, header, Spinner, die, dieOnContract, requirePrivateKey } from "../ui.js";
|
|
20
20
|
import { withConfig } from "../config.js";
|
|
21
21
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
22
22
|
// ─── ABIs ─────────────────────────────────────────────────────────────────────
|
|
@@ -128,9 +128,7 @@ function defaultUserSalt(address) {
|
|
|
128
128
|
// ─── Shared core ──────────────────────────────────────────────────────────────
|
|
129
129
|
async function executeLiquidity(action, opts) {
|
|
130
130
|
const cfg = withConfig(opts);
|
|
131
|
-
const rawKey = cfg.privateKey ?? process.env["PRIVATE_KEY"];
|
|
132
|
-
if (!rawKey)
|
|
133
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
131
|
+
const rawKey = requirePrivateKey(cfg.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
134
132
|
if (!cfg.router)
|
|
135
133
|
die("ILALRouter address required. Use --router or set in .ilal.json");
|
|
136
134
|
if (!cfg.hook)
|
|
@@ -199,7 +197,7 @@ async function executeLiquidity(action, opts) {
|
|
|
199
197
|
if (tokenId === 0n) {
|
|
200
198
|
preflightErrors.push("wallet has no CNF credential; mint one before changing liquidity.");
|
|
201
199
|
if (hasEASPath)
|
|
202
|
-
preflightErrors.push("issuer supports EAS/mock attestation minting: run `ilal credential mint --attestation <uid>`.");
|
|
200
|
+
preflightErrors.push("issuer supports EAS/mock attestation minting: first ask issuer to run `ilal demo attest --wallet <wallet>`, then run `ilal credential mint --attestation <uid>`.");
|
|
203
201
|
else if (hasZKPath)
|
|
204
202
|
preflightErrors.push(`issuer supports ZK minting: run \`ilal credential prove --wallet ${account.address}\`.`);
|
|
205
203
|
else
|
package/dist/commands/mint.d.ts
CHANGED
package/dist/commands/mint.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createPublicClient, createWalletClient, http, isAddress, isHex, } from "viem";
|
|
2
2
|
import { privateKeyToAccount } from "viem/accounts";
|
|
3
3
|
import { base, baseSepolia } from "viem/chains";
|
|
4
|
-
import { fmt, log, die } from "../ui.js";
|
|
4
|
+
import { fmt, log, die, Spinner, requirePrivateKey } from "../ui.js";
|
|
5
|
+
import { withConfig } from "../config.js";
|
|
5
6
|
import { EAS_ADDRESSES, COINBASE_SCHEMA_UID, COINBASE_ATTESTER } from "../constants.js";
|
|
6
7
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
7
8
|
const CNF_ISSUER_ABI = [
|
|
@@ -32,25 +33,24 @@ const CNF_ISSUER_ABI = [
|
|
|
32
33
|
];
|
|
33
34
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
34
35
|
async function sendMintTx(mode, opts) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
die(`Invalid issuer address: ${opts.issuer}`);
|
|
36
|
+
const cfg = withConfig(opts);
|
|
37
|
+
const rawKey = requirePrivateKey(cfg.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
38
|
+
if (!cfg.issuer)
|
|
39
|
+
die("CNFIssuer address required. Use --issuer or run `ilal init`.");
|
|
40
|
+
if (!isAddress(cfg.issuer))
|
|
41
|
+
die(`Invalid issuer address: ${cfg.issuer}`);
|
|
42
42
|
if (!isHex(opts.attestation) || opts.attestation.length !== 66)
|
|
43
43
|
die("Attestation UID must be 0x + 32 bytes (64 hex chars).");
|
|
44
|
-
const chain = CHAINS[
|
|
44
|
+
const chain = CHAINS[cfg.chain ?? "84532"] ?? baseSepolia;
|
|
45
45
|
const account = privateKeyToAccount(rawKey);
|
|
46
|
-
const transport =
|
|
46
|
+
const transport = cfg.rpc ? http(cfg.rpc) : http();
|
|
47
47
|
const publicClient = createPublicClient({ chain, transport });
|
|
48
48
|
const walletClient = createWalletClient({ account, chain, transport });
|
|
49
49
|
console.log();
|
|
50
50
|
console.log(fmt.bold(` ILAL Credential ${mode === "mint" ? "Mint" : "Renew"}`));
|
|
51
51
|
log.line();
|
|
52
52
|
log.kv("wallet", account.address);
|
|
53
|
-
log.kv("issuer",
|
|
53
|
+
log.kv("issuer", cfg.issuer);
|
|
54
54
|
log.kv("attestation", opts.attestation);
|
|
55
55
|
log.kv("chain", chain.name);
|
|
56
56
|
if (opts.simulate)
|
|
@@ -59,9 +59,9 @@ async function sendMintTx(mode, opts) {
|
|
|
59
59
|
// Verify EAS attestation exists on-chain before sending tx
|
|
60
60
|
log.step("Verifying attestation on EAS…");
|
|
61
61
|
const [issuerEAS, issuerSchema, issuerAttester] = await Promise.all([
|
|
62
|
-
publicClient.readContract({ address:
|
|
63
|
-
publicClient.readContract({ address:
|
|
64
|
-
publicClient.readContract({ address:
|
|
62
|
+
publicClient.readContract({ address: cfg.issuer, abi: CNF_ISSUER_ABI, functionName: "eas" }),
|
|
63
|
+
publicClient.readContract({ address: cfg.issuer, abi: CNF_ISSUER_ABI, functionName: "schemaUID" }),
|
|
64
|
+
publicClient.readContract({ address: cfg.issuer, abi: CNF_ISSUER_ABI, functionName: "trustedAttester" }),
|
|
65
65
|
]);
|
|
66
66
|
const easAddress = issuerEAS !== ZERO_ADDRESS ? issuerEAS : EAS_ADDRESSES[chain.id];
|
|
67
67
|
if (!easAddress)
|
|
@@ -132,7 +132,7 @@ async function sendMintTx(mode, opts) {
|
|
|
132
132
|
}
|
|
133
133
|
log.step(`Sending ${mode === "mint" ? "mintWithEAS" : "renewWithEAS"} transaction…`);
|
|
134
134
|
const hash = await walletClient.writeContract({
|
|
135
|
-
address:
|
|
135
|
+
address: cfg.issuer,
|
|
136
136
|
abi: CNF_ISSUER_ABI,
|
|
137
137
|
functionName: mode === "mint" ? "mintWithEAS" : "renewWithEAS",
|
|
138
138
|
args: [opts.attestation],
|
|
@@ -144,13 +144,26 @@ async function sendMintTx(mode, opts) {
|
|
|
144
144
|
log.ok(fmt.bold(fmt.green(`CNF ${mode === "mint" ? "minted" : "renewed"} successfully`)));
|
|
145
145
|
log.kv("tx hash", hash);
|
|
146
146
|
log.kv("block", receipt.blockNumber.toString());
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
const validSpin = new Spinner("Waiting for credential validity…").start();
|
|
148
|
+
let valid = false;
|
|
149
|
+
for (let attempt = 0; attempt < 6; attempt++) {
|
|
150
|
+
valid = await publicClient.readContract({
|
|
151
|
+
address: cfg.issuer,
|
|
152
|
+
abi: CNF_ISSUER_ABI,
|
|
153
|
+
functionName: "isValid",
|
|
154
|
+
args: [account.address],
|
|
155
|
+
});
|
|
156
|
+
if (valid)
|
|
157
|
+
break;
|
|
158
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
159
|
+
}
|
|
160
|
+
if (valid)
|
|
161
|
+
validSpin.succeed("Credential active");
|
|
162
|
+
else {
|
|
163
|
+
validSpin.succeed("Mint confirmed; credential validity may take a few seconds to appear on this RPC");
|
|
164
|
+
log.info("Run `ilal credential status <wallet>` if this RPC still shows stale state.");
|
|
165
|
+
}
|
|
166
|
+
log.kv("isValid()", valid ? fmt.green("true ✓") : fmt.yellow("pending RPC refresh"));
|
|
154
167
|
}
|
|
155
168
|
else {
|
|
156
169
|
die(`Transaction reverted. Hash: ${hash}`);
|
package/dist/commands/oracle.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import { createPublicClient, createWalletClient, http, isAddress, } from "viem";
|
|
26
26
|
import { privateKeyToAccount } from "viem/accounts";
|
|
27
27
|
import { base, baseSepolia } from "viem/chains";
|
|
28
|
-
import { fmt, log, header, Spinner, die, dieOnContract } from "../ui.js";
|
|
28
|
+
import { fmt, log, header, Spinner, die, dieOnContract, requirePrivateKey } from "../ui.js";
|
|
29
29
|
import { withConfig } from "../config.js";
|
|
30
30
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
31
31
|
const ORACLE_ABI = [
|
|
@@ -79,9 +79,7 @@ function txUrl(chain, hash) {
|
|
|
79
79
|
return baseUrl ? `${baseUrl}/tx/${hash}` : undefined;
|
|
80
80
|
}
|
|
81
81
|
function makeClients(cfg, opts) {
|
|
82
|
-
const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
83
|
-
if (!rawKey)
|
|
84
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
82
|
+
const rawKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
85
83
|
const chain = CHAINS[opts.chain ?? "84532"] ?? baseSepolia;
|
|
86
84
|
const transport = opts.rpc ? http(opts.rpc) : http();
|
|
87
85
|
const account = privateKeyToAccount(rawKey);
|
package/dist/commands/pool.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPublicClient, createWalletClient, http, isAddress, isHex, } from "viem";
|
|
2
2
|
import { privateKeyToAccount } from "viem/accounts";
|
|
3
3
|
import { base, baseSepolia } from "viem/chains";
|
|
4
|
-
import { fmt, log, header, Spinner, die } from "../ui.js";
|
|
4
|
+
import { fmt, log, header, Spinner, die, requirePrivateKey } from "../ui.js";
|
|
5
5
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
6
6
|
const REGISTRY_ABI = [
|
|
7
7
|
{
|
|
@@ -38,11 +38,7 @@ const REGISTRY_ABI = [
|
|
|
38
38
|
},
|
|
39
39
|
];
|
|
40
40
|
export async function poolPolicySet(opts) {
|
|
41
|
-
const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
42
|
-
if (!rawKey)
|
|
43
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
44
|
-
if (!isHex(rawKey) || rawKey.length !== 66)
|
|
45
|
-
die("Invalid private key.");
|
|
41
|
+
const rawKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
46
42
|
if (!isAddress(opts.issuer))
|
|
47
43
|
die(`Invalid issuer address: ${opts.issuer}`);
|
|
48
44
|
if (!isAddress(opts.registry))
|
package/dist/commands/proof.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync } from "fs";
|
|
|
2
2
|
import { createPublicClient, createWalletClient, encodeAbiParameters, http, isAddress, } from "viem";
|
|
3
3
|
import { privateKeyToAccount } from "viem/accounts";
|
|
4
4
|
import { base, baseSepolia } from "viem/chains";
|
|
5
|
-
import { fmt, log, die } from "../ui.js";
|
|
5
|
+
import { fmt, log, die, requirePrivateKey } from "../ui.js";
|
|
6
6
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
7
7
|
const CNF_ISSUER_ABI = [
|
|
8
8
|
{
|
|
@@ -73,9 +73,7 @@ export async function proofRenew(opts) {
|
|
|
73
73
|
await sendProofTx("renew", opts);
|
|
74
74
|
}
|
|
75
75
|
async function sendProofTx(mode, opts) {
|
|
76
|
-
const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
77
|
-
if (!rawKey)
|
|
78
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
76
|
+
const rawKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
79
77
|
if (!isAddress(opts.issuer))
|
|
80
78
|
die(`Invalid issuer address: ${opts.issuer}`);
|
|
81
79
|
const chain = CHAINS[opts.chain] ?? baseSepolia;
|
package/dist/commands/prove.d.ts
CHANGED
package/dist/commands/prove.js
CHANGED
|
@@ -16,8 +16,11 @@
|
|
|
16
16
|
* `ilal oracle activate-root`.
|
|
17
17
|
*/
|
|
18
18
|
import { execSync } from "child_process";
|
|
19
|
-
import {
|
|
19
|
+
import { createWriteStream, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
20
|
+
import { homedir } from "os";
|
|
20
21
|
import { resolve, dirname } from "path";
|
|
22
|
+
import { Readable } from "stream";
|
|
23
|
+
import { pipeline } from "stream/promises";
|
|
21
24
|
import { fileURLToPath } from "url";
|
|
22
25
|
import { createPublicClient, createWalletClient, encodeAbiParameters, http, isAddress, keccak256, } from "viem";
|
|
23
26
|
import { privateKeyToAccount } from "viem/accounts";
|
|
@@ -26,12 +29,21 @@ import { base, baseSepolia } from "viem/chains";
|
|
|
26
29
|
// @ts-ignore — no bundled types for this package
|
|
27
30
|
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree";
|
|
28
31
|
import { poseidon2, poseidon4 } from "poseidon-lite";
|
|
29
|
-
import { fmt, log, header, Spinner, die, dieOnContract } from "../ui.js";
|
|
32
|
+
import { fmt, log, header, Spinner, die, dieOnContract, requirePrivateKey } from "../ui.js";
|
|
30
33
|
import { withConfig } from "../config.js";
|
|
31
34
|
import { COINBASE_SCHEMA_UID } from "../constants.js";
|
|
32
35
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
33
36
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
34
37
|
const DEPTH = 20;
|
|
38
|
+
const DEFAULT_ARTIFACT_URL = "https://unpkg.com/@ilalv3/proving-artifacts@0.1.0";
|
|
39
|
+
const DEFAULT_ARTIFACT_CACHE = resolve(homedir(), ".ilal/artifacts/ilal-v1");
|
|
40
|
+
const PROVING_ARTIFACTS = [
|
|
41
|
+
{ asset: "ilal.zkey", rel: "ilal.zkey", minBytes: 50_000_000 },
|
|
42
|
+
{ asset: "ilal_vkey.json", rel: "ilal_vkey.json", minBytes: 1_000 },
|
|
43
|
+
{ asset: "ilal_js/ilal.wasm", rel: "ilal_js/ilal.wasm", minBytes: 1_000_000 },
|
|
44
|
+
{ asset: "ilal_js/generate_witness.js", rel: "ilal_js/generate_witness.js", minBytes: 100 },
|
|
45
|
+
{ asset: "ilal_js/witness_calculator.js", rel: "ilal_js/witness_calculator.js", minBytes: 1_000 },
|
|
46
|
+
];
|
|
35
47
|
// ─── ABI ──────────────────────────────────────────────────────────────────────
|
|
36
48
|
const CNF_ABI = [
|
|
37
49
|
{
|
|
@@ -82,26 +94,81 @@ function schemaHash(schemaUID) {
|
|
|
82
94
|
const schemaHi = BigInt("0x" + schemaHex.slice(0, 32));
|
|
83
95
|
return poseidon2([schemaLo, schemaHi]);
|
|
84
96
|
}
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
function hasCircuitArtifacts(dir) {
|
|
98
|
+
return PROVING_ARTIFACTS.every((artifact) => {
|
|
99
|
+
const file = resolve(dir, artifact.rel);
|
|
100
|
+
try {
|
|
101
|
+
return existsSync(file) && statSync(file).size >= artifact.minBytes;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function requireCircuitArtifacts(dir) {
|
|
109
|
+
const resolved = resolve(dir);
|
|
110
|
+
const missing = PROVING_ARTIFACTS.filter((artifact) => {
|
|
111
|
+
const file = resolve(resolved, artifact.rel);
|
|
112
|
+
try {
|
|
113
|
+
return !existsSync(file) || statSync(file).size < artifact.minBytes;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
if (missing.length > 0) {
|
|
120
|
+
die(`Proving artifacts are incomplete in ${resolved}.\n` +
|
|
121
|
+
` Missing: ${missing.map((x) => x.rel).join(", ")}\n` +
|
|
122
|
+
" Pass --artifact-url <base-url> to download them, or use --circuit-dir <path>.");
|
|
123
|
+
}
|
|
124
|
+
return resolved;
|
|
125
|
+
}
|
|
126
|
+
async function downloadFile(url, target) {
|
|
127
|
+
const res = await fetch(url);
|
|
128
|
+
if (!res.ok || !res.body) {
|
|
129
|
+
throw new Error(`download failed ${res.status} ${res.statusText}: ${url}`);
|
|
130
|
+
}
|
|
131
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
132
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(target));
|
|
133
|
+
}
|
|
134
|
+
async function ensureHostedArtifacts(opts) {
|
|
135
|
+
const cacheDir = resolve(opts.artifactCache ?? DEFAULT_ARTIFACT_CACHE);
|
|
136
|
+
if (hasCircuitArtifacts(cacheDir))
|
|
137
|
+
return cacheDir;
|
|
138
|
+
if (opts.offline) {
|
|
139
|
+
die("Hosted proving artifacts are not cached locally.\n" +
|
|
140
|
+
` Cache: ${cacheDir}\n` +
|
|
141
|
+
" Re-run without --offline, or pass --circuit-dir <path>.");
|
|
142
|
+
}
|
|
143
|
+
const baseUrl = (opts.artifactUrl ?? DEFAULT_ARTIFACT_URL).replace(/\/+$/, "");
|
|
144
|
+
for (const artifact of PROVING_ARTIFACTS) {
|
|
145
|
+
const target = resolve(cacheDir, artifact.rel);
|
|
146
|
+
if (existsSync(target) && statSync(target).size >= artifact.minBytes)
|
|
147
|
+
continue;
|
|
148
|
+
await downloadFile(`${baseUrl}/${artifact.asset}`, target);
|
|
149
|
+
}
|
|
150
|
+
return requireCircuitArtifacts(cacheDir);
|
|
151
|
+
}
|
|
152
|
+
async function findCircuitDir(opts) {
|
|
153
|
+
if (opts.override) {
|
|
154
|
+
return { dir: requireCircuitArtifacts(opts.override), source: "local" };
|
|
155
|
+
}
|
|
88
156
|
// Look relative to the CLI package root (cli/ → circuits/build)
|
|
89
157
|
const candidates = [
|
|
90
158
|
resolve(__dirname, "../../../../circuits/build"), // dev: cli/src/commands → circuits/build
|
|
91
159
|
resolve(__dirname, "../../../circuits/build"),
|
|
92
160
|
resolve(process.cwd(), "circuits/build"),
|
|
93
161
|
resolve(process.cwd(), "build"),
|
|
162
|
+
resolve(opts.artifactCache ?? DEFAULT_ARTIFACT_CACHE),
|
|
94
163
|
];
|
|
95
164
|
for (const p of candidates) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return p;
|
|
165
|
+
if (hasCircuitArtifacts(p)) {
|
|
166
|
+
const source = resolve(p) === resolve(opts.artifactCache ?? DEFAULT_ARTIFACT_CACHE) ? "cache" : "local";
|
|
167
|
+
return { dir: p, source };
|
|
99
168
|
}
|
|
100
|
-
catch { /* not found */ }
|
|
101
169
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
" Or pass: --circuit-dir <path/to/circuits/build>");
|
|
170
|
+
const dir = await ensureHostedArtifacts(opts);
|
|
171
|
+
return { dir, source: "download" };
|
|
105
172
|
}
|
|
106
173
|
function resolveExpiresAt(expiresAt) {
|
|
107
174
|
if (!expiresAt)
|
|
@@ -193,9 +260,7 @@ function encodeProof(proofJson, publicJson) {
|
|
|
193
260
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
194
261
|
export async function credentialProve(opts) {
|
|
195
262
|
const cfg = withConfig(opts);
|
|
196
|
-
const rawKey = cfg.privateKey ?? process.env["PRIVATE_KEY"];
|
|
197
|
-
if (!rawKey)
|
|
198
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
263
|
+
const rawKey = requirePrivateKey(cfg.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
199
264
|
if (!cfg.wallet)
|
|
200
265
|
die("Wallet address required. Use --wallet or set issuer in .ilal.json");
|
|
201
266
|
if (!cfg.issuer)
|
|
@@ -226,11 +291,29 @@ export async function credentialProve(opts) {
|
|
|
226
291
|
action = tokenId === 0n ? "mint" : "renew";
|
|
227
292
|
spin.succeed(`Action: ${fmt.cyan(action)}${tokenId > 0n ? fmt.gray(` (token #${tokenId})`) : ""}`);
|
|
228
293
|
}
|
|
229
|
-
// ──
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
294
|
+
// ── Prepare hosted/local proving artifacts ────────────────────────────────
|
|
295
|
+
const artifactSpin = new Spinner("Preparing proving artifacts…").start();
|
|
296
|
+
let circuitDir;
|
|
297
|
+
try {
|
|
298
|
+
const artifacts = await findCircuitDir({
|
|
299
|
+
override: cfg.circuitDir,
|
|
300
|
+
artifactUrl: cfg.artifactUrl,
|
|
301
|
+
artifactCache: cfg.artifactCache,
|
|
302
|
+
offline: cfg.offline,
|
|
303
|
+
});
|
|
304
|
+
circuitDir = artifacts.dir;
|
|
305
|
+
const sourceLabel = artifacts.source === "download" ? "downloaded to cache" :
|
|
306
|
+
artifacts.source === "cache" ? "cache" :
|
|
307
|
+
"local";
|
|
308
|
+
artifactSpin.succeed(`Proving artifacts ready (${sourceLabel})`);
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
artifactSpin.fail("Proving artifacts unavailable");
|
|
312
|
+
die(e instanceof Error ? e.message : String(e));
|
|
313
|
+
}
|
|
314
|
+
const outDir = cfg.outDir ? resolve(cfg.outDir) : resolve(process.cwd(), "outputs");
|
|
315
|
+
log.kv("artifacts", fmt.gray(circuitDir));
|
|
316
|
+
log.kv("proofOut", fmt.gray(outDir));
|
|
234
317
|
log.line();
|
|
235
318
|
// ── Generate proof ─────────────────────────────────────────────────────────
|
|
236
319
|
const spin = new Spinner("Building Merkle tree & generating ZK proof…").start();
|
package/dist/commands/session.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createWalletClient, encodeAbiParameters, http, isAddress, isHex, parseAbiParameters, } from "viem";
|
|
2
2
|
import { privateKeyToAccount } from "viem/accounts";
|
|
3
3
|
import { base, baseSepolia } from "viem/chains";
|
|
4
|
-
import { fmt, log, header, die } from "../ui.js";
|
|
4
|
+
import { fmt, log, header, die, requirePrivateKey } from "../ui.js";
|
|
5
5
|
import { withConfig } from "../config.js";
|
|
6
6
|
const CHAINS = {
|
|
7
7
|
"8453": base,
|
|
@@ -30,11 +30,7 @@ const HOOK_DATA_ABI = parseAbiParameters([
|
|
|
30
30
|
export async function sessionSign(opts) {
|
|
31
31
|
const cfg = withConfig({ chain: opts.chain, hook: opts.hook, issuer: opts.issuer });
|
|
32
32
|
// Resolve private key
|
|
33
|
-
const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
|
|
34
|
-
if (!rawKey)
|
|
35
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
36
|
-
if (!isHex(rawKey) || rawKey.length !== 66)
|
|
37
|
-
die("Invalid private key format (expected 0x + 32 bytes).");
|
|
33
|
+
const rawKey = requirePrivateKey(opts.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
38
34
|
const account = privateKeyToAccount(rawKey);
|
|
39
35
|
const user = (opts.user ?? account.address);
|
|
40
36
|
const pool = opts.pool ?? cfg.poolId;
|
package/dist/commands/swap.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { createPublicClient, createWalletClient, decodeAbiParameters, encodeAbiParameters, formatEther, formatUnits, http, isAddress, isHex, parseAbiParameters, parseUnits, } from "viem";
|
|
22
22
|
import { privateKeyToAccount } from "viem/accounts";
|
|
23
23
|
import { base, baseSepolia } from "viem/chains";
|
|
24
|
-
import { fmt, log, header, Spinner, die, dieOnContract } from "../ui.js";
|
|
24
|
+
import { fmt, log, header, Spinner, die, dieOnContract, requirePrivateKey } from "../ui.js";
|
|
25
25
|
import { withConfig } from "../config.js";
|
|
26
26
|
const CHAINS = { "8453": base, "84532": baseSepolia };
|
|
27
27
|
// ─── ABIs ─────────────────────────────────────────────────────────────────────
|
|
@@ -127,9 +127,7 @@ function secondsSince(startMs) {
|
|
|
127
127
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
128
128
|
export async function swap(opts) {
|
|
129
129
|
const cfg = withConfig(opts);
|
|
130
|
-
const rawKey = cfg.privateKey ?? process.env["PRIVATE_KEY"];
|
|
131
|
-
if (!rawKey)
|
|
132
|
-
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
130
|
+
const rawKey = requirePrivateKey(cfg.privateKey ?? process.env["PRIVATE_KEY"]);
|
|
133
131
|
if (!cfg.router)
|
|
134
132
|
die("ILALRouter address required. Use --router or set in .ilal.json");
|
|
135
133
|
if (!cfg.hook)
|
|
@@ -211,7 +209,7 @@ export async function swap(opts) {
|
|
|
211
209
|
if (tokenId === 0n) {
|
|
212
210
|
preflightErrors.push(`wallet has no CNF credential; mint one before trading.`);
|
|
213
211
|
if (hasEASPath)
|
|
214
|
-
preflightErrors.push("issuer supports EAS/mock attestation minting: run `ilal credential mint --attestation <uid>`.");
|
|
212
|
+
preflightErrors.push("issuer supports EAS/mock attestation minting: first ask issuer to run `ilal demo attest --wallet <wallet>`, then run `ilal credential mint --attestation <uid>`.");
|
|
215
213
|
else if (hasZKPath)
|
|
216
214
|
preflightErrors.push(`issuer supports ZK minting: run \`ilal credential prove --wallet ${account.address}\`.`);
|
|
217
215
|
else
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -52,6 +52,10 @@ export function loadConfig() {
|
|
|
52
52
|
chain: process.env["ILAL_CHAIN"] ?? fileConfig.chain,
|
|
53
53
|
rpc: process.env["ILAL_RPC"] ?? fileConfig.rpc,
|
|
54
54
|
circuitDir: process.env["ILAL_CIRCUIT_DIR"] ?? fileConfig.circuitDir,
|
|
55
|
+
artifactUrl: process.env["ILAL_ARTIFACT_BASE_URL"]
|
|
56
|
+
?? process.env["ILAL_ARTIFACT_URL"]
|
|
57
|
+
?? fileConfig.artifactUrl,
|
|
58
|
+
artifactCache: process.env["ILAL_ARTIFACT_CACHE"] ?? fileConfig.artifactCache,
|
|
55
59
|
outDir: process.env["ILAL_OUT_DIR"] ?? fileConfig.outDir,
|
|
56
60
|
};
|
|
57
61
|
return _config;
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ const program = new Command();
|
|
|
19
19
|
program
|
|
20
20
|
.name("ilal")
|
|
21
21
|
.description("ILAL Protocol CLI — Uniswap v4 compliance hook toolkit")
|
|
22
|
-
.version("0.2.
|
|
22
|
+
.version("0.2.11")
|
|
23
23
|
.addHelpText("before", `\n ${fmt.bold(fmt.cyan("◆"))} ${fmt.bold("ILAL Protocol")} ${fmt.gray("Uniswap v4 Compliance Hook")}\n`);
|
|
24
24
|
// ─── init ─────────────────────────────────────────────────────────────────────
|
|
25
25
|
program
|
|
@@ -38,6 +38,8 @@ program
|
|
|
38
38
|
.option("-c, --chain <chainId>", "Chain ID (8453=Base, 84532=Base Sepolia)", "84532")
|
|
39
39
|
.option("-r, --rpc <url>", "Custom RPC URL")
|
|
40
40
|
.option("--circuit-dir <path>", "Path to circuits/build directory")
|
|
41
|
+
.option("--artifact-url <url>", "Hosted proving artifact base URL")
|
|
42
|
+
.option("--artifact-cache <path>", "Local proving artifact cache directory")
|
|
41
43
|
.option("-f, --force", "Overwrite existing .ilal.json", false)
|
|
42
44
|
.action(async (opts) => {
|
|
43
45
|
await init(opts).catch(err);
|
|
@@ -99,8 +101,8 @@ const credential = program.command("credential").description("Manage compliance
|
|
|
99
101
|
credential
|
|
100
102
|
.command("status <wallet>")
|
|
101
103
|
.description("Check CNF credential status for a wallet")
|
|
102
|
-
.
|
|
103
|
-
.option("-c, --chain <chainId>", "Chain ID (8453=Base, 84532=Base Sepolia)", "
|
|
104
|
+
.option("-i, --issuer <address>", "CNFIssuer contract address (or set in .ilal.json)")
|
|
105
|
+
.option("-c, --chain <chainId>", "Chain ID (8453=Base, 84532=Base Sepolia)", "84532")
|
|
104
106
|
.option("-r, --rpc <url>", "Custom RPC URL")
|
|
105
107
|
.action(async (wallet, opts) => {
|
|
106
108
|
await credentialStatus({ wallet, ...opts }).catch(err);
|
|
@@ -111,7 +113,10 @@ credential
|
|
|
111
113
|
.option("-w, --wallet <address>", "Wallet address to prove eligibility for")
|
|
112
114
|
.option("-i, --issuer <address>", "CNFIssuer contract address (or set in .ilal.json)")
|
|
113
115
|
.option("-a, --action <action>", "mint or renew (default: auto-detect)")
|
|
114
|
-
.option("--circuit-dir <path>", "Path to circuits/build directory (
|
|
116
|
+
.option("--circuit-dir <path>", "Path to circuits/build directory (dev/offline override)")
|
|
117
|
+
.option("--artifact-url <url>", "Hosted proving artifact base URL (defaults to ILAL release artifacts)")
|
|
118
|
+
.option("--artifact-cache <path>", "Local proving artifact cache directory (default: ~/.ilal/artifacts/ilal-v1)")
|
|
119
|
+
.option("--offline", "Do not download proving artifacts; require cache or --circuit-dir", false)
|
|
115
120
|
.option("--out-dir <path>", "Directory to write proof/witness files")
|
|
116
121
|
.option("-c, --chain <chainId>", "Chain ID (8453=Base, 84532=Base Sepolia)", "84532")
|
|
117
122
|
.option("-r, --rpc <url>", "Custom RPC URL")
|
|
@@ -133,8 +138,8 @@ credential
|
|
|
133
138
|
.command("mint")
|
|
134
139
|
.description("Mint a CNF credential using a Coinbase EAS attestation")
|
|
135
140
|
.requiredOption("-a, --attestation <uid>", "EAS attestation UID (0x + 64 hex chars)")
|
|
136
|
-
.
|
|
137
|
-
.option("-c, --chain <chainId>", "Chain ID", "
|
|
141
|
+
.option("-i, --issuer <address>", "CNFIssuer contract address (or set in .ilal.json)")
|
|
142
|
+
.option("-c, --chain <chainId>", "Chain ID", "84532")
|
|
138
143
|
.option("-r, --rpc <url>", "Custom RPC URL")
|
|
139
144
|
.option("-k, --private-key <hex>", "Private key (or set PRIVATE_KEY env var)")
|
|
140
145
|
.option("--simulate", "Verify attestation without sending tx", false)
|
|
@@ -145,8 +150,8 @@ credential
|
|
|
145
150
|
.command("renew")
|
|
146
151
|
.description("Renew an existing CNF credential with a fresh EAS attestation")
|
|
147
152
|
.requiredOption("-a, --attestation <uid>", "EAS attestation UID (0x + 64 hex chars)")
|
|
148
|
-
.
|
|
149
|
-
.option("-c, --chain <chainId>", "Chain ID", "
|
|
153
|
+
.option("-i, --issuer <address>", "CNFIssuer contract address (or set in .ilal.json)")
|
|
154
|
+
.option("-c, --chain <chainId>", "Chain ID", "84532")
|
|
150
155
|
.option("-r, --rpc <url>", "Custom RPC URL")
|
|
151
156
|
.option("-k, --private-key <hex>", "Private key (or set PRIVATE_KEY env var)")
|
|
152
157
|
.option("--simulate", "Verify attestation without sending tx", false)
|
package/dist/ui.d.ts
CHANGED
|
@@ -56,4 +56,5 @@ export declare const log: {
|
|
|
56
56
|
};
|
|
57
57
|
export declare function header(title: string, subtitle?: string): void;
|
|
58
58
|
export declare function die(msg: string): never;
|
|
59
|
+
export declare function requirePrivateKey(rawKey?: string): `0x${string}`;
|
|
59
60
|
export declare function dieOnContract(e: unknown): never;
|
package/dist/ui.js
CHANGED
|
@@ -213,6 +213,19 @@ export function die(msg) {
|
|
|
213
213
|
console.error();
|
|
214
214
|
process.exit(1);
|
|
215
215
|
}
|
|
216
|
+
export function requirePrivateKey(rawKey) {
|
|
217
|
+
const key = rawKey?.trim();
|
|
218
|
+
if (!key) {
|
|
219
|
+
die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
|
|
220
|
+
}
|
|
221
|
+
if (/^[0-9a-fA-F]{64}$/.test(key)) {
|
|
222
|
+
die("Private key must include the 0x prefix. Example: PRIVATE_KEY=0x...");
|
|
223
|
+
}
|
|
224
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(key)) {
|
|
225
|
+
die("Private key must be 32-byte hex and include the 0x prefix. Example: PRIVATE_KEY=0x...");
|
|
226
|
+
}
|
|
227
|
+
return key;
|
|
228
|
+
}
|
|
216
229
|
export function dieOnContract(e) {
|
|
217
230
|
die(parseViemError(e));
|
|
218
231
|
}
|