@ilalv3/cli 0.2.12 → 0.2.14

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.
@@ -18,7 +18,7 @@
18
18
  * --pool-id 0xPOOLID \
19
19
  * --chain 84532
20
20
  */
21
- import { createPublicClient, createWalletClient, decodeAbiParameters, encodeAbiParameters, formatEther, formatUnits, http, isAddress, isHex, parseAbiParameters, parseUnits, } from "viem";
21
+ import { createPublicClient, createWalletClient, decodeAbiParameters, encodeAbiParameters, formatEther, formatUnits, http, isAddress, isHex, parseAbiParameters, parseUnits, recoverTypedDataAddress, } from "viem";
22
22
  import { privateKeyToAccount } from "viem/accounts";
23
23
  import { base, baseSepolia } from "viem/chains";
24
24
  import { fmt, log, header, Spinner, die, dieOnContract, requirePrivateKey } from "../ui.js";
@@ -86,6 +86,7 @@ const DYNAMIC_FEE_FLAG = 8388608;
86
86
  const PIPS_DENOMINATOR = 1000000n;
87
87
  const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
88
88
  const MAX_UINT256 = 2n ** 256n - 1n;
89
+ const SECP256K1_HALF_ORDER = BigInt("0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0");
89
90
  function txUrl(chain, hash) {
90
91
  const baseUrl = chain.blockExplorers?.default?.url;
91
92
  return baseUrl ? `${baseUrl}/tx/${hash}` : undefined;
@@ -124,6 +125,17 @@ function allowanceLabel(raw, decimals, symbol) {
124
125
  function secondsSince(startMs) {
125
126
  return `${((Date.now() - startMs) / 1000).toFixed(1)}s`;
126
127
  }
128
+ function validateEcdsaSignature(sig) {
129
+ if (!/^0x[0-9a-fA-F]{130}$/.test(sig))
130
+ return "signature must be 65 bytes";
131
+ const s = BigInt(`0x${sig.slice(66, 130)}`);
132
+ const v = Number.parseInt(sig.slice(130, 132), 16);
133
+ if (s > SECP256K1_HALF_ORDER)
134
+ return "signature uses high-s form; re-sign to produce canonical EIP-2 low-s signature";
135
+ if (v !== 27 && v !== 28)
136
+ return "signature recovery id must be 27 or 28";
137
+ return undefined;
138
+ }
127
139
  // ─── Main export ──────────────────────────────────────────────────────────────
128
140
  export async function swap(opts) {
129
141
  const cfg = withConfig(opts);
@@ -259,7 +271,7 @@ export async function swap(opts) {
259
271
  if (!isHex(opts.hookData))
260
272
  die("--hook-data must be 0x-prefixed ABI-encoded hookData.");
261
273
  try {
262
- const [externalToken] = decodeAbiParameters(HOOK_DATA_ABI, opts.hookData);
274
+ const [externalToken, externalSig] = decodeAbiParameters(HOOK_DATA_ABI, opts.hookData);
263
275
  const issues = [];
264
276
  if (externalToken.user.toLowerCase() !== account.address.toLowerCase())
265
277
  issues.push("user does not match signer wallet");
@@ -277,6 +289,27 @@ export async function swap(opts) {
277
289
  issues.push("action is not swap");
278
290
  if (externalToken.deadline < BigInt(Math.floor(Date.now() / 1000)))
279
291
  issues.push("session deadline has expired");
292
+ const sigIssue = validateEcdsaSignature(externalSig);
293
+ if (sigIssue) {
294
+ issues.push(sigIssue);
295
+ }
296
+ else {
297
+ const recovered = await recoverTypedDataAddress({
298
+ domain: {
299
+ name: "ILAL ComplianceHook",
300
+ version: "1",
301
+ chainId: BigInt(chain.id),
302
+ verifyingContract: cfg.hook,
303
+ },
304
+ types: { SessionToken: SESSION_TOKEN_TYPE },
305
+ primaryType: "SessionToken",
306
+ message: externalToken,
307
+ signature: externalSig,
308
+ });
309
+ if (recovered.toLowerCase() !== externalToken.user.toLowerCase()) {
310
+ issues.push(`signature does not recover to session user ${fmt.addr(externalToken.user)}`);
311
+ }
312
+ }
280
313
  if (issues.length > 0)
281
314
  die(`Invalid --hook-data for this swap: ${issues.join("; ")}`);
282
315
  sessionNonce = externalToken.nonce;
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.12")
22
+ .version("0.2.14")
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
package/dist/ui.js CHANGED
@@ -178,6 +178,9 @@ const CONTRACT_ERRORS = {
178
178
  "0xfa9f081c": "Schema hash mismatch",
179
179
  "0xb7e9429b": "Issuer hash mismatch",
180
180
  "0x0e917e64": "Wallet hash mismatch — wrong wallet key",
181
+ "0x21374865": "Session signature invalid — re-sign hookData with the wallet that owns the CNF",
182
+ "0xd9b2290c": "Session user mismatch — hookData can only be used by the wallet that signed it",
183
+ "0x1fb09b80": "Session nonce already used — sign a fresh session",
181
184
  };
182
185
  function parseViemError(e) {
183
186
  const msg = e instanceof Error ? e.message : String(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ilalv3/cli",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "ILAL Protocol CLI — compliant swaps and credential management for Uniswap v4",
5
5
  "type": "module",
6
6
  "bin": {