kawasekit 0.4.0 → 0.6.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.
Files changed (37) hide show
  1. package/dist/{chunk-6CNAYQOL.js → chunk-7YKRKV6D.js} +18 -4
  2. package/dist/chunk-7YKRKV6D.js.map +1 -0
  3. package/dist/{chunk-FD6Q4NB2.js → chunk-NY6VLXQB.js} +137 -32
  4. package/dist/chunk-NY6VLXQB.js.map +1 -0
  5. package/dist/{chunk-G3UC6SME.js → chunk-P5563RGP.js} +3 -3
  6. package/dist/{chunk-G3UC6SME.js.map → chunk-P5563RGP.js.map} +1 -1
  7. package/dist/{chunk-5TTOAVHE.js → chunk-VJUXTVSW.js} +11 -3
  8. package/dist/chunk-VJUXTVSW.js.map +1 -0
  9. package/dist/{chunk-E47SIVFY.js → chunk-X54U6GU4.js} +18 -5
  10. package/dist/chunk-X54U6GU4.js.map +1 -0
  11. package/dist/cli/index.cjs +29 -3
  12. package/dist/cli/index.cjs.map +1 -1
  13. package/dist/cli/index.js +6 -6
  14. package/dist/index.cjs +169 -30
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +5 -5
  19. package/dist/policy/index.cjs +27 -3
  20. package/dist/policy/index.cjs.map +1 -1
  21. package/dist/policy/index.d.cts +41 -3
  22. package/dist/policy/index.d.ts +41 -3
  23. package/dist/policy/index.js +2 -2
  24. package/dist/signer/index.cjs +141 -27
  25. package/dist/signer/index.cjs.map +1 -1
  26. package/dist/signer/index.d.cts +58 -2
  27. package/dist/signer/index.d.ts +58 -2
  28. package/dist/signer/index.js +3 -3
  29. package/dist/{spending-policy-BD2Mpm-L.d.ts → spending-policy-BBgXDrVD.d.ts} +10 -3
  30. package/dist/{spending-policy-CGIaBpg-.d.cts → spending-policy-CwXcTFD_.d.cts} +10 -3
  31. package/dist/x402/index.cjs.map +1 -1
  32. package/dist/x402/index.js +2 -2
  33. package/package.json +1 -1
  34. package/dist/chunk-5TTOAVHE.js.map +0 -1
  35. package/dist/chunk-6CNAYQOL.js.map +0 -1
  36. package/dist/chunk-E47SIVFY.js.map +0 -1
  37. package/dist/chunk-FD6Q4NB2.js.map +0 -1
@@ -17,9 +17,17 @@ var PolicyGatedSignerConfigError = class extends Error {
17
17
  }
18
18
  };
19
19
  var CoSignUnavailableError = class extends Error {
20
+ /**
21
+ * `true` when the failure is the **transient transport class** (connect failed /
22
+ * connection dropped) — the only class the adapter's bounded retry replays
23
+ * (RFC m6-3a §4.7: never a delivered rejection, never a ban/identifiable-abort,
24
+ * never a protocol anomaly or timeout). Defaults to `false`.
25
+ */
26
+ transient;
20
27
  constructor(message, options) {
21
- super(message, options);
28
+ super(message, options?.cause === void 0 ? void 0 : { cause: options.cause });
22
29
  this.name = "CoSignUnavailableError";
30
+ this.transient = options?.transient ?? false;
23
31
  }
24
32
  };
25
33
 
@@ -363,6 +371,7 @@ function createLocalPolicyGatedSigner(params) {
363
371
 
364
372
  // src/signer/mpc-2p-wire.ts
365
373
  var WIRE_VERSION = 2;
374
+ var MAX_FRAME_BYTES = 8 * 1024 * 1024;
366
375
  function toWireIntent(intent) {
367
376
  return {
368
377
  token: intent.token.toLowerCase(),
@@ -417,6 +426,12 @@ function createMpc2pPolicyGatedSigner(params) {
417
426
  const { agent, transport, authenticator, session } = params;
418
427
  const pinned = resolveAssetParam(params.asset);
419
428
  const from = viem.getAddress(params.from);
429
+ const wire = params.wire ?? {};
430
+ const maxAttempts = Math.max(1, wire.maxAttempts ?? 2);
431
+ const ceremonyTimeoutMs = wire.ceremonyTimeoutMs ?? 3e4;
432
+ const minWindowSecs = wire.minWindowSecs ?? 30;
433
+ const clockSkewBudgetSecs = wire.clockSkewBudgetSecs ?? 30;
434
+ const maxFrameBytes = wire.maxFrameBytes ?? MAX_FRAME_BYTES;
420
435
  const agentEoa = viem.getAddress(agent.groupEoa());
421
436
  if (agentEoa !== from) {
422
437
  throw new PolicyGatedSignerConfigError(
@@ -453,23 +468,59 @@ function createMpc2pPolicyGatedSigner(params) {
453
468
  nonce: intent.nonce
454
469
  }
455
470
  });
456
- const env = {
457
- ceremonyId: globalThis.crypto.randomUUID(),
458
- ssid: globalThis.crypto.randomUUID(),
459
- intent,
460
- freshnessTs: Math.floor(Date.now() / 1e3),
461
- freshnessNonce: viem.toHex(globalThis.crypto.getRandomValues(new Uint8Array(16)))
462
- };
463
- const authTag = await authenticator.tag(canonicalRequestBytes(env));
464
- const conn = await openConnection(transport);
465
- try {
466
- return await runCeremony(conn, agent, session.id, env, digest, authTag);
467
- } finally {
471
+ const validBeforeSecs = Number(intent.validBefore);
472
+ const nowSecs = Math.floor(Date.now() / 1e3);
473
+ if (validBeforeSecs - nowSecs < minWindowSecs + clockSkewBudgetSecs) {
474
+ throw new CoSignUnavailableError(
475
+ `intent.validBefore (${validBeforeSecs}) leaves under the minimum ceremony window (${minWindowSecs}s + ${clockSkewBudgetSecs}s clock-skew budget) \u2014 refusing a co-sign that risks being born expired (W11)`
476
+ );
477
+ }
478
+ const deadlineMs = Math.min(
479
+ Date.now() + ceremonyTimeoutMs,
480
+ (validBeforeSecs - clockSkewBudgetSecs) * 1e3
481
+ );
482
+ let lastTransient;
483
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
484
+ if (Date.now() >= deadlineMs) break;
485
+ const env = {
486
+ ceremonyId: globalThis.crypto.randomUUID(),
487
+ ssid: globalThis.crypto.randomUUID(),
488
+ intent,
489
+ freshnessTs: Math.floor(Date.now() / 1e3),
490
+ freshnessNonce: viem.toHex(globalThis.crypto.getRandomValues(new Uint8Array(16)))
491
+ };
492
+ const authTag = await authenticator.tag(canonicalRequestBytes(env));
468
493
  try {
469
- await conn.close();
470
- } catch {
494
+ const conn = await openConnection(transport);
495
+ try {
496
+ return await runCeremony({
497
+ conn,
498
+ agent,
499
+ sessionId: session.id,
500
+ env,
501
+ digest,
502
+ authTag,
503
+ from,
504
+ deadlineMs,
505
+ maxFrameBytes
506
+ });
507
+ } finally {
508
+ try {
509
+ await conn.close();
510
+ } catch {
511
+ }
512
+ }
513
+ } catch (error) {
514
+ if (error instanceof CoSignUnavailableError && error.transient && attempt < maxAttempts) {
515
+ lastTransient = error;
516
+ continue;
517
+ }
518
+ throw error;
471
519
  }
472
520
  }
521
+ throw lastTransient ?? new CoSignUnavailableError(
522
+ "co-sign ceremony budget exhausted before validBefore (W11) \u2014 no attempt could start"
523
+ );
473
524
  },
474
525
  describe() {
475
526
  return {
@@ -488,23 +539,32 @@ async function openConnection(transport) {
488
539
  try {
489
540
  return await transport.connect();
490
541
  } catch (cause) {
491
- throw new CoSignUnavailableError("co-signer connection failed", { cause });
542
+ throw new CoSignUnavailableError("co-signer connection failed", { cause, transient: true });
492
543
  }
493
544
  }
494
- async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
545
+ async function runCeremony(ctx) {
546
+ const { conn, agent, env, digest, from } = ctx;
495
547
  const send = async (frame) => {
496
548
  try {
497
- await conn.send(frame);
549
+ await withDeadline(conn.send(frame), ctx.deadlineMs);
498
550
  } catch (cause) {
499
- throw new CoSignUnavailableError("co-sign connection dropped while sending", { cause });
551
+ if (cause instanceof CoSignUnavailableError) throw cause;
552
+ throw new CoSignUnavailableError("co-sign connection dropped while sending", {
553
+ cause,
554
+ transient: true
555
+ });
500
556
  }
501
557
  };
502
558
  const recv = async () => {
503
559
  let frame;
504
560
  try {
505
- frame = await conn.recv();
561
+ frame = await withDeadline(conn.recv(), ctx.deadlineMs);
506
562
  } catch (cause) {
507
- throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", { cause });
563
+ if (cause instanceof CoSignUnavailableError) throw cause;
564
+ throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", {
565
+ cause,
566
+ transient: true
567
+ });
508
568
  }
509
569
  if (frame.wire_version !== WIRE_VERSION) {
510
570
  throw new CoSignUnavailableError(
@@ -516,13 +576,13 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
516
576
  await send({
517
577
  wire_version: WIRE_VERSION,
518
578
  kind: "request",
519
- session_id: sessionId,
579
+ session_id: ctx.sessionId,
520
580
  ceremony_id: env.ceremonyId,
521
581
  ssid: env.ssid,
522
582
  intent: toWireIntent(env.intent),
523
583
  freshness_ts: env.freshnessTs,
524
584
  freshness_nonce: env.freshnessNonce,
525
- auth_tag: authTag
585
+ auth_tag: ctx.authTag
526
586
  });
527
587
  let first;
528
588
  try {
@@ -536,6 +596,12 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
536
596
  const frame = await recv();
537
597
  switch (frame.kind) {
538
598
  case "round": {
599
+ const payloadBytes = (frame.payload.length - 2) / 2;
600
+ if (payloadBytes > ctx.maxFrameBytes) {
601
+ throw new CoSignUnavailableError(
602
+ `co-signer sent a ${payloadBytes}-byte round frame (bound ${ctx.maxFrameBytes}) \u2014 refused pre-decode`
603
+ );
604
+ }
539
605
  let step;
540
606
  try {
541
607
  step = agent.step(frame.payload);
@@ -550,15 +616,16 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
550
616
  break;
551
617
  }
552
618
  case "result": {
619
+ const signature = assembleSignature({ r: frame.r, s: frame.s, v: frame.v });
553
620
  if (agentSig === null) {
554
- throw new CoSignUnavailableError(
555
- "the backend returned a result before the agent derived a signature"
556
- );
621
+ await verifyRecovers(digest, signature, frame.s, from, "idempotent-replay");
622
+ return { ok: true, signature, intent: env.intent };
557
623
  }
558
624
  if (frame.r.toLowerCase() !== agentSig.r.toLowerCase() || frame.s.toLowerCase() !== agentSig.s.toLowerCase() || frame.v !== agentSig.v) {
559
625
  throw new CoSignUnavailableError("the backend and agent derived different signatures");
560
626
  }
561
- return { ok: true, signature: assembleSignature(agentSig), intent: env.intent };
627
+ await verifyRecovers(digest, signature, frame.s, from, "co-signed");
628
+ return { ok: true, signature, intent: env.intent };
562
629
  }
563
630
  case "rejection": {
564
631
  if (POLICY_REASONS.has(frame.reason)) {
@@ -577,6 +644,52 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
577
644
  }
578
645
  }
579
646
  }
647
+ function withDeadline(promise, deadlineMs) {
648
+ const remaining = deadlineMs - Date.now();
649
+ if (remaining <= 0) {
650
+ return Promise.reject(
651
+ new CoSignUnavailableError(
652
+ "co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
653
+ )
654
+ );
655
+ }
656
+ return new Promise((resolve, reject) => {
657
+ const timer = setTimeout(() => {
658
+ reject(
659
+ new CoSignUnavailableError(
660
+ "co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
661
+ )
662
+ );
663
+ }, remaining);
664
+ promise.then(
665
+ (value) => {
666
+ clearTimeout(timer);
667
+ resolve(value);
668
+ },
669
+ (cause) => {
670
+ clearTimeout(timer);
671
+ reject(cause);
672
+ }
673
+ );
674
+ });
675
+ }
676
+ var SECP256K1_HALF_N = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
677
+ async function verifyRecovers(digest, signature, s, from, what) {
678
+ if (BigInt(s) > SECP256K1_HALF_N) {
679
+ throw new CoSignUnavailableError(`the ${what} signature is not low-S (EIP-2)`);
680
+ }
681
+ let recovered;
682
+ try {
683
+ recovered = await viem.recoverAddress({ hash: digest, signature });
684
+ } catch (cause) {
685
+ throw new CoSignUnavailableError(`the ${what} signature failed to recover`, { cause });
686
+ }
687
+ if (viem.getAddress(recovered) !== from) {
688
+ throw new CoSignUnavailableError(
689
+ `the ${what} signature recovers to ${viem.getAddress(recovered)}, not the group EOA ${from}`
690
+ );
691
+ }
692
+ }
580
693
  function assembleSignature(sig) {
581
694
  const yParity = sig.v - 27;
582
695
  if (yParity !== 0 && yParity !== 1) {
@@ -586,6 +699,7 @@ function assembleSignature(sig) {
586
699
  }
587
700
 
588
701
  exports.CoSignUnavailableError = CoSignUnavailableError;
702
+ exports.MAX_FRAME_BYTES = MAX_FRAME_BYTES;
589
703
  exports.PolicyGatedSignerConfigError = PolicyGatedSignerConfigError;
590
704
  exports.WIRE_VERSION = WIRE_VERSION;
591
705
  exports.assertNonBypassable = assertNonBypassable;