@anywalletkit/extension-kit 0.5.2 → 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.
package/dist/index.cjs CHANGED
@@ -107,6 +107,7 @@ __export(src_exports, {
107
107
  earnPlanMatchers: () => earnPlanMatchers,
108
108
  encodeApproveCalldata: () => encodeApproveCalldata,
109
109
  evmCallMatcher: () => evmCallMatcher,
110
+ evmSignedCallMatcher: () => evmSignedCallMatcher,
110
111
  executionFloor: () => executionFloor,
111
112
  fromBase64Url: () => fromBase64Url,
112
113
  gasLimitExcessive: () => gasLimitExcessive,
@@ -2496,6 +2497,19 @@ function evmCallMatcher(expected) {
2496
2497
  const data = expected.data && expected.data !== "0x" ? lc2(expected.data) : void 0;
2497
2498
  return (op) => op.kind === "evm-tx" && op.caip2 === expected.caip2 && lc2(op.to) === lc2(expected.to) && (op.data && op.data !== "0x" ? lc2(op.data) : void 0) === data && op.value === expected.value;
2498
2499
  }
2500
+ function evmSignedCallMatcher(expected) {
2501
+ const base = lc2(expected.data);
2502
+ return (op) => {
2503
+ if (op.kind !== "evm-tx" || op.caip2 !== expected.caip2 || lc2(op.to) !== lc2(expected.to) || op.value !== expected.value || !op.data) return false;
2504
+ const data = lc2(op.data);
2505
+ if (!data.startsWith(base)) return false;
2506
+ const tail = data.slice(base.length);
2507
+ const word = /^0{60}([0-9a-f]{4})/.exec(tail);
2508
+ if (!word || !/^[0-9a-f]*$/.test(tail)) return false;
2509
+ const n = parseInt(word[1], 16);
2510
+ return n >= 64 && n <= 2048 && tail.length === 64 + 2 * n;
2511
+ };
2512
+ }
2499
2513
  function typedDataMatcher(expected) {
2500
2514
  const hash = lc2(expected.typedDataHash);
2501
2515
  return (op) => op.kind === "evm-typed-data" && op.caip2 === expected.caip2 && lc2(op.typedDataHash) === hash;
@@ -2647,8 +2661,14 @@ function swapApprovalPayload(plan, meta) {
2647
2661
  let source;
2648
2662
  if (s.kind === "call") {
2649
2663
  const deposit = depositOf(s, plan.approvals.length);
2650
- source = { kind: "call", to: s.to, ...s.data ? { data: s.data } : {}, value: s.value.toString(), ...deposit ? { deposit } : {} };
2664
+ const permit = s.permit2Transfer !== void 0 ? parsePermit2Transfer(s.permit2Transfer) : void 0;
2665
+ source = { kind: "call", to: s.to, ...s.data ? { data: s.data } : {}, value: s.value.toString(), ...deposit ? { deposit } : {}, ...permit ? { permit2Transfer: permit } : {} };
2651
2666
  checks.push(deposit || assigned ? depositCheck : { status: "ok", label: `Calls the ${meta.providerLabel} contract` });
2667
+ if (permit) {
2668
+ const exact = BigInt(permit.amount) === plan.sourceAmount && lc3(permit.spender) === lc3(s.to);
2669
+ const minutes = Math.max(0, Math.ceil((permit.deadline * 1e3 - Date.now()) / 6e4));
2670
+ checks.push(exact ? { status: "ok", label: `Permit2 signature: exactly the amount sold, usable only by ${s.to.slice(0, 6)}\u2026${s.to.slice(-4)} in this swap, expires in ${minutes} min` } : { status: "warn", label: "Permit2 signature does not match this swap (amount or contract)" });
2671
+ }
2652
2672
  } else if (s.kind === "token-transfer") {
2653
2673
  source = { kind: "token-transfer", token: s.token, recipient: s.recipient, amount: s.amount.toString(), ...s.memo ? { memo: s.memo } : {} };
2654
2674
  checks.push(depositCheck);
@@ -2661,6 +2681,7 @@ function swapApprovalPayload(plan, meta) {
2661
2681
  checks.push({ status: "ok", label: "Signed order \u2014 no transaction is sent from your wallet" });
2662
2682
  }
2663
2683
  for (const a of plan.approvals) checks.push(a.amount > plan.sourceAmount ? { status: "warn", label: "Approval is larger than this swap needs" } : { status: "ok", label: "Approval is exactly the amount needed" });
2684
+ if (plan.approvals.some((a) => a.amount > 0n && lc3(a.spender) === lc3(import_protocol_swap2_core.PERMIT2_ADDRESS))) checks.push({ status: "ok", label: "Approval goes to Permit2 (shared by many apps): only the signed permit decides who can use it" });
2664
2685
  if (plan.approvals.length > 1) checks.push({ status: "ok", label: `${plan.approvals.length} approvals (reset to 0 first)` });
2665
2686
  checks.push(!isOwn ? { status: "warn", label: `Funds arrive at ${plan.recipient.slice(0, 6)}\u2026${plan.recipient.slice(-4)}, not your address` } : assigned ? { status: "unknown", label: `Delivery to your address is the ${meta.providerLabel} server\u2019s promise \u2014 this transaction does not enforce it` } : { status: "ok", label: "Funds arrive at your address" });
2666
2687
  let slippage;
@@ -2691,16 +2712,34 @@ function swapApprovalPayload(plan, meta) {
2691
2712
  ...assigned ? { depositAssignedByProvider: true } : {}
2692
2713
  };
2693
2714
  }
2715
+ function parsePermit2Transfer(json) {
2716
+ const typedData = parseTypedOrder(json);
2717
+ const m = typedData.message;
2718
+ const token = m.permitted?.token;
2719
+ const amount = m.permitted?.amount;
2720
+ const deadline = m.deadline;
2721
+ if (typedData.primaryType !== "PermitTransferFrom" || typeof token !== "string" || typeof amount !== "string" || !/^[0-9]{1,78}$/.test(amount) || typeof m.spender !== "string" || typeof deadline !== "string" || !/^[0-9]{1,12}$/.test(deadline)) {
2722
+ throw new Error("The swap's Permit2 transfer is not a PermitTransferFrom.");
2723
+ }
2724
+ return { typedData, text: JSON.stringify(typedData, null, 2), token, amount, spender: m.spender, deadline: Number(deadline) };
2725
+ }
2694
2726
  function parseTypedOrder(json) {
2695
2727
  const t = JSON.parse(json);
2696
2728
  if (!t || typeof t !== "object" || !t.domain || typeof t.domain !== "object" || !t.types || typeof t.types !== "object" || !t.message || typeof t.message !== "object") throw new Error("The swap order is not EIP-712 typed data.");
2697
2729
  return t;
2698
2730
  }
2699
2731
  function planMatchers(plan) {
2700
- const m = plan.approvals.map((a) => evmCallMatcher({ caip2: plan.fromChain, to: a.token, data: encodeApproveCalldata(a.spender, a.amount), value: 0n }));
2732
+ const m = [];
2701
2733
  const s = plan.source;
2734
+ const permit = s.kind === "call" && s.permit2Transfer !== void 0 && plan.fromChain.startsWith("eip155:");
2735
+ if (permit) {
2736
+ if (!s.data) throw new Error("A Permit2 swap plan carries no calldata.");
2737
+ m.push(typedDataMatcher({ caip2: plan.fromChain, typedDataHash: (0, import_wallet_evm.typedDataHash)(parsePermit2Transfer(s.permit2Transfer).typedData) }));
2738
+ }
2739
+ m.push(...plan.approvals.map((a) => evmCallMatcher({ caip2: plan.fromChain, to: a.token, data: encodeApproveCalldata(a.spender, a.amount), value: 0n })));
2702
2740
  if (s.kind === "call") {
2703
- if (plan.fromChain.startsWith("eip155:")) m.push(evmCallMatcher({ caip2: plan.fromChain, to: s.to, data: s.data, value: s.value }));
2741
+ if (permit) m.push(evmSignedCallMatcher({ caip2: plan.fromChain, to: s.to, data: s.data, value: s.value }));
2742
+ else if (plan.fromChain.startsWith("eip155:")) m.push(evmCallMatcher({ caip2: plan.fromChain, to: s.to, data: s.data, value: s.value }));
2704
2743
  else if (plan.fromChain.startsWith("solana:")) m.push((op) => op.kind === "solana-tx" && op.to === s.to && op.value === s.value);
2705
2744
  else m.push(chainTxMatcher({ chain: plan.fromChain, to: s.to, amount: s.value, memo: s.memo }));
2706
2745
  } else if (s.kind === "token-transfer") {