kawasekit 0.6.0 → 0.8.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,121 @@
1
+ import { invokeHookSafely } from './chunk-LEHWRDVS.js';
2
+ import { buildJpycTransferCallPolicy } from './chunk-UIDDRHLW.js';
3
+ import { getJpycAddress, jpycAbi } from './chunk-KT7XDT2T.js';
4
+ import { isSupportedChainId } from './chunk-SOTYGX67.js';
5
+ import { createZeroDevPaymasterClient, createKernelAccountClient } from '@zerodev/sdk';
6
+ import { http, isAddress, encodeFunctionData } from 'viem';
7
+ import { toRateLimitPolicy, toTimestampPolicy } from '@zerodev/permissions/policies';
8
+
9
+ async function sponsorWithObservability(sponsor, account, observability) {
10
+ try {
11
+ const data = await sponsor();
12
+ invokeHookSafely(observability?.onSponsor, { account });
13
+ return data;
14
+ } catch (error) {
15
+ invokeHookSafely(observability?.onSponsorError, { account, error });
16
+ throw error;
17
+ }
18
+ }
19
+ function createSponsoredKernelClient(params) {
20
+ const paymasterClient = createZeroDevPaymasterClient({
21
+ chain: params.chain,
22
+ transport: http(params.zerodevRpc)
23
+ });
24
+ const client = createKernelAccountClient({
25
+ account: params.account,
26
+ chain: params.chain,
27
+ // exactOptionalPropertyTypes: only pass `client` when given (never `undefined`).
28
+ ...params.publicClient !== void 0 ? { client: params.publicClient } : {},
29
+ bundlerTransport: http(params.zerodevRpc),
30
+ paymaster: {
31
+ getPaymasterData: (userOperation) => sponsorWithObservability(
32
+ () => paymasterClient.sponsorUserOperation({ userOperation }),
33
+ params.account.address,
34
+ params.observability
35
+ )
36
+ }
37
+ });
38
+ return client;
39
+ }
40
+ var TransferJpycInputError = class extends Error {
41
+ constructor(message) {
42
+ super(`transferJpyc: ${message}`);
43
+ this.name = "TransferJpycInputError";
44
+ }
45
+ };
46
+ async function transferJpyc(kernelClient, params) {
47
+ if (!isAddress(params.to, { strict: false })) {
48
+ throw new TransferJpycInputError(`\`to\` is not a valid address: ${params.to}`);
49
+ }
50
+ if (params.amount <= 0n) {
51
+ throw new TransferJpycInputError(`\`amount\` must be positive, got ${params.amount}.`);
52
+ }
53
+ const chainId = kernelClient.chain.id;
54
+ if (!isSupportedChainId(chainId)) {
55
+ throw new TransferJpycInputError(`Chain ID ${chainId} is not a kawasekit-supported chain.`);
56
+ }
57
+ const jpycAddress = getJpycAddress(chainId);
58
+ const data = encodeFunctionData({
59
+ abi: jpycAbi,
60
+ functionName: "transfer",
61
+ args: [params.to, params.amount]
62
+ });
63
+ const callData = await kernelClient.account.encodeCalls([{ to: jpycAddress, value: 0n, data }]);
64
+ const userOpHash = await kernelClient.sendUserOperation({ callData });
65
+ if (params.waitForReceipt === false) {
66
+ return { userOpHash, transactionHash: null, success: null };
67
+ }
68
+ const receipt = await kernelClient.waitForUserOperationReceipt({ hash: userOpHash });
69
+ return {
70
+ userOpHash,
71
+ transactionHash: receipt.receipt.transactionHash,
72
+ success: receipt.success
73
+ };
74
+ }
75
+ function createBuyListPolicies(params) {
76
+ if (params.merchants.length === 0) {
77
+ throw new Error(
78
+ "createBuyListPolicies: merchants must not be empty \u2014 a buy-list must target at least one merchant."
79
+ );
80
+ }
81
+ if (!Number.isInteger(params.maxTransfers) || params.maxTransfers < 1) {
82
+ throw new Error(
83
+ `createBuyListPolicies: maxTransfers must be a positive integer, got ${params.maxTransfers}.`
84
+ );
85
+ }
86
+ if (!Number.isInteger(params.validUntil) || params.validUntil <= 0) {
87
+ throw new Error(
88
+ `createBuyListPolicies: validUntil must be a positive unix-seconds integer, got ${params.validUntil}.`
89
+ );
90
+ }
91
+ const validAfter = params.validAfter ?? 0;
92
+ if (!Number.isInteger(validAfter) || validAfter < 0) {
93
+ throw new Error(
94
+ `createBuyListPolicies: validAfter must be a non-negative unix-seconds integer, got ${params.validAfter}.`
95
+ );
96
+ }
97
+ if (validAfter >= params.validUntil) {
98
+ throw new Error(
99
+ `createBuyListPolicies: validAfter (${validAfter}) must be before validUntil (${params.validUntil}).`
100
+ );
101
+ }
102
+ const callPolicy = buildJpycTransferCallPolicy({
103
+ jpycAddress: params.jpycAddress,
104
+ maxPerTransfer: params.maxPerTransfer,
105
+ recipientAllowlist: params.merchants,
106
+ callPolicyVersion: params.callPolicyVersion
107
+ });
108
+ const rateLimitPolicy = toRateLimitPolicy({
109
+ interval: params.validUntil - validAfter,
110
+ count: params.maxTransfers,
111
+ startAt: validAfter
112
+ });
113
+ const timestampPolicy = toTimestampPolicy(
114
+ params.validAfter === void 0 ? { validUntil: params.validUntil } : { validAfter: params.validAfter, validUntil: params.validUntil }
115
+ );
116
+ return [callPolicy, rateLimitPolicy, timestampPolicy];
117
+ }
118
+
119
+ export { TransferJpycInputError, createBuyListPolicies, createSponsoredKernelClient, transferJpyc };
120
+ //# sourceMappingURL=chunk-DMP7YFGA.js.map
121
+ //# sourceMappingURL=chunk-DMP7YFGA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/sponsored-kernel-client.ts","../src/client/transfer-jpyc.ts","../src/policy/buy-list.ts"],"names":[],"mappings":"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","file":"chunk-DMP7YFGA.js","sourcesContent":["/**\n * Build a gas-sponsored Kernel account client — a {@link ConfiguredKernelClient}\n * whose UserOp gas is paid by the ZeroDev paymaster. Pass the returned client\n * straight to {@link transferJpyc}; callers never construct a paymaster client or\n * cast to {@link ConfiguredKernelClient}.\n *\n * @packageDocumentation\n */\n\nimport {\n\ttype CreateKernelAccountReturnType,\n\tcreateKernelAccountClient,\n\tcreateZeroDevPaymasterClient,\n} from \"@zerodev/sdk\";\nimport { type Address, type Chain, http, type PublicClient, type Transport } from \"viem\";\nimport { invokeHookSafely } from \"../observability/hooks\";\nimport type { ConfiguredKernelClient } from \"./transfer-jpyc\";\n\n/**\n * Optional sponsorship observability. Hooks fire through {@link invokeHookSafely},\n * so a throwing hook never breaks sponsorship. The existing `ObservabilityHooks`\n * is x402-facilitator-shaped (verify/settle); this is the paymaster-seam surface.\n */\nexport interface SponsoredKernelClientObservability {\n\t/** Fired AFTER the paymaster GRANTS sponsorship for a userOp. */\n\treadonly onSponsor?: (event: { readonly account: Address }) => void;\n\t/** Fired when the paymaster DECLINES sponsorship (the raw error then propagates). */\n\treadonly onSponsorError?: (event: { readonly account: Address; readonly error: unknown }) => void;\n}\n\n/**\n * @internal Sponsor a userOp and fire the granted/declined observability hook.\n * The original paymaster error is re-thrown unchanged (no SDK wrapping). Exported\n * for unit testing the seam without a live chain; NOT part of the public API\n * (not re-exported from `src/index.ts`).\n */\nexport async function sponsorWithObservability<T>(\n\tsponsor: () => Promise<T>,\n\taccount: Address,\n\tobservability: SponsoredKernelClientObservability | undefined,\n): Promise<T> {\n\ttry {\n\t\tconst data = await sponsor();\n\t\tinvokeHookSafely(observability?.onSponsor, { account });\n\t\treturn data;\n\t} catch (error) {\n\t\tinvokeHookSafely(observability?.onSponsorError, { account, error });\n\t\tthrow error;\n\t}\n}\n\n/** Parameters for {@link createSponsoredKernelClient}. */\nexport interface CreateSponsoredKernelClientParams {\n\t/** A Kernel v0.7 account from `createAgentSmartAccount` or `restoreSessionAccount`. */\n\treadonly account: CreateKernelAccountReturnType<\"0.7\">;\n\t/** The viem chain the client operates on (e.g. `polygonAmoy`). */\n\treadonly chain: Chain;\n\t/**\n\t * The ZeroDev RPC URL — used for BOTH the bundler and the paymaster (ZeroDev\n\t * serves both from one project RPC). Build it from a project id with\n\t * `zerodevRpcUrl(chain, projectId)`, or paste the dashboard URL.\n\t */\n\treadonly zerodevRpc: string;\n\t/** Optional viem `PublicClient` for on-chain reads during userOp prep (recommended). */\n\treadonly publicClient?: PublicClient<Transport, Chain>;\n\t/** Optional sponsorship observability — granted / declined. */\n\treadonly observability?: SponsoredKernelClientObservability;\n}\n\n/**\n * Build a gas-sponsored Kernel account client. The returned\n * {@link ConfiguredKernelClient} pays UserOp gas via the ZeroDev paymaster and is\n * accepted directly by {@link transferJpyc} — no caller-side cast.\n *\n * @example\n * ```ts\n * import {\n * createSponsoredKernelClient,\n * polygonAmoy,\n * restoreSessionAccount,\n * transferJpyc,\n * zerodevRpcUrl,\n * } from \"kawasekit\";\n *\n * const account = await restoreSessionAccount({ publicClient, envelope, sessionKeySigner });\n * const client = createSponsoredKernelClient({\n * account,\n * chain: polygonAmoy,\n * zerodevRpc: zerodevRpcUrl(polygonAmoy, projectId),\n * publicClient,\n * });\n * const { transactionHash } = await transferJpyc(client, { to, amount });\n * ```\n */\nexport function createSponsoredKernelClient(\n\tparams: CreateSponsoredKernelClientParams,\n): ConfiguredKernelClient {\n\tconst paymasterClient = createZeroDevPaymasterClient({\n\t\tchain: params.chain,\n\t\ttransport: http(params.zerodevRpc),\n\t});\n\tconst client = createKernelAccountClient({\n\t\taccount: params.account,\n\t\tchain: params.chain,\n\t\t// exactOptionalPropertyTypes: only pass `client` when given (never `undefined`).\n\t\t...(params.publicClient !== undefined ? { client: params.publicClient } : {}),\n\t\tbundlerTransport: http(params.zerodevRpc),\n\t\tpaymaster: {\n\t\t\tgetPaymasterData: (userOperation) =>\n\t\t\t\tsponsorWithObservability(\n\t\t\t\t\t() => paymasterClient.sponsorUserOperation({ userOperation }),\n\t\t\t\t\tparams.account.address,\n\t\t\t\t\tparams.observability,\n\t\t\t\t),\n\t\t},\n\t});\n\t// createKernelAccountClient's deep generics don't unify with the exported\n\t// ConfiguredKernelClient alias; the runtime client is identical. One cast here\n\t// means callers never cast (closing gap G4).\n\treturn client as unknown as ConfiguredKernelClient;\n}\n","/**\n * High-level helper: transfer JPYC from a Kernel smart account via a sponsored\n * UserOp.\n *\n * This is the canonical \"agent payment\" path for M2. The flow:\n * 1. Resolve the JPYC contract address for `kernelClient.chain`.\n * 2. Encode `JPYC.transfer(to, amount)` calldata.\n * 3. Wrap it as a Kernel call and submit via `sendUserOperation`.\n * 4. Wait for the bundler receipt and return both hashes.\n *\n * EIP-3009 `transferWithAuthorization` cannot be used here: JPYC's signature\n * verification is pure `ecrecover`, so a smart account cannot be `from`. See\n * `src/tokens/eip3009.ts` for the EOA-payer path.\n *\n * @packageDocumentation\n */\n\nimport type { KernelAccountClient } from \"@zerodev/sdk\";\nimport type { Address, Chain, Hex, Transport } from \"viem\";\nimport { encodeFunctionData, isAddress } from \"viem\";\nimport type { SmartAccount } from \"viem/account-abstraction\";\nimport { isSupportedChainId, type SupportedChainId } from \"../chains\";\nimport { getJpycAddress, jpycAbi } from \"../tokens/jpyc\";\n\n/** A {@link KernelAccountClient} that is fully configured (chain + account). */\nexport type ConfiguredKernelClient = KernelAccountClient<Transport, Chain, SmartAccount>;\n\n/** Parameters for {@link transferJpyc}. */\nexport interface TransferJpycParams {\n\t/** Recipient address. */\n\treadonly to: Address;\n\t/**\n\t * Raw token amount in the token's smallest unit (JPYC has 18 decimals).\n\t *\n\t * @example\n\t * ```ts\n\t * import { parseUnits } from \"viem\";\n\t * import { JPYC_DECIMALS } from \"kawasekit\";\n\t *\n\t * parseUnits(\"100\", JPYC_DECIMALS); // 100 JPYC\n\t * ```\n\t */\n\treadonly amount: bigint;\n\t/**\n\t * Optional. Defaults to waiting for the bundler receipt before returning.\n\t * Set to `false` to return after the UserOp is submitted but before it\n\t * lands on chain — useful when the caller wants to do its own polling.\n\t */\n\treadonly waitForReceipt?: boolean;\n}\n\n/** Result of a {@link transferJpyc} call. */\nexport interface TransferJpycResult {\n\treadonly userOpHash: Hex;\n\t/** `null` when `waitForReceipt: false` was requested. */\n\treadonly transactionHash: Hex | null;\n\t/** `true` if the bundler receipt reported success; `null` if not awaited. */\n\treadonly success: boolean | null;\n}\n\n/** Thrown when {@link transferJpyc} is called with invalid arguments. */\nexport class TransferJpycInputError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(`transferJpyc: ${message}`);\n\t\tthis.name = \"TransferJpycInputError\";\n\t}\n}\n\n/**\n * Transfer JPYC from the Kernel smart account to `to` via a sponsored UserOp.\n *\n * @example\n * ```ts\n * import { parseUnits } from \"viem\";\n * import { JPYC_DECIMALS, transferJpyc } from \"kawasekit\";\n *\n * const { userOpHash, transactionHash } = await transferJpyc(kernelClient, {\n * to: \"0xBeef0000000000000000000000000000DEADBEEF\",\n * amount: parseUnits(\"100\", JPYC_DECIMALS),\n * });\n * ```\n */\nexport async function transferJpyc(\n\tkernelClient: ConfiguredKernelClient,\n\tparams: TransferJpycParams,\n): Promise<TransferJpycResult> {\n\tif (!isAddress(params.to, { strict: false })) {\n\t\tthrow new TransferJpycInputError(`\\`to\\` is not a valid address: ${params.to}`);\n\t}\n\tif (params.amount <= 0n) {\n\t\tthrow new TransferJpycInputError(`\\`amount\\` must be positive, got ${params.amount}.`);\n\t}\n\n\tconst chainId = kernelClient.chain.id;\n\tif (!isSupportedChainId(chainId)) {\n\t\tthrow new TransferJpycInputError(`Chain ID ${chainId} is not a kawasekit-supported chain.`);\n\t}\n\tconst jpycAddress = getJpycAddress(chainId satisfies SupportedChainId);\n\n\tconst data = encodeFunctionData({\n\t\tabi: jpycAbi,\n\t\tfunctionName: \"transfer\",\n\t\targs: [params.to, params.amount],\n\t});\n\n\tconst callData = await kernelClient.account.encodeCalls([{ to: jpycAddress, value: 0n, data }]);\n\n\tconst userOpHash = await kernelClient.sendUserOperation({ callData });\n\n\tif (params.waitForReceipt === false) {\n\t\treturn { userOpHash, transactionHash: null, success: null };\n\t}\n\n\tconst receipt = await kernelClient.waitForUserOperationReceipt({ hash: userOpHash });\n\treturn {\n\t\tuserOpHash,\n\t\ttransactionHash: receipt.receipt.transactionHash,\n\t\tsuccess: receipt.success,\n\t};\n}\n","/**\n * Buy-list → ZeroDev policy bundle for a **disposable, scoped session key**\n * (the Agent Commerce Hub authorization flow).\n *\n * A user's resolved buy-list (its merchants + a per-transfer cap + a total\n * transfer count + a schedule window) is baked into a single-use session key by\n * composing three on-chain policies:\n * 1. **callPolicy** — `JPYC.transfer(to, value)` with `value ≤ maxPerTransfer`\n * and `to ∈ merchants` (the allowlist; shared with\n * {@link createJpycDailyLimitPolicies} via {@link buildJpycTransferCallPolicy}).\n * 2. **rateLimitPolicy** — a **total** cap of `maxTransfers` over the whole\n * schedule window (the rate window is set to span `[validAfter, validUntil]`,\n * so `count` does NOT reset within the session — it is a session total, not a\n * per-day limit).\n * 3. **timestampPolicy** — the session key is only valid within\n * `[validAfter, validUntil]`.\n *\n * Cumulative budget (\"spend ≤ ¥X total\") is NOT a policy field — it is the\n * amount the user funds the account with (funding is the user's responsibility,\n * out of the SDK's scope). These policies bound *who* (allowlist), *how much per\n * transfer* (cap), *how many* (count), and *when* (window).\n *\n * @packageDocumentation\n */\n\nimport type { Policy } from \"@zerodev/permissions\";\nimport {\n\ttype CallPolicyVersion,\n\ttoRateLimitPolicy,\n\ttoTimestampPolicy,\n} from \"@zerodev/permissions/policies\";\nimport type { Address } from \"viem\";\nimport { buildJpycTransferCallPolicy } from \"./jpyc-call-policy\";\n\n/** Parameters for {@link createBuyListPolicies}. */\nexport interface CreateBuyListPoliciesParams {\n\t/** JPYC contract address on the target chain. */\n\treadonly jpycAddress: Address;\n\t/**\n\t * The buy-list's resolved merchant recipient addresses — the allowlist the\n\t * session key may pay. **Required and non-empty** (a buy-list always targets\n\t * specific merchants); checksum-normalized + de-duplicated.\n\t */\n\treadonly merchants: readonly Address[];\n\t/** Maximum JPYC (raw units) per single transfer. Must be positive. */\n\treadonly maxPerTransfer: bigint;\n\t/**\n\t * Maximum number of transfers over the WHOLE window — a session total, not a\n\t * per-day limit. Must be a positive integer.\n\t */\n\treadonly maxTransfers: number;\n\t/** Schedule-window end (unix seconds); the key is invalid after this. Must be a positive integer. */\n\treadonly validUntil: number;\n\t/**\n\t * Optional schedule-window start (unix seconds); the key is invalid before\n\t * this. Defaults to 0 (valid immediately). Must be `< validUntil`.\n\t */\n\treadonly validAfter?: number;\n\t/** ZeroDev callPolicy on-chain version. Defaults to V0_0_4. */\n\treadonly callPolicyVersion?: CallPolicyVersion;\n}\n\n/**\n * Build the ZeroDev policy bundle for a buy-list-scoped, single-use session key.\n *\n * Plug the returned policies into `toPermissionValidator({ policies, … })` and\n * issue the session key via {@link issueSessionKey}.\n *\n * @example\n * ```ts\n * import { parseUnits } from \"viem\";\n * import { createBuyListPolicies, getJpycAddress, JPYC_DECIMALS, polygonAmoy } from \"kawasekit\";\n *\n * const policies = createBuyListPolicies({\n * jpycAddress: getJpycAddress(polygonAmoy.id),\n * merchants: [merchantA, merchantB], // pay ONLY these (allowlist)\n * maxPerTransfer: parseUnits(\"500\", JPYC_DECIMALS),\n * maxTransfers: 3, // at most 3 transfers, total\n * validUntil: Math.floor(Date.now() / 1000) + 3 * 86_400, // valid 3 days\n * });\n * // user funds the account with their budget; the policies bound who/how-much/how-many/when.\n * ```\n */\nexport function createBuyListPolicies(\n\tparams: CreateBuyListPoliciesParams,\n): readonly [Policy, Policy, Policy] {\n\tif (params.merchants.length === 0) {\n\t\tthrow new Error(\n\t\t\t\"createBuyListPolicies: merchants must not be empty — a buy-list must target at least one merchant.\",\n\t\t);\n\t}\n\tif (!Number.isInteger(params.maxTransfers) || params.maxTransfers < 1) {\n\t\tthrow new Error(\n\t\t\t`createBuyListPolicies: maxTransfers must be a positive integer, got ${params.maxTransfers}.`,\n\t\t);\n\t}\n\tif (!Number.isInteger(params.validUntil) || params.validUntil <= 0) {\n\t\tthrow new Error(\n\t\t\t`createBuyListPolicies: validUntil must be a positive unix-seconds integer, got ${params.validUntil}.`,\n\t\t);\n\t}\n\tconst validAfter = params.validAfter ?? 0;\n\tif (!Number.isInteger(validAfter) || validAfter < 0) {\n\t\tthrow new Error(\n\t\t\t`createBuyListPolicies: validAfter must be a non-negative unix-seconds integer, got ${params.validAfter}.`,\n\t\t);\n\t}\n\tif (validAfter >= params.validUntil) {\n\t\tthrow new Error(\n\t\t\t`createBuyListPolicies: validAfter (${validAfter}) must be before validUntil (${params.validUntil}).`,\n\t\t);\n\t}\n\n\t// callPolicy: amount cap + merchant allowlist (shared with daily-limit).\n\tconst callPolicy = buildJpycTransferCallPolicy({\n\t\tjpycAddress: params.jpycAddress,\n\t\tmaxPerTransfer: params.maxPerTransfer,\n\t\trecipientAllowlist: params.merchants,\n\t\tcallPolicyVersion: params.callPolicyVersion,\n\t});\n\n\t// rateLimitPolicy: a TOTAL cap of maxTransfers over the window. Setting the\n\t// rate window to span exactly [validAfter, validUntil] (interval = the window\n\t// length, startAt = validAfter) keeps the whole session in one rate bucket, so\n\t// `count` is a session total — NOT a per-day limit that could reset and let\n\t// more than maxTransfers through over a multi-day window.\n\tconst rateLimitPolicy = toRateLimitPolicy({\n\t\tinterval: params.validUntil - validAfter,\n\t\tcount: params.maxTransfers,\n\t\tstartAt: validAfter,\n\t});\n\n\t// timestampPolicy: the key is only valid within the schedule window. Omit\n\t// validAfter when not given (exactOptionalPropertyTypes: don't pass undefined).\n\tconst timestampPolicy = toTimestampPolicy(\n\t\tparams.validAfter === undefined\n\t\t\t? { validUntil: params.validUntil }\n\t\t\t: { validAfter: params.validAfter, validUntil: params.validUntil },\n\t);\n\n\treturn [callPolicy, rateLimitPolicy, timestampPolicy] as const;\n}\n"]}
@@ -2,30 +2,22 @@ import { normalizeRecipientAllowlist } from './chunk-7YKRKV6D.js';
2
2
  import { jpycAbi } from './chunk-KT7XDT2T.js';
3
3
  import { CallPolicyVersion, toCallPolicy, ParamCondition, toRateLimitPolicy } from '@zerodev/permissions/policies';
4
4
 
5
- var ONE_DAY_SECONDS = 86400;
6
- function createJpycDailyLimitPolicies(params) {
5
+ function buildJpycTransferCallPolicy(params) {
7
6
  if (params.maxPerTransfer <= 0n) {
8
- throw new Error(
9
- `createJpycDailyLimitPolicies: maxPerTransfer must be positive, got ${params.maxPerTransfer}.`
10
- );
11
- }
12
- if (!Number.isInteger(params.maxTransfersPerDay) || params.maxTransfersPerDay < 1) {
13
- throw new Error(
14
- `createJpycDailyLimitPolicies: maxTransfersPerDay must be a positive integer, got ${params.maxTransfersPerDay}.`
15
- );
7
+ throw new Error(`maxPerTransfer must be positive, got ${params.maxPerTransfer}.`);
16
8
  }
17
9
  const recipients = params.recipientAllowlist === void 0 || params.recipientAllowlist === "any" ? null : normalizeRecipientAllowlist(params.recipientAllowlist);
18
10
  if (recipients !== null && recipients.length === 0) {
19
11
  throw new Error(
20
- 'createJpycDailyLimitPolicies: recipientAllowlist must not be empty \u2014 omit it or pass "any" to allow any recipient.'
12
+ 'recipientAllowlist must not be empty \u2014 omit it or pass "any" to allow any recipient.'
21
13
  );
22
14
  }
23
15
  if (recipients !== null && params.callPolicyVersion === CallPolicyVersion.V0_0_1) {
24
16
  throw new Error(
25
- "createJpycDailyLimitPolicies: recipientAllowlist requires callPolicyVersion V0_0_2 or later (the ONE_OF condition is unsupported on V0_0_1)."
17
+ "recipientAllowlist requires callPolicyVersion V0_0_2 or later (the ONE_OF condition is unsupported on V0_0_1)."
26
18
  );
27
19
  }
28
- const callPolicy = toCallPolicy({
20
+ return toCallPolicy({
29
21
  policyVersion: params.callPolicyVersion ?? CallPolicyVersion.V0_0_4,
30
22
  permissions: [
31
23
  {
@@ -45,6 +37,22 @@ function createJpycDailyLimitPolicies(params) {
45
37
  }
46
38
  ]
47
39
  });
40
+ }
41
+
42
+ // src/policy/daily-limit.ts
43
+ var ONE_DAY_SECONDS = 86400;
44
+ function createJpycDailyLimitPolicies(params) {
45
+ if (!Number.isInteger(params.maxTransfersPerDay) || params.maxTransfersPerDay < 1) {
46
+ throw new Error(
47
+ `createJpycDailyLimitPolicies: maxTransfersPerDay must be a positive integer, got ${params.maxTransfersPerDay}.`
48
+ );
49
+ }
50
+ const callPolicy = buildJpycTransferCallPolicy({
51
+ jpycAddress: params.jpycAddress,
52
+ maxPerTransfer: params.maxPerTransfer,
53
+ recipientAllowlist: params.recipientAllowlist,
54
+ callPolicyVersion: params.callPolicyVersion
55
+ });
48
56
  const rateLimitPolicy = toRateLimitPolicy({
49
57
  interval: ONE_DAY_SECONDS,
50
58
  count: params.maxTransfersPerDay
@@ -52,6 +60,6 @@ function createJpycDailyLimitPolicies(params) {
52
60
  return [callPolicy, rateLimitPolicy];
53
61
  }
54
62
 
55
- export { ONE_DAY_SECONDS, createJpycDailyLimitPolicies };
56
- //# sourceMappingURL=chunk-X54U6GU4.js.map
57
- //# sourceMappingURL=chunk-X54U6GU4.js.map
63
+ export { ONE_DAY_SECONDS, buildJpycTransferCallPolicy, createJpycDailyLimitPolicies };
64
+ //# sourceMappingURL=chunk-UIDDRHLW.js.map
65
+ //# sourceMappingURL=chunk-UIDDRHLW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/policy/jpyc-call-policy.ts","../src/policy/daily-limit.ts"],"names":[],"mappings":";;;;AA0CO,SAAS,4BAA4B,MAAA,EAA8C;AACzF,EAAA,IAAI,MAAA,CAAO,kBAAkB,EAAA,EAAI;AAChC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qCAAA,EAAwC,MAAA,CAAO,cAAc,CAAA,CAAA,CAAG,CAAA;AAAA,EACjF;AAKA,EAAA,MAAM,UAAA,GACL,MAAA,CAAO,kBAAA,KAAuB,MAAA,IAAa,MAAA,CAAO,uBAAuB,KAAA,GACtE,IAAA,GACA,2BAAA,CAA4B,MAAA,CAAO,kBAAkB,CAAA;AACzD,EAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG;AACnD,IAAA,MAAM,IAAI,KAAA;AAAA,MACT;AAAA,KACD;AAAA,EACD;AAIA,EAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,MAAA,CAAO,iBAAA,KAAsB,kBAAkB,MAAA,EAAQ;AACjF,IAAA,MAAM,IAAI,KAAA;AAAA,MACT;AAAA,KACD;AAAA,EACD;AAEA,EAAA,OAAO,YAAA,CAAa;AAAA,IACnB,aAAA,EAAe,MAAA,CAAO,iBAAA,IAAqB,iBAAA,CAAkB,MAAA;AAAA,IAC7D,WAAA,EAAa;AAAA,MACZ;AAAA,QACC,QAAQ,MAAA,CAAO,WAAA;AAAA,QACf,GAAA,EAAK,OAAA;AAAA,QACL,YAAA,EAAc,UAAA;AAAA,QACd,IAAA,EAAM;AAAA;AAAA;AAAA,UAGL,UAAA,KAAe,OAAO,IAAA,GAAO,EAAE,WAAW,cAAA,CAAe,MAAA,EAAQ,OAAO,UAAA,EAAW;AAAA;AAAA,UAEnF;AAAA,YACC,WAAW,cAAA,CAAe,kBAAA;AAAA,YAC1B,OAAO,MAAA,CAAO;AAAA;AACf;AACD;AACD;AACD,GACA,CAAA;AACF;;;AC7DO,IAAM,eAAA,GAAkB;AA2ExB,SAAS,6BACf,MAAA,EAC4B;AAC5B,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAA,CAAO,kBAAkB,CAAA,IAAK,MAAA,CAAO,qBAAqB,CAAA,EAAG;AAClF,IAAA,MAAM,IAAI,KAAA;AAAA,MACT,CAAA,iFAAA,EAAoF,OAAO,kBAAkB,CAAA,CAAA;AAAA,KAC9G;AAAA,EACD;AAIA,EAAA,MAAM,aAAa,2BAAA,CAA4B;AAAA,IAC9C,aAAa,MAAA,CAAO,WAAA;AAAA,IACpB,gBAAgB,MAAA,CAAO,cAAA;AAAA,IACvB,oBAAoB,MAAA,CAAO,kBAAA;AAAA,IAC3B,mBAAmB,MAAA,CAAO;AAAA,GAC1B,CAAA;AAED,EAAA,MAAM,kBAAkB,iBAAA,CAAkB;AAAA,IACzC,QAAA,EAAU,eAAA;AAAA,IACV,OAAO,MAAA,CAAO;AAAA,GACd,CAAA;AAED,EAAA,OAAO,CAAC,YAAY,eAAe,CAAA;AACpC","file":"chunk-UIDDRHLW.js","sourcesContent":["/**\n * Shared builder for the JPYC `transfer` callPolicy — the on-chain rule that\n * locks a ZeroDev session key to `JPYC.transfer(to, value)` with\n * `value ≤ maxPerTransfer` and an optional recipient allowlist (condition\n * `ONE_OF`). Used by BOTH {@link createJpycDailyLimitPolicies} (`./daily-limit`)\n * and {@link createBuyListPolicies} (`./buy-list`) so the recipient/amount\n * constraint is built identically; each caller composes its own rate-limit /\n * timestamp policy on top.\n *\n * @packageDocumentation\n */\n\nimport type { Policy } from \"@zerodev/permissions\";\nimport { CallPolicyVersion, ParamCondition, toCallPolicy } from \"@zerodev/permissions/policies\";\nimport type { Address } from \"viem\";\nimport { jpycAbi } from \"../tokens/jpyc\";\nimport { normalizeRecipientAllowlist } from \"./normalize-allowlist\";\n\n/** Parameters for {@link buildJpycTransferCallPolicy}. */\nexport interface JpycTransferCallPolicyParams {\n\t/** JPYC contract address on the target chain. */\n\treadonly jpycAddress: Address;\n\t/** Maximum JPYC (raw units) per single `transfer`. Must be positive. */\n\treadonly maxPerTransfer: bigint;\n\t/**\n\t * Recipient restriction. An address list constrains `to` to those recipients\n\t * (`ONE_OF`); `\"any\"` (or `undefined`) leaves it unrestricted. Entries are\n\t * checksum-normalized + de-duplicated ({@link normalizeRecipientAllowlist}).\n\t * An **empty** array throws (an on-chain allowlist cannot encode \"match\n\t * nothing\"). An address list requires `callPolicyVersion` ≥ `V0_0_2`.\n\t */\n\treadonly recipientAllowlist?: readonly Address[] | \"any\" | undefined;\n\t/** ZeroDev callPolicy on-chain version. Defaults to V0_0_4. */\n\treadonly callPolicyVersion?: CallPolicyVersion | undefined;\n}\n\n/**\n * Build the single ZeroDev callPolicy for a JPYC `transfer`, enforcing the\n * per-transfer amount cap and (optionally) the recipient allowlist. Throws on a\n * non-positive `maxPerTransfer`, an empty allowlist, or an allowlist combined\n * with `callPolicyVersion` `V0_0_1` (which lacks `ONE_OF`).\n */\nexport function buildJpycTransferCallPolicy(params: JpycTransferCallPolicyParams): Policy {\n\tif (params.maxPerTransfer <= 0n) {\n\t\tthrow new Error(`maxPerTransfer must be positive, got ${params.maxPerTransfer}.`);\n\t}\n\t// Resolve the recipient constraint up-front. Omitted or \"any\" => unrestricted\n\t// (a `null` arg); an address list => checksum-normalized + de-duped (shared\n\t// with the off-chain SpendingPolicy). Unlike off-chain, an empty list is a\n\t// caller bug here — an on-chain allowlist cannot encode \"deny all\".\n\tconst recipients =\n\t\tparams.recipientAllowlist === undefined || params.recipientAllowlist === \"any\"\n\t\t\t? null\n\t\t\t: normalizeRecipientAllowlist(params.recipientAllowlist);\n\tif (recipients !== null && recipients.length === 0) {\n\t\tthrow new Error(\n\t\t\t'recipientAllowlist must not be empty — omit it or pass \"any\" to allow any recipient.',\n\t\t);\n\t}\n\t// The ONE_OF condition (used to encode an address list) is only supported by\n\t// the V0_0_2+ CallPolicy contract; catch the incompatible combination here\n\t// with a typed message rather than a bare error from deep inside toCallPolicy.\n\tif (recipients !== null && params.callPolicyVersion === CallPolicyVersion.V0_0_1) {\n\t\tthrow new Error(\n\t\t\t\"recipientAllowlist requires callPolicyVersion V0_0_2 or later (the ONE_OF condition is unsupported on V0_0_1).\",\n\t\t);\n\t}\n\n\treturn toCallPolicy({\n\t\tpolicyVersion: params.callPolicyVersion ?? CallPolicyVersion.V0_0_4,\n\t\tpermissions: [\n\t\t\t{\n\t\t\t\ttarget: params.jpycAddress,\n\t\t\t\tabi: jpycAbi,\n\t\t\t\tfunctionName: \"transfer\",\n\t\t\t\targs: [\n\t\t\t\t\t// Recipient (`to`): unrestricted (`null`) unless an allowlist resolved\n\t\t\t\t\t// above, in which case it is constrained via the ONE_OF condition.\n\t\t\t\t\trecipients === null ? null : { condition: ParamCondition.ONE_OF, value: recipients },\n\t\t\t\t\t// value: must be ≤ maxPerTransfer.\n\t\t\t\t\t{\n\t\t\t\t\t\tcondition: ParamCondition.LESS_THAN_OR_EQUAL,\n\t\t\t\t\t\tvalue: params.maxPerTransfer,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n}\n","/**\n * Daily-limit spending policy for JPYC, built on ZeroDev's Permission System.\n *\n * Composes two ZeroDev policies:\n * 1. **callPolicy** — locks the session key to `JPYC.transfer(to, value)`\n * with `value ≤ maxPerTransfer`, and — when {@link\n * CreateJpycDailyLimitPoliciesParams.recipientAllowlist} is provided —\n * restricts `to` to the allowlisted recipients (otherwise any recipient is\n * allowed). This is the on-chain enforcement behind the \"pay only registered\n * merchants\" model: a buy-list resolves to its merchants' addresses, which\n * are baked into a disposable session key here.\n * 2. **rateLimitPolicy** — caps userOp count to `maxTransfersPerDay` in any\n * 24-hour rolling window.\n *\n * Effective daily cap = `maxPerTransfer × maxTransfersPerDay`. This is not a\n * cumulative-amount tracker (ZeroDev doesn't ship one), but the agent cannot\n * exceed either dimension, so the spirit of \"daily limit\" holds.\n *\n * @packageDocumentation\n */\n\nimport type { Policy } from \"@zerodev/permissions\";\nimport { type CallPolicyVersion, toRateLimitPolicy } from \"@zerodev/permissions/policies\";\nimport type { Address } from \"viem\";\nimport { buildJpycTransferCallPolicy } from \"./jpyc-call-policy\";\n\n/** One day in seconds — the period for {@link createJpycDailyLimitPolicies}. */\nexport const ONE_DAY_SECONDS = 86_400;\n\n/** Parameters for {@link createJpycDailyLimitPolicies}. */\nexport interface CreateJpycDailyLimitPoliciesParams {\n\t/** JPYC contract address on the target chain. */\n\treadonly jpycAddress: Address;\n\t/** Maximum JPYC (in raw units) the session key may move in one transfer. */\n\treadonly maxPerTransfer: bigint;\n\t/** Maximum number of transfer userOps the session key may submit per day. */\n\treadonly maxTransfersPerDay: number;\n\t/**\n\t * ZeroDev callPolicy on-chain version. Defaults to V0_0_4 (latest at the\n\t * time of writing). Bump only after auditing the new version's semantics.\n\t */\n\treadonly callPolicyVersion?: CallPolicyVersion;\n\t/**\n\t * Recipient restriction. An address list restricts the session key to\n\t * `transfer` JPYC only to those recipients (enforced on-chain via the\n\t * callPolicy `to` argument, condition `ONE_OF`); every other recipient is\n\t * rejected before any funds move. `\"any\"` (or omitting the field) leaves the\n\t * recipient unrestricted.\n\t *\n\t * The type mirrors the off-chain {@link SpendingPolicy.recipientAllowlist}\n\t * (`Address[] | \"any\"`) so a buy-list's resolved allowlist can feed both the\n\t * off-chain and on-chain policy paths unchanged. Two **deliberate** differences\n\t * from that sibling, forced by on-chain semantics:\n\t * - it is **optional** here (omitted = `\"any\"`) for backward compatibility —\n\t * the pre-allowlist policy left the recipient unrestricted;\n\t * - an **empty** array `[]` **throws** (rather than meaning deny-all as it does\n\t * off-chain) — an on-chain allowlist cannot encode \"match nothing\"; omit it\n\t * or pass `\"any\"` for unrestricted, or list ≥1 recipient.\n\t *\n\t * Entries are checksum-normalized and de-duplicated (shared with the off-chain\n\t * path via {@link normalizeRecipientAllowlist}). An address list requires\n\t * `callPolicyVersion` ≥ `V0_0_2` (the `ONE_OF` condition is unsupported on\n\t * `V0_0_1`); the default `V0_0_4` is fine.\n\t */\n\treadonly recipientAllowlist?: readonly Address[] | \"any\";\n}\n\n/**\n * Builds the ZeroDev policy bundle that enforces a JPYC daily spend limit.\n *\n * Plug the returned policies into `toPermissionValidator({ policies, … })`\n * — see {@link createAgentSmartAccount} for the common wiring.\n *\n * @example\n * ```ts\n * import { parseUnits } from \"viem\";\n * import {\n * createJpycDailyLimitPolicies,\n * getJpycAddress,\n * JPYC_DECIMALS,\n * polygonAmoy,\n * } from \"kawasekit\";\n *\n * const policies = createJpycDailyLimitPolicies({\n * jpycAddress: getJpycAddress(polygonAmoy.id),\n * maxPerTransfer: parseUnits(\"100\", JPYC_DECIMALS), // 100 JPYC / tx\n * maxTransfersPerDay: 10, // 10 tx / day\n * // effective daily cap = 1000 JPYC\n * });\n * ```\n *\n * @example Restrict the session key to a buy-list's merchants only\n * ```ts\n * const policies = createJpycDailyLimitPolicies({\n * jpycAddress: getJpycAddress(polygonAmoy.id),\n * maxPerTransfer: parseUnits(\"100\", JPYC_DECIMALS),\n * maxTransfersPerDay: 5,\n * // on-chain: any transfer to a non-allowlisted address reverts before funds move\n * recipientAllowlist: [merchantA, merchantB],\n * });\n * ```\n */\nexport function createJpycDailyLimitPolicies(\n\tparams: CreateJpycDailyLimitPoliciesParams,\n): readonly [Policy, Policy] {\n\tif (!Number.isInteger(params.maxTransfersPerDay) || params.maxTransfersPerDay < 1) {\n\t\tthrow new Error(\n\t\t\t`createJpycDailyLimitPolicies: maxTransfersPerDay must be a positive integer, got ${params.maxTransfersPerDay}.`,\n\t\t);\n\t}\n\n\t// callPolicy (amount cap + optional recipient allowlist) is the shared builder;\n\t// this function adds the daily-window rate limit on top.\n\tconst callPolicy = buildJpycTransferCallPolicy({\n\t\tjpycAddress: params.jpycAddress,\n\t\tmaxPerTransfer: params.maxPerTransfer,\n\t\trecipientAllowlist: params.recipientAllowlist,\n\t\tcallPolicyVersion: params.callPolicyVersion,\n\t});\n\n\tconst rateLimitPolicy = toRateLimitPolicy({\n\t\tinterval: ONE_DAY_SECONDS,\n\t\tcount: params.maxTransfersPerDay,\n\t});\n\n\treturn [callPolicy, rateLimitPolicy] as const;\n}\n"]}
@@ -711,31 +711,23 @@ function normalizeRecipientAllowlist(allowlist) {
711
711
  return normalized;
712
712
  }
713
713
 
714
- // src/policy/daily-limit.ts
715
- var ONE_DAY_SECONDS = 86400;
716
- function createJpycDailyLimitPolicies(params) {
714
+ // src/policy/jpyc-call-policy.ts
715
+ function buildJpycTransferCallPolicy(params) {
717
716
  if (params.maxPerTransfer <= 0n) {
718
- throw new Error(
719
- `createJpycDailyLimitPolicies: maxPerTransfer must be positive, got ${params.maxPerTransfer}.`
720
- );
721
- }
722
- if (!Number.isInteger(params.maxTransfersPerDay) || params.maxTransfersPerDay < 1) {
723
- throw new Error(
724
- `createJpycDailyLimitPolicies: maxTransfersPerDay must be a positive integer, got ${params.maxTransfersPerDay}.`
725
- );
717
+ throw new Error(`maxPerTransfer must be positive, got ${params.maxPerTransfer}.`);
726
718
  }
727
719
  const recipients = params.recipientAllowlist === void 0 || params.recipientAllowlist === "any" ? null : normalizeRecipientAllowlist(params.recipientAllowlist);
728
720
  if (recipients !== null && recipients.length === 0) {
729
721
  throw new Error(
730
- 'createJpycDailyLimitPolicies: recipientAllowlist must not be empty \u2014 omit it or pass "any" to allow any recipient.'
722
+ 'recipientAllowlist must not be empty \u2014 omit it or pass "any" to allow any recipient.'
731
723
  );
732
724
  }
733
725
  if (recipients !== null && params.callPolicyVersion === policies.CallPolicyVersion.V0_0_1) {
734
726
  throw new Error(
735
- "createJpycDailyLimitPolicies: recipientAllowlist requires callPolicyVersion V0_0_2 or later (the ONE_OF condition is unsupported on V0_0_1)."
727
+ "recipientAllowlist requires callPolicyVersion V0_0_2 or later (the ONE_OF condition is unsupported on V0_0_1)."
736
728
  );
737
729
  }
738
- const callPolicy = policies.toCallPolicy({
730
+ return policies.toCallPolicy({
739
731
  policyVersion: params.callPolicyVersion ?? policies.CallPolicyVersion.V0_0_4,
740
732
  permissions: [
741
733
  {
@@ -755,6 +747,20 @@ function createJpycDailyLimitPolicies(params) {
755
747
  }
756
748
  ]
757
749
  });
750
+ }
751
+ var ONE_DAY_SECONDS = 86400;
752
+ function createJpycDailyLimitPolicies(params) {
753
+ if (!Number.isInteger(params.maxTransfersPerDay) || params.maxTransfersPerDay < 1) {
754
+ throw new Error(
755
+ `createJpycDailyLimitPolicies: maxTransfersPerDay must be a positive integer, got ${params.maxTransfersPerDay}.`
756
+ );
757
+ }
758
+ const callPolicy = buildJpycTransferCallPolicy({
759
+ jpycAddress: params.jpycAddress,
760
+ maxPerTransfer: params.maxPerTransfer,
761
+ recipientAllowlist: params.recipientAllowlist,
762
+ callPolicyVersion: params.callPolicyVersion
763
+ });
758
764
  const rateLimitPolicy = policies.toRateLimitPolicy({
759
765
  interval: ONE_DAY_SECONDS,
760
766
  count: params.maxTransfersPerDay
@@ -1669,7 +1675,7 @@ function registerTransferCommand(program2) {
1669
1675
  }
1670
1676
 
1671
1677
  // cli/index.ts
1672
- var CLI_VERSION = "0.6.0" ;
1678
+ var CLI_VERSION = "0.8.0" ;
1673
1679
  var program = new commander.Command();
1674
1680
  program.name("kawasekit").description(
1675
1681
  "kawasekit \u2014 CLI for the kawasekit SDK (AI-agent stablecoin payments, Japan-first, JPYC-native)."