@ilalv3/cli 0.1.0

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.
@@ -0,0 +1,249 @@
1
+ /**
2
+ * liquidity.ts — `ilal pool add-liquidity` / `ilal pool remove-liquidity`
3
+ *
4
+ * Add or remove liquidity from an ILAL-compliant Uniswap v4 pool.
5
+ *
6
+ * Signs a fresh SessionToken internally, calls ILALRouter.addLiquidity()
7
+ * or removeLiquidity(). The ComplianceHook verifies session + CNF.
8
+ *
9
+ * Usage:
10
+ * ilal pool add-liquidity \
11
+ * --tick-lower -600 --tick-upper 600 \
12
+ * --liquidity 1000000000000000000 \
13
+ * --router 0xROUTER --hook 0xHOOK --issuer 0xISSUER \
14
+ * --pool-id 0xPOOLID --token-a 0xTOKA --token-b 0xTOKB
15
+ */
16
+ import { createPublicClient, createWalletClient, encodeAbiParameters, http, isAddress, isHex, parseAbiParameters, } from "viem";
17
+ import { privateKeyToAccount } from "viem/accounts";
18
+ import { base, baseSepolia } from "viem/chains";
19
+ import { fmt, log, header, Spinner, die, dieOnContract } from "../ui.js";
20
+ import { withConfig } from "../config.js";
21
+ const CHAINS = { "8453": base, "84532": baseSepolia };
22
+ // ─── ABIs ─────────────────────────────────────────────────────────────────────
23
+ const ERC20_ABI = [
24
+ { name: "symbol", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
25
+ { name: "decimals", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
26
+ { name: "allowance", type: "function", stateMutability: "view", inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }], outputs: [{ type: "uint256" }] },
27
+ { name: "approve", type: "function", stateMutability: "nonpayable", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }] },
28
+ ];
29
+ const ROUTER_LIQUIDITY_ABI = [
30
+ {
31
+ name: "addLiquidity", type: "function", stateMutability: "payable",
32
+ inputs: [
33
+ { name: "key", type: "tuple", components: [
34
+ { name: "currency0", type: "address" },
35
+ { name: "currency1", type: "address" },
36
+ { name: "fee", type: "uint24" },
37
+ { name: "tickSpacing", type: "int24" },
38
+ { name: "hooks", type: "address" },
39
+ ] },
40
+ { name: "params", type: "tuple", components: [
41
+ { name: "tickLower", type: "int24" },
42
+ { name: "tickUpper", type: "int24" },
43
+ { name: "liquidityDelta", type: "int256" },
44
+ { name: "salt", type: "bytes32" },
45
+ ] },
46
+ { name: "hookData", type: "bytes" },
47
+ ],
48
+ outputs: [
49
+ { name: "callerDelta", type: "int256" },
50
+ { name: "feesAccrued", type: "int256" },
51
+ ],
52
+ },
53
+ {
54
+ name: "removeLiquidity", type: "function", stateMutability: "nonpayable",
55
+ inputs: [
56
+ { name: "key", type: "tuple", components: [
57
+ { name: "currency0", type: "address" },
58
+ { name: "currency1", type: "address" },
59
+ { name: "fee", type: "uint24" },
60
+ { name: "tickSpacing", type: "int24" },
61
+ { name: "hooks", type: "address" },
62
+ ] },
63
+ { name: "params", type: "tuple", components: [
64
+ { name: "tickLower", type: "int24" },
65
+ { name: "tickUpper", type: "int24" },
66
+ { name: "liquidityDelta", type: "int256" },
67
+ { name: "salt", type: "bytes32" },
68
+ ] },
69
+ { name: "hookData", type: "bytes" },
70
+ ],
71
+ outputs: [
72
+ { name: "callerDelta", type: "int256" },
73
+ { name: "feesAccrued", type: "int256" },
74
+ ],
75
+ },
76
+ ];
77
+ // ─── Session helpers ──────────────────────────────────────────────────────────
78
+ const SESSION_TOKEN_TYPE = [
79
+ { name: "user", type: "address" },
80
+ { name: "authorizedCaller", type: "address" },
81
+ { name: "cnfIssuer", type: "address" },
82
+ { name: "chainId", type: "uint256" },
83
+ { name: "verifyingHook", type: "address" },
84
+ { name: "poolId", type: "bytes32" },
85
+ { name: "action", type: "uint8" },
86
+ { name: "deadline", type: "uint64" },
87
+ { name: "nonce", type: "bytes32" },
88
+ ];
89
+ const HOOK_DATA_ABI = parseAbiParameters([
90
+ "(address user, address authorizedCaller, address cnfIssuer, uint256 chainId, address verifyingHook, bytes32 poolId, uint8 action, uint64 deadline, bytes32 nonce) token",
91
+ "bytes signature",
92
+ ]);
93
+ function txUrl(chain, hash) {
94
+ const baseUrl = chain.blockExplorers?.default?.url;
95
+ return baseUrl ? `${baseUrl}/tx/${hash}` : undefined;
96
+ }
97
+ // ─── Shared core ──────────────────────────────────────────────────────────────
98
+ async function executeLiquidity(action, opts) {
99
+ const cfg = withConfig(opts);
100
+ const rawKey = cfg.privateKey ?? process.env["PRIVATE_KEY"];
101
+ if (!rawKey)
102
+ die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
103
+ if (!cfg.router)
104
+ die("ILALRouter address required. Use --router or set in .ilal.json");
105
+ if (!cfg.hook)
106
+ die("ComplianceHook address required. Use --hook or set in .ilal.json");
107
+ if (!cfg.issuer)
108
+ die("CNFIssuer address required. Use --issuer or set in .ilal.json");
109
+ if (!cfg.poolId)
110
+ die("Pool ID required. Use --pool-id or set in .ilal.json");
111
+ if (!isAddress(cfg.router))
112
+ die(`Invalid router address: ${cfg.router}`);
113
+ if (!isAddress(cfg.hook))
114
+ die(`Invalid hook address: ${cfg.hook}`);
115
+ if (!isAddress(cfg.issuer))
116
+ die(`Invalid issuer address: ${cfg.issuer}`);
117
+ if (!isHex(cfg.poolId) || cfg.poolId.length !== 66)
118
+ die("poolId must be 0x + 64 hex chars");
119
+ const tokenA = (cfg.tokenA ?? opts.tokenA);
120
+ const tokenB = (cfg.tokenB ?? opts.tokenB);
121
+ if (!tokenA || !tokenB)
122
+ die("Token addresses required. Use --token-a/--token-b or set in .ilal.json");
123
+ const chain = CHAINS[cfg.chain ?? "84532"] ?? baseSepolia;
124
+ const account = privateKeyToAccount(rawKey);
125
+ const transport = cfg.rpc ? http(cfg.rpc) : http();
126
+ const pubClient = createPublicClient({ chain, transport });
127
+ const walClient = createWalletClient({ account, chain, transport });
128
+ // Ensure currency0 < currency1
129
+ const c0 = tokenA.toLowerCase() < tokenB.toLowerCase() ? tokenA : tokenB;
130
+ const c1 = tokenA.toLowerCase() < tokenB.toLowerCase() ? tokenB : tokenA;
131
+ const tickLower = parseInt(opts.tickLower);
132
+ const tickUpper = parseInt(opts.tickUpper);
133
+ const liquidity = BigInt(opts.liquidity);
134
+ const fee = parseInt(cfg.fee ?? "3000");
135
+ const tickSpacing = parseInt(cfg.tickSpacing ?? "60");
136
+ const salt = (opts.salt ?? "0x0000000000000000000000000000000000000000000000000000000000000000");
137
+ const verb = action === "add" ? "Add" : "Remove";
138
+ header(`ILAL ${verb} Liquidity`, chain.name);
139
+ log.kv("router", fmt.cyan(cfg.router));
140
+ log.kv("hook", fmt.cyan(cfg.hook));
141
+ log.kv("pool", fmt.gray(cfg.poolId.slice(0, 18) + "…"));
142
+ log.kv("tickLower", tickLower.toString());
143
+ log.kv("tickUpper", tickUpper.toString());
144
+ log.kv("liquidity", liquidity.toString());
145
+ log.line();
146
+ // Approve both tokens if adding liquidity
147
+ if (action === "add") {
148
+ const MAX = 2n ** 256n - 1n;
149
+ for (const token of [c0, c1]) {
150
+ const sym = await pubClient.readContract({ address: token, abi: ERC20_ABI, functionName: "symbol" });
151
+ const allowed = await pubClient.readContract({
152
+ address: token, abi: ERC20_ABI, functionName: "allowance",
153
+ args: [account.address, cfg.router],
154
+ });
155
+ if (allowed < MAX / 2n) {
156
+ const appSpin = new Spinner(`Approving ${sym}…`).start();
157
+ const h = await walClient.writeContract({
158
+ address: token, abi: ERC20_ABI, functionName: "approve",
159
+ args: [cfg.router, MAX],
160
+ });
161
+ await pubClient.waitForTransactionReceipt({ hash: h });
162
+ appSpin.succeed(`Approved ${sym} ${fmt.gray(fmt.hash(h))}`);
163
+ }
164
+ }
165
+ }
166
+ // Sign session token
167
+ const signSpin = new Spinner("Signing session token…").start();
168
+ const ttl = parseInt(opts.ttl ?? "600");
169
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + ttl);
170
+ const nonce = `0x${Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex")}`;
171
+ // action: 2 = ADD_LIQUIDITY, 3 = REMOVE_LIQUIDITY
172
+ const actionCode = action === "add" ? 2 : 3;
173
+ const token = {
174
+ user: account.address,
175
+ authorizedCaller: cfg.router,
176
+ cnfIssuer: cfg.issuer,
177
+ chainId: BigInt(chain.id),
178
+ verifyingHook: cfg.hook,
179
+ poolId: cfg.poolId,
180
+ action: actionCode,
181
+ deadline,
182
+ nonce,
183
+ };
184
+ const signature = await walClient.signTypedData({
185
+ account,
186
+ domain: {
187
+ name: "ILAL ComplianceHook",
188
+ version: "1",
189
+ chainId: BigInt(chain.id),
190
+ verifyingContract: cfg.hook,
191
+ },
192
+ types: { SessionToken: SESSION_TOKEN_TYPE },
193
+ primaryType: "SessionToken",
194
+ message: token,
195
+ });
196
+ const hookData = encodeAbiParameters(HOOK_DATA_ABI, [token, signature]);
197
+ signSpin.succeed(`Session signed (expires in ${ttl}s)`);
198
+ log.section("Gate Checks");
199
+ log.kv("credential", `${fmt.badge("required", "cyan")} issuer ${fmt.addr(cfg.issuer)}`);
200
+ log.kv("caller", `${fmt.badge("bound", "green")} ${fmt.addr(cfg.router)}`);
201
+ log.kv("nonce", `${fmt.badge("fresh", "green")} ${fmt.hash(nonce)}`);
202
+ log.line();
203
+ // Build PoolKey + params
204
+ const poolKey = {
205
+ currency0: c0,
206
+ currency1: c1,
207
+ fee,
208
+ tickSpacing,
209
+ hooks: cfg.hook,
210
+ };
211
+ const liquidityDelta = action === "add" ? liquidity : -liquidity;
212
+ const liquidityParams = { tickLower, tickUpper, liquidityDelta, salt };
213
+ const fnName = action === "add" ? "addLiquidity" : "removeLiquidity";
214
+ // Execute
215
+ const txSpin = new Spinner(`Sending ${fnName}…`).start();
216
+ let txHash;
217
+ try {
218
+ const baseArgs = [poolKey, liquidityParams, hookData];
219
+ txHash = await (action === "add"
220
+ ? walClient.writeContract({ address: cfg.router, abi: ROUTER_LIQUIDITY_ABI, functionName: "addLiquidity", args: baseArgs, value: 0n })
221
+ : walClient.writeContract({ address: cfg.router, abi: ROUTER_LIQUIDITY_ABI, functionName: "removeLiquidity", args: baseArgs }));
222
+ txSpin.update(`Confirming ${fmt.gray(fmt.hash(txHash))}…`);
223
+ const receipt = await pubClient.waitForTransactionReceipt({ hash: txHash });
224
+ if (receipt.status !== "success") {
225
+ txSpin.fail("Transaction reverted");
226
+ die(`Tx failed: ${txHash}`);
227
+ }
228
+ txSpin.succeed(fmt.bold(fmt.green(`Liquidity ${action === "add" ? "added" : "removed"} via ILAL channel ✓`)));
229
+ }
230
+ catch (e) {
231
+ txSpin.fail(`${fnName} failed`);
232
+ dieOnContract(e);
233
+ }
234
+ log.line();
235
+ log.callout(action === "add" ? "Hook-enforced liquidity add" : "Hook-enforced liquidity removal", "pool policy, credential type, session binding, and nonce all passed on-chain", "green");
236
+ log.kv("tx", fmt.gray(txHash));
237
+ log.kv("block", fmt.gray((await pubClient.getTransactionReceipt({ hash: txHash })).blockNumber.toString()));
238
+ const explorer = txUrl(chain, txHash);
239
+ if (explorer)
240
+ log.kv("explorer", fmt.cyan(explorer));
241
+ console.log();
242
+ }
243
+ // ─── Public exports ───────────────────────────────────────────────────────────
244
+ export async function addLiquidity(opts) {
245
+ await executeLiquidity("add", opts);
246
+ }
247
+ export async function removeLiquidity(opts) {
248
+ await executeLiquidity("remove", opts);
249
+ }
@@ -0,0 +1,11 @@
1
+ declare function sendMintTx(mode: "mint" | "renew", opts: {
2
+ attestation: string;
3
+ issuer: string;
4
+ chain: string;
5
+ rpc?: string;
6
+ privateKey?: string;
7
+ simulate: boolean;
8
+ }): Promise<void>;
9
+ export declare const mintCredential: (opts: Parameters<typeof sendMintTx>[1]) => Promise<void>;
10
+ export declare const renewCredential: (opts: Parameters<typeof sendMintTx>[1]) => Promise<void>;
11
+ export {};
@@ -0,0 +1,146 @@
1
+ import { createPublicClient, createWalletClient, http, isAddress, isHex, } from "viem";
2
+ import { privateKeyToAccount } from "viem/accounts";
3
+ import { base, baseSepolia } from "viem/chains";
4
+ import { fmt, log, die } from "../ui.js";
5
+ import { EAS_ADDRESSES, COINBASE_SCHEMA_UID, COINBASE_ATTESTER } from "../constants.js";
6
+ const CHAINS = { "8453": base, "84532": baseSepolia };
7
+ const CNF_ISSUER_ABI = [
8
+ {
9
+ name: "mintWithEAS",
10
+ type: "function",
11
+ stateMutability: "nonpayable",
12
+ inputs: [{ name: "attestationUID", type: "bytes32" }],
13
+ outputs: [{ name: "tokenId", type: "uint256" }],
14
+ },
15
+ {
16
+ name: "renewWithEAS",
17
+ type: "function",
18
+ stateMutability: "nonpayable",
19
+ inputs: [{ name: "attestationUID", type: "bytes32" }],
20
+ outputs: [],
21
+ },
22
+ {
23
+ name: "isValid",
24
+ type: "function",
25
+ stateMutability: "view",
26
+ inputs: [{ name: "wallet", type: "address" }],
27
+ outputs: [{ type: "bool" }],
28
+ },
29
+ ];
30
+ async function sendMintTx(mode, opts) {
31
+ const rawKey = opts.privateKey ?? process.env["PRIVATE_KEY"];
32
+ if (!rawKey)
33
+ die("Private key required. Use --private-key or set PRIVATE_KEY env var.");
34
+ if (!isHex(rawKey) || rawKey.length !== 66)
35
+ die("Invalid private key (expected 0x + 32 bytes).");
36
+ if (!isAddress(opts.issuer))
37
+ die(`Invalid issuer address: ${opts.issuer}`);
38
+ if (!isHex(opts.attestation) || opts.attestation.length !== 66)
39
+ die("Attestation UID must be 0x + 32 bytes (64 hex chars).");
40
+ const chain = CHAINS[opts.chain] ?? baseSepolia;
41
+ const account = privateKeyToAccount(rawKey);
42
+ const transport = opts.rpc ? http(opts.rpc) : http();
43
+ const publicClient = createPublicClient({ chain, transport });
44
+ const walletClient = createWalletClient({ account, chain, transport });
45
+ console.log();
46
+ console.log(fmt.bold(` ILAL Credential ${mode === "mint" ? "Mint" : "Renew"}`));
47
+ log.line();
48
+ log.kv("wallet", account.address);
49
+ log.kv("issuer", opts.issuer);
50
+ log.kv("attestation", opts.attestation);
51
+ log.kv("chain", chain.name);
52
+ if (opts.simulate)
53
+ log.kv("mode", fmt.yellow("simulate (no tx sent)"));
54
+ log.line();
55
+ // Verify EAS attestation exists on-chain before sending tx
56
+ log.step("Verifying attestation on EAS…");
57
+ const easAddress = EAS_ADDRESSES[chain.id];
58
+ if (!easAddress)
59
+ die(`No EAS contract known for chain ${chain.id}. Use --rpc with a custom setup.`);
60
+ const EAS_ABI = [
61
+ {
62
+ name: "getAttestation",
63
+ type: "function",
64
+ stateMutability: "view",
65
+ inputs: [{ name: "uid", type: "bytes32" }],
66
+ outputs: [{
67
+ type: "tuple",
68
+ components: [
69
+ { name: "uid", type: "bytes32" },
70
+ { name: "schema", type: "bytes32" },
71
+ { name: "time", type: "uint64" },
72
+ { name: "expirationTime", type: "uint64" },
73
+ { name: "revocationTime", type: "uint64" },
74
+ { name: "refUID", type: "bytes32" },
75
+ { name: "recipient", type: "address" },
76
+ { name: "attester", type: "address" },
77
+ { name: "revocable", type: "bool" },
78
+ { name: "data", type: "bytes" },
79
+ ],
80
+ }],
81
+ },
82
+ ];
83
+ const attestation = await publicClient.readContract({
84
+ address: easAddress,
85
+ abi: EAS_ABI,
86
+ functionName: "getAttestation",
87
+ args: [opts.attestation],
88
+ });
89
+ if (attestation.uid === "0x" + "0".repeat(64))
90
+ die("Attestation not found on EAS.");
91
+ if (attestation.revocationTime !== 0n)
92
+ die("Attestation has been revoked.");
93
+ if (attestation.expirationTime !== 0n && attestation.expirationTime < BigInt(Math.floor(Date.now() / 1000)))
94
+ die("Attestation has expired.");
95
+ if (attestation.recipient.toLowerCase() !== account.address.toLowerCase())
96
+ die(`Attestation recipient (${attestation.recipient}) does not match your wallet (${account.address}).`);
97
+ log.ok(`Attester: ${attestation.attester}`);
98
+ if (attestation.attester.toLowerCase() === COINBASE_ATTESTER.toLowerCase()) {
99
+ log.ok("Coinbase Verifications attester confirmed");
100
+ }
101
+ else {
102
+ log.warn(`Unknown attester — not Coinbase Verifications (${COINBASE_ATTESTER})`);
103
+ }
104
+ if (attestation.schema.toLowerCase() !== COINBASE_SCHEMA_UID.toLowerCase()) {
105
+ log.warn(`Schema mismatch — expected Coinbase schema, got ${attestation.schema}`);
106
+ }
107
+ else {
108
+ log.ok("Coinbase Account Verification schema confirmed");
109
+ }
110
+ log.line();
111
+ if (opts.simulate) {
112
+ log.ok("Simulation complete — attestation valid, tx would succeed");
113
+ console.log();
114
+ console.log(` Run without ${fmt.cyan("--simulate")} to send the transaction.`);
115
+ console.log();
116
+ return;
117
+ }
118
+ log.step(`Sending ${mode === "mint" ? "mintWithEAS" : "renewWithEAS"} transaction…`);
119
+ const hash = await walletClient.writeContract({
120
+ address: opts.issuer,
121
+ abi: CNF_ISSUER_ABI,
122
+ functionName: mode === "mint" ? "mintWithEAS" : "renewWithEAS",
123
+ args: [opts.attestation],
124
+ });
125
+ log.step(`Tx sent: ${hash}`);
126
+ log.step("Waiting for confirmation…");
127
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
128
+ if (receipt.status === "success") {
129
+ log.ok(fmt.bold(fmt.green(`CNF ${mode === "mint" ? "minted" : "renewed"} successfully`)));
130
+ log.kv("tx hash", hash);
131
+ log.kv("block", receipt.blockNumber.toString());
132
+ const valid = await publicClient.readContract({
133
+ address: opts.issuer,
134
+ abi: CNF_ISSUER_ABI,
135
+ functionName: "isValid",
136
+ args: [account.address],
137
+ });
138
+ log.kv("isValid()", valid ? fmt.green("true ✓") : fmt.red("false"));
139
+ }
140
+ else {
141
+ die(`Transaction reverted. Hash: ${hash}`);
142
+ }
143
+ console.log();
144
+ }
145
+ export const mintCredential = (opts) => sendMintTx("mint", opts);
146
+ export const renewCredential = (opts) => sendMintTx("renew", opts);
@@ -0,0 +1,15 @@
1
+ export declare function poolPolicySet(opts: {
2
+ pool: string;
3
+ issuer: string;
4
+ credType: string;
5
+ registry: string;
6
+ chain: string;
7
+ rpc?: string;
8
+ privateKey?: string;
9
+ }): Promise<void>;
10
+ export declare function poolPolicyGet(opts: {
11
+ pool: string;
12
+ registry: string;
13
+ chain: string;
14
+ rpc?: string;
15
+ }): Promise<void>;
@@ -0,0 +1,115 @@
1
+ import { createPublicClient, createWalletClient, http, isAddress, isHex, } from "viem";
2
+ import { privateKeyToAccount } from "viem/accounts";
3
+ import { base, baseSepolia } from "viem/chains";
4
+ import { fmt, log, header, Spinner, die } from "../ui.js";
5
+ const CHAINS = { "8453": base, "84532": baseSepolia };
6
+ const REGISTRY_ABI = [
7
+ {
8
+ name: "setPolicy",
9
+ type: "function",
10
+ stateMutability: "nonpayable",
11
+ inputs: [
12
+ { name: "poolId", type: "bytes32" },
13
+ { name: "cnfIssuer", type: "address" },
14
+ { name: "credentialType", type: "bytes32" },
15
+ ],
16
+ outputs: [],
17
+ },
18
+ {
19
+ name: "disablePolicy",
20
+ type: "function",
21
+ stateMutability: "nonpayable",
22
+ inputs: [{ name: "poolId", type: "bytes32" }],
23
+ outputs: [],
24
+ },
25
+ {
26
+ name: "getPolicy",
27
+ type: "function",
28
+ stateMutability: "view",
29
+ inputs: [{ name: "poolId", type: "bytes32" }],
30
+ outputs: [{
31
+ type: "tuple",
32
+ components: [
33
+ { name: "cnfIssuer", type: "address" },
34
+ { name: "requiredCredentialType", type: "bytes32" },
35
+ { name: "enabled", type: "bool" },
36
+ ],
37
+ }],
38
+ },
39
+ ];
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.");
46
+ if (!isAddress(opts.issuer))
47
+ die(`Invalid issuer address: ${opts.issuer}`);
48
+ if (!isAddress(opts.registry))
49
+ die(`Invalid registry address: ${opts.registry}`);
50
+ if (!isHex(opts.pool) || opts.pool.length !== 66)
51
+ die("poolId must be 0x + 32 bytes.");
52
+ if (!isHex(opts.credType) || opts.credType.length !== 66)
53
+ die("credType must be 0x + 32 bytes.");
54
+ const chain = CHAINS[opts.chain] ?? baseSepolia;
55
+ const account = privateKeyToAccount(rawKey);
56
+ const transport = opts.rpc ? http(opts.rpc) : http();
57
+ const publicClient = createPublicClient({ chain, transport });
58
+ const walletClient = createWalletClient({ account, chain, transport });
59
+ header("Pool Policy Set", chain.name);
60
+ log.section("Request");
61
+ log.kv("operator", fmt.addr(account.address));
62
+ log.kv("registry", fmt.addr(opts.registry));
63
+ log.kv("pool", fmt.hash(opts.pool));
64
+ log.kv("issuer", fmt.addr(opts.issuer));
65
+ log.kv("credType", fmt.hash(opts.credType));
66
+ log.line();
67
+ const spin = new Spinner("Sending setPolicy transaction…").start();
68
+ const hash = await walletClient.writeContract({
69
+ address: opts.registry,
70
+ abi: REGISTRY_ABI,
71
+ functionName: "setPolicy",
72
+ args: [opts.pool, opts.issuer, opts.credType],
73
+ });
74
+ spin.update(`Confirming ${fmt.hash(hash)}…`);
75
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
76
+ if (receipt.status !== "success")
77
+ die(`Transaction reverted. Hash: ${hash}`);
78
+ spin.succeed("Transaction confirmed");
79
+ const policy = await publicClient.readContract({
80
+ address: opts.registry,
81
+ abi: REGISTRY_ABI,
82
+ functionName: "getPolicy",
83
+ args: [opts.pool],
84
+ });
85
+ log.section("Registered Policy");
86
+ log.kv("cnfIssuer", fmt.addr(policy.cnfIssuer));
87
+ log.kv("credType", fmt.hash(policy.requiredCredentialType));
88
+ log.kv("enabled", policy.enabled ? fmt.badge("true", "green") : fmt.badge("false", "red"));
89
+ log.kv("tx", fmt.hash(hash));
90
+ log.result("Policy registered", "", "green");
91
+ console.log();
92
+ }
93
+ export async function poolPolicyGet(opts) {
94
+ if (!isAddress(opts.registry))
95
+ die(`Invalid registry address: ${opts.registry}`);
96
+ if (!isHex(opts.pool) || opts.pool.length !== 66)
97
+ die("poolId must be 0x + 32 bytes.");
98
+ const chain = CHAINS[opts.chain] ?? baseSepolia;
99
+ const transport = opts.rpc ? http(opts.rpc) : http();
100
+ const publicClient = createPublicClient({ chain, transport });
101
+ header("Pool Policy", chain.name);
102
+ const policy = await publicClient.readContract({
103
+ address: opts.registry,
104
+ abi: REGISTRY_ABI,
105
+ functionName: "getPolicy",
106
+ args: [opts.pool],
107
+ });
108
+ log.section("Policy");
109
+ log.kv("pool", fmt.hash(opts.pool));
110
+ log.kv("registry", fmt.addr(opts.registry));
111
+ log.kv("cnfIssuer", policy.cnfIssuer === "0x0000000000000000000000000000000000000000" ? fmt.badge("unset", "red") : fmt.addr(policy.cnfIssuer));
112
+ log.kv("credType", fmt.hash(policy.requiredCredentialType));
113
+ log.kv("enabled", policy.enabled ? fmt.badge("true", "green") : fmt.badge("false", "red"));
114
+ console.log();
115
+ }
@@ -0,0 +1,16 @@
1
+ export declare function proofMint(opts: {
2
+ proof: string;
3
+ public: string;
4
+ issuer: string;
5
+ chain: string;
6
+ rpc?: string;
7
+ privateKey?: string;
8
+ }): Promise<void>;
9
+ export declare function proofRenew(opts: {
10
+ proof: string;
11
+ public: string;
12
+ issuer: string;
13
+ chain: string;
14
+ rpc?: string;
15
+ privateKey?: string;
16
+ }): Promise<void>;