kawasekit 0.1.0-alpha.1 → 0.1.0-beta.3
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/README.md +86 -20
- package/dist/{chunk-2QYCWRYR.js → chunk-2V3W4B64.js} +123 -30
- package/dist/chunk-2V3W4B64.js.map +1 -0
- package/dist/{chunk-7DWIT6D4.js → chunk-6G345P2I.js} +3 -3
- package/dist/{chunk-7DWIT6D4.js.map → chunk-6G345P2I.js.map} +1 -1
- package/dist/{chunk-LNXYCHRY.js → chunk-CD6SQBZN.js} +222 -29
- package/dist/chunk-CD6SQBZN.js.map +1 -0
- package/dist/{chunk-V5PUKFPL.js → chunk-KWCPYGFE.js} +3 -3
- package/dist/{chunk-V5PUKFPL.js.map → chunk-KWCPYGFE.js.map} +1 -1
- package/dist/chunk-WMFCI6KC.js +333 -0
- package/dist/chunk-WMFCI6KC.js.map +1 -0
- package/dist/cli/index.cjs +4 -4
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +8 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/idempotency/index.cjs +353 -0
- package/dist/idempotency/index.cjs.map +1 -0
- package/dist/idempotency/index.d.cts +203 -0
- package/dist/idempotency/index.d.ts +203 -0
- package/dist/idempotency/index.js +5 -0
- package/dist/idempotency/index.js.map +1 -0
- package/dist/{index-BT-LhXV1.d.ts → index-CykLOgYD.d.ts} +13 -5
- package/dist/{index-CzX09uRS.d.cts → index-DzveM0RN.d.cts} +13 -5
- package/dist/index-NdNKNnZP.d.cts +808 -0
- package/dist/index-kqH78Yms.d.ts +808 -0
- package/dist/index.cjs +667 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -5
- package/dist/index.d.ts +41 -5
- package/dist/index.js +5 -4
- package/dist/observability/index.d.cts +203 -2
- package/dist/observability/index.d.ts +203 -2
- package/dist/observability/otlp/index.d.cts +2 -1
- package/dist/observability/otlp/index.d.ts +2 -1
- package/dist/observability/prometheus/index.d.cts +2 -1
- package/dist/observability/prometheus/index.d.ts +2 -1
- package/dist/{server-t1qVFVnJ.d.ts → server-BMeg_hNB.d.cts} +30 -2
- package/dist/{server-C3XkkTIP.d.cts → server-CSpATNiH.d.ts} +30 -2
- package/dist/session/index.cjs +1 -1
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +1 -1
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js +1 -1
- package/dist/store-BY16tCbe.d.cts +166 -0
- package/dist/store-Bd-91QL0.d.ts +166 -0
- package/dist/{index-Bf78wMqn.d.cts → types-A_WwFpcv.d.cts} +1 -200
- package/dist/{index-WQ_Hq4_Z.d.ts → types-DwFfT4E7.d.ts} +1 -200
- package/dist/x402/hono/index.cjs +335 -4
- package/dist/x402/hono/index.cjs.map +1 -1
- package/dist/x402/hono/index.d.cts +4 -2
- package/dist/x402/hono/index.d.ts +4 -2
- package/dist/x402/hono/index.js +2 -1
- package/dist/x402/hono/index.js.map +1 -1
- package/dist/x402/index.cjs +524 -84
- package/dist/x402/index.cjs.map +1 -1
- package/dist/x402/index.d.cts +7 -632
- package/dist/x402/index.d.ts +7 -632
- package/dist/x402/index.js +3 -2
- package/package.json +12 -1
- package/dist/chunk-2QYCWRYR.js.map +0 -1
- package/dist/chunk-LNXYCHRY.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/kawasekit)
|
|
6
6
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
7
|
|
|
8
|
-
🚧 **Status**:
|
|
8
|
+
🚧 **Status**: M4 complete — `kawasekit@0.1.0-beta.2` is published on npm (SLSA provenance, `beta` dist-tag) and mainnet-capable, with payment flows verified on Polygon mainnet. **Not yet GA.** Production use is currently constrained to **small per-call values**: the reasoning-step idempotency gap (see [`docs/THREAT_MODEL.md` §6.1](./docs/THREAT_MODEL.md#61-reasoning-step-idempotency-gap)) is not yet closed, so duplicate-payment scenarios are the integrator's responsibility. GA (`0.1.0` on the `latest` tag) is gated on closing the fund-correctness gaps — the §6.1 idempotency layer (or GA explicitly scoped to small per-call values) plus a `maxAmountPerSign` ceiling — and a clean beta soak. Review happens **continuously in the open**: issues and counter-examples are welcome on GitHub and via [SECURITY.md](./SECURITY.md) (the §6.1 gap itself came from public feedback). A formal third-party audit is a goal on the road to `1.0`, not a `0.1.0` GA blocker. Built in public.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add kawasekit@beta # 0.1.0-beta.2 — pre-GA, mainnet-capable
|
|
12
|
+
```
|
|
9
13
|
|
|
10
14
|
## Vision
|
|
11
15
|
|
|
@@ -19,9 +23,9 @@ Built around modern account abstraction (ERC-4337 / Kernel v3.1) and Japan's fir
|
|
|
19
23
|
|
|
20
24
|
- [x] **M1**: Smart account skeleton on Polygon Amoy
|
|
21
25
|
- [x] **M2**: JPYC transfer via UserOp + EIP-3009 signing helpers + Daily Limit spending policy
|
|
22
|
-
- [x] **M3**: x402 v2 server/client/facilitator + session-key lifecycle + Mastra/Hono integration example
|
|
23
|
-
- [
|
|
24
|
-
- [ ] **M5**:
|
|
26
|
+
- [x] **M3**: x402 v2 server/client/facilitator + session-key lifecycle + Mastra/Hono integration example
|
|
27
|
+
- [x] **M4**: Polygon mainnet support + observability (Prometheus / OTLP) + CLI + docs site + npm `0.1.0-alpha`/`0.1.0-beta` release
|
|
28
|
+
- [ ] **M5**: Reasoning-step idempotency layer (§6.1) + `0.1.0` GA promote + Kaia support — the technical prerequisites for first real integrations
|
|
25
29
|
- [ ] **M6**: Managed service alpha + Rust policy engine
|
|
26
30
|
|
|
27
31
|
## Quick Start
|
|
@@ -64,6 +68,28 @@ pays 0.001 JPYC, and the summary prints Polygonscan tx URLs for every
|
|
|
64
68
|
settlement. See the [example README](./examples/agent-x402-jpyc/README.md)
|
|
65
69
|
for the full walkthrough.
|
|
66
70
|
|
|
71
|
+
### CLI (M4-4)
|
|
72
|
+
|
|
73
|
+
Installing the package exposes a `kawasekit` binary (`npx kawasekit <cmd>`, or
|
|
74
|
+
`pnpm exec kawasekit` in a workspace):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pnpm add kawasekit@beta
|
|
78
|
+
|
|
79
|
+
npx kawasekit init # scaffold .env + required vars
|
|
80
|
+
npx kawasekit account create --chain polygonAmoy # deploy a Kernel smart account
|
|
81
|
+
npx kawasekit policy create --chain polygonAmoy # build a Daily Limit policy
|
|
82
|
+
npx kawasekit transfer --chain polygonAmoy # send JPYC via a sponsored UserOp
|
|
83
|
+
npx kawasekit session-key issue --chain polygonAmoy # issue a session key + envelope
|
|
84
|
+
npx kawasekit session-key restore --chain polygonAmoy
|
|
85
|
+
npx kawasekit session-key revoke --chain polygonAmoy
|
|
86
|
+
npx kawasekit session-key rotate --chain polygonAmoy
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Every on-chain command takes `--chain "polygon" | "polygonAmoy"`. Mainnet
|
|
90
|
+
(`--chain polygon`) additionally requires `KAWASEKIT_ALLOW_MAINNET=1` in the
|
|
91
|
+
environment as a safety guard against accidental real-funds broadcasts.
|
|
92
|
+
|
|
67
93
|
### Programmatic use (M2)
|
|
68
94
|
|
|
69
95
|
```typescript
|
|
@@ -147,7 +173,9 @@ const app = new Hono();
|
|
|
147
173
|
app.use(
|
|
148
174
|
"/weather/*",
|
|
149
175
|
x402Middleware({
|
|
150
|
-
|
|
176
|
+
// `network` is required (M4-1): it is cross-checked against
|
|
177
|
+
// walletClient.chain.isTestnet and throws on mismatch.
|
|
178
|
+
facilitator: createSelfFacilitator({ network: "testnet", walletClient, publicClient }),
|
|
151
179
|
requirementsFor: () => [
|
|
152
180
|
buildPaymentRequirements({
|
|
153
181
|
chainId: polygonAmoy.id,
|
|
@@ -164,16 +192,45 @@ app.get("/weather/:city", (c) => c.json({ city: c.req.param("city"), weather: "s
|
|
|
164
192
|
Client (any `fetch` becomes x402-aware):
|
|
165
193
|
|
|
166
194
|
```typescript
|
|
167
|
-
import { createX402PaymentSigner, wrapFetch } from "kawasekit";
|
|
195
|
+
import { createX402PaymentSigner, JPYC_DECIMALS, wrapFetch } from "kawasekit";
|
|
196
|
+
import { parseUnits } from "viem";
|
|
168
197
|
import { privateKeyToAccount } from "viem/accounts";
|
|
169
198
|
|
|
170
|
-
const signer = createX402PaymentSigner({
|
|
171
|
-
|
|
199
|
+
const signer = createX402PaymentSigner({
|
|
200
|
+
network: "testnet",
|
|
201
|
+
account: privateKeyToAccount("0x..."),
|
|
202
|
+
// Pin to the JPYC v2 EIP-712 domain at construction. The wire-format
|
|
203
|
+
// extra.name / extra.version are ignored — Threat 1.4 mitigation.
|
|
204
|
+
asset: { kind: "known", id: "jpyc-v2" },
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// onPayment is *required* at the type level — kawasekit refuses to default
|
|
208
|
+
// to "always pay" silently. The callback is your budget guard.
|
|
209
|
+
let spent = 0n;
|
|
210
|
+
const MAX_SPEND = parseUnits("100", JPYC_DECIMALS); // 100 JPYC (JPYC has 18 decimals)
|
|
211
|
+
const fetch402 = wrapFetch({
|
|
212
|
+
signer,
|
|
213
|
+
onPayment: (req) => {
|
|
214
|
+
const next = spent + BigInt(req.amount);
|
|
215
|
+
if (next > MAX_SPEND) return false; // budget exhausted → 402 returned
|
|
216
|
+
spent = next;
|
|
217
|
+
return true;
|
|
218
|
+
},
|
|
219
|
+
});
|
|
172
220
|
|
|
173
221
|
const res = await fetch402("https://api.example.com/weather/Tokyo");
|
|
174
|
-
// → 402 → signed retry → 200 with JPYC settled on-chain
|
|
222
|
+
// → 402 → onPayment guard → signed retry → 200 with JPYC settled on-chain
|
|
175
223
|
```
|
|
176
224
|
|
|
225
|
+
> **⚠️ Call-level idempotency only.** kawasekit guarantees that a single
|
|
226
|
+
> `fetch402(...)` call settles **at most once** (EIP-3009 nonce + viem
|
|
227
|
+
> `nonceManager`). It does **not** prevent your agent from invoking
|
|
228
|
+
> `fetch402(...)` twice for the same reasoning step — retries, regeneration,
|
|
229
|
+
> pause-resume, and multi-agent fan-out can each cause duplicate charges.
|
|
230
|
+
> **Step-level idempotency is your responsibility**: track an
|
|
231
|
+
> `Idempotency-Key` per reasoning step at the agent framework layer.
|
|
232
|
+
> See [`docs/THREAT_MODEL.md` §6.1](./docs/THREAT_MODEL.md#61-reasoning-step-idempotency-gap) for the threat boundary.
|
|
233
|
+
|
|
177
234
|
### Session-key lifecycle (M3-2)
|
|
178
235
|
|
|
179
236
|
```typescript
|
|
@@ -206,21 +263,30 @@ const restored = await restoreSessionAccount({
|
|
|
206
263
|
|
|
207
264
|
## Supported Chains
|
|
208
265
|
|
|
209
|
-
|
|
266
|
+
JPYC availability and kawasekit support are **two separate axes** — JPYC being
|
|
267
|
+
live on a chain does **not** mean kawasekit has a config or has been tested
|
|
268
|
+
there. Today kawasekit ships a chain config only for **Polygon + Polygon Amoy**
|
|
269
|
+
(`src/chains/`); `getJpycAddress` / `SupportedChainId` accept only those two.
|
|
270
|
+
|
|
271
|
+
| Chain | JPYC availability | kawasekit support |
|
|
210
272
|
|---|---|---|
|
|
211
|
-
| Polygon
|
|
212
|
-
| Polygon Amoy (testnet) |
|
|
213
|
-
| Kaia |
|
|
214
|
-
| Avalanche |
|
|
215
|
-
| Ethereum |
|
|
273
|
+
| Polygon (mainnet) | ✅ Live (`0xE7C3…c29`) | ✅ M4 — config shipped, verified with live mainnet txs |
|
|
274
|
+
| Polygon Amoy (testnet) | ✅ Live (`0xE7C3…c29`) | ✅ primary testnet target |
|
|
275
|
+
| Kaia | ✅ Live (`0xE7C3…c29`, same address)¹ | 🚧 planned M5 (x402 EOA-payer path first) |
|
|
276
|
+
| Avalanche | ✅ Live (`0xE7C3…c29`) | ⬜ not yet — no chain config |
|
|
277
|
+
| Ethereum | ✅ Live (`0xE7C3…c29`) | ⬜ not yet — no chain config |
|
|
278
|
+
|
|
279
|
+
¹ JPYC officially launched on Kaia in 2026-05 (Kaia DLT Foundation; Unifi began
|
|
280
|
+
JPYC support 2026-05-22), same contract address as the other chains. kawasekit
|
|
281
|
+
has no Kaia chain config yet — support is scheduled for M5.
|
|
216
282
|
|
|
217
283
|
## Why Japan-first
|
|
218
284
|
|
|
219
285
|
The Japanese stablecoin ecosystem in 2026 is uniquely positioned:
|
|
220
286
|
|
|
221
287
|
- **JPYC** is a fully regulated yen-pegged stablecoin under the revised Payment Services Act
|
|
222
|
-
- Multi-chain by design (same address on Ethereum, Polygon, Avalanche)
|
|
223
|
-
- Kaia
|
|
288
|
+
- Multi-chain by design (same address on Ethereum, Polygon, Avalanche, and Kaia)
|
|
289
|
+
- Now live on Kaia (2026-05), with LINE NEXT's Unifi supporting JPYC since 2026-05-22
|
|
224
290
|
- Japanese AI startup ecosystem actively seeking modern payment rails
|
|
225
291
|
|
|
226
292
|
kawasekit aims to be the developer-facing layer that connects this stablecoin infrastructure to the global AI agent ecosystem.
|
|
@@ -237,11 +303,11 @@ pnpm install
|
|
|
237
303
|
pnpm dev
|
|
238
304
|
```
|
|
239
305
|
|
|
240
|
-
The site
|
|
306
|
+
The site is live at **[kawasekit.k0yote.dev](https://kawasekit.k0yote.dev)**, auto-deployed from `main` via `.github/workflows/docs.yml`.
|
|
241
307
|
|
|
242
308
|
## Contributing
|
|
243
309
|
|
|
244
|
-
This is currently a solo project, but
|
|
310
|
+
This is currently a solo project, but with M1–M4 shipped and `0.1.0-beta` on npm, contributions are now welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) for guidelines.
|
|
245
311
|
|
|
246
312
|
For now, feedback, issues, and discussions are the most valuable contributions.
|
|
247
313
|
|
|
@@ -259,4 +325,4 @@ This license includes an explicit patent grant, which is important for working i
|
|
|
259
325
|
|
|
260
326
|
---
|
|
261
327
|
|
|
262
|
-
Follow development progress: [@k0yote](https://github.com/k0yote) · Project home: kawasekit.k0yote.dev
|
|
328
|
+
Follow development progress: [@k0yote](https://github.com/k0yote) · Project home: [kawasekit.k0yote.dev](https://kawasekit.k0yote.dev)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JPYC_EIP712_DOMAIN_HINT, JPYC_V2_ADDRESS, X402InvalidPayloadError, X402_HEADER_PAYMENT_SIGNATURE, encodePaymentSignatureHeader, X402_HEADER_IDEMPOTENCY_KEY, X402_HEADER_PAYMENT_RESPONSE, decodePaymentResponseHeader, X402InvalidConfigError, X402_HEADER_PAYMENT_REQUIRED, decodePaymentRequiredHeader, jpycAbi } from './chunk-CD6SQBZN.js';
|
|
2
|
+
import { X402_VERSION, chainIdToX402Network, x402NetworkToChainId } from './chunk-WMFCI6KC.js';
|
|
2
3
|
import { getChain, isSupportedChainId } from './chunk-SA7LMQFG.js';
|
|
3
4
|
import { invokeHookSafely } from './chunk-LEHWRDVS.js';
|
|
4
|
-
import {
|
|
5
|
+
import { getAddress, keccak256, stringToHex, parseSignature, isAddress, recoverTypedDataAddress } from 'viem';
|
|
5
6
|
|
|
6
7
|
var transferWithAuthorizationTypes = {
|
|
7
8
|
TransferWithAuthorization: [
|
|
@@ -34,6 +35,20 @@ function generateAuthorizationNonce() {
|
|
|
34
35
|
crypto.getRandomValues(bytes);
|
|
35
36
|
return `0x${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
36
37
|
}
|
|
38
|
+
var EIP3009_NONCE_DOMAIN_TAG = "kawasekit/eip3009-nonce/1";
|
|
39
|
+
function deriveAuthorizationNonce(input, scope) {
|
|
40
|
+
if (input.idempotencyKey === "") {
|
|
41
|
+
throw new Error("deriveAuthorizationNonce: idempotencyKey must be a non-empty string");
|
|
42
|
+
}
|
|
43
|
+
const preimage = JSON.stringify([
|
|
44
|
+
EIP3009_NONCE_DOMAIN_TAG,
|
|
45
|
+
input.idempotencyKey,
|
|
46
|
+
getAddress(scope.from),
|
|
47
|
+
getAddress(scope.verifyingContract),
|
|
48
|
+
scope.chainId
|
|
49
|
+
]);
|
|
50
|
+
return keccak256(stringToHex(preimage));
|
|
51
|
+
}
|
|
37
52
|
function authorizationDeadlineFromNow(seconds, nowSec) {
|
|
38
53
|
const now = nowSec ?? BigInt(Math.floor(Date.now() / 1e3));
|
|
39
54
|
return now + BigInt(seconds);
|
|
@@ -98,6 +113,20 @@ function splitAuthorization(signature, domain, message) {
|
|
|
98
113
|
message
|
|
99
114
|
};
|
|
100
115
|
}
|
|
116
|
+
var KNOWN_ASSETS = [
|
|
117
|
+
{
|
|
118
|
+
id: "jpyc-v2",
|
|
119
|
+
name: JPYC_EIP712_DOMAIN_HINT.name,
|
|
120
|
+
version: JPYC_EIP712_DOMAIN_HINT.version,
|
|
121
|
+
verifyingContract: getAddress(JPYC_V2_ADDRESS)
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
function getKnownAssetDomain(id) {
|
|
125
|
+
return KNOWN_ASSETS.find((entry) => entry.id === id);
|
|
126
|
+
}
|
|
127
|
+
function listKnownAssetIds() {
|
|
128
|
+
return KNOWN_ASSETS.map((entry) => entry.id);
|
|
129
|
+
}
|
|
101
130
|
var X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS = 300;
|
|
102
131
|
var UINT256_MAX = (1n << 256n) - 1n;
|
|
103
132
|
var UINT256_DECIMAL = /^(0|[1-9][0-9]*)$/;
|
|
@@ -126,20 +155,51 @@ function assertAddress(value, field) {
|
|
|
126
155
|
}
|
|
127
156
|
return value;
|
|
128
157
|
}
|
|
129
|
-
function
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
158
|
+
function resolveAssetParam(asset) {
|
|
159
|
+
if (asset.kind === "known") {
|
|
160
|
+
const entry = getKnownAssetDomain(asset.id);
|
|
161
|
+
if (entry === void 0) {
|
|
162
|
+
throw new X402InvalidConfigError(
|
|
163
|
+
"asset.id",
|
|
164
|
+
`unknown asset id ${JSON.stringify(asset.id)}. Supported: ${listKnownAssetIds().map((id) => JSON.stringify(id)).join(", ")}.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
name: entry.name,
|
|
169
|
+
version: entry.version,
|
|
170
|
+
verifyingContract: entry.verifyingContract
|
|
171
|
+
};
|
|
136
172
|
}
|
|
137
|
-
if (
|
|
138
|
-
|
|
173
|
+
if (asset.kind === "unsafeOverride") {
|
|
174
|
+
const { domain } = asset;
|
|
175
|
+
if (typeof domain.name !== "string" || domain.name === "") {
|
|
176
|
+
throw new X402InvalidConfigError(
|
|
177
|
+
"asset.domain.name",
|
|
178
|
+
"`unsafeOverride.domain.name` must be a non-empty string"
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
if (typeof domain.version !== "string" || domain.version === "") {
|
|
182
|
+
throw new X402InvalidConfigError(
|
|
183
|
+
"asset.domain.version",
|
|
184
|
+
"`unsafeOverride.domain.version` must be a non-empty string"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (!isAddress(domain.verifyingContract, { strict: false })) {
|
|
188
|
+
throw new X402InvalidConfigError(
|
|
189
|
+
"asset.domain.verifyingContract",
|
|
190
|
+
`\`unsafeOverride.domain.verifyingContract\` must be a valid address, got ${JSON.stringify(domain.verifyingContract)}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
name: domain.name,
|
|
195
|
+
version: domain.version,
|
|
196
|
+
verifyingContract: getAddress(domain.verifyingContract)
|
|
197
|
+
};
|
|
139
198
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
199
|
+
const exhaustive = asset;
|
|
200
|
+
throw new X402InvalidConfigError(
|
|
201
|
+
"asset.kind",
|
|
202
|
+
`unsupported kind ${JSON.stringify(exhaustive.kind)}. Expected "known" or "unsafeOverride".`
|
|
143
203
|
);
|
|
144
204
|
}
|
|
145
205
|
function validateRequirements(requirements) {
|
|
@@ -179,7 +239,14 @@ function createX402PaymentSigner(params) {
|
|
|
179
239
|
`\`defaultLifetimeSeconds\` must be positive, got ${defaultLifetimeSeconds}`
|
|
180
240
|
);
|
|
181
241
|
}
|
|
182
|
-
const
|
|
242
|
+
const maxAmountPerSign = params.maxAmountPerSign;
|
|
243
|
+
if (maxAmountPerSign !== void 0 && maxAmountPerSign <= 0n) {
|
|
244
|
+
throw new X402InvalidPayloadError(
|
|
245
|
+
"X402PaymentSignerConfig",
|
|
246
|
+
`\`maxAmountPerSign\` must be a positive bigint, got ${maxAmountPerSign}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const pinnedDomain = resolveAssetParam(params.asset);
|
|
183
250
|
return {
|
|
184
251
|
address: account.address,
|
|
185
252
|
async sign(signParams) {
|
|
@@ -198,7 +265,18 @@ function createX402PaymentSigner(params) {
|
|
|
198
265
|
`signer was configured for network="testnet" but requirements.network="${paymentRequirements.network}" (chainId ${chainId}) is a mainnet \u2014 refusing to sign payment for real funds`
|
|
199
266
|
);
|
|
200
267
|
}
|
|
201
|
-
|
|
268
|
+
if (getAddress(asset) !== pinnedDomain.verifyingContract) {
|
|
269
|
+
throw new X402InvalidPayloadError(
|
|
270
|
+
"PaymentRequirements",
|
|
271
|
+
`requirements.asset (${getAddress(asset)}) does not match the signer's pinned verifyingContract (${pinnedDomain.verifyingContract}) \u2014 refusing to sign for an asset the signer was not configured to handle`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (maxAmountPerSign !== void 0 && value > maxAmountPerSign) {
|
|
275
|
+
throw new X402InvalidPayloadError(
|
|
276
|
+
"PaymentRequirements",
|
|
277
|
+
`requirements.amount (${value}) exceeds the signer's \`maxAmountPerSign\` ceiling (${maxAmountPerSign}) \u2014 refusing to sign a payment above the configured per-signature limit (threat 1.14)`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
202
280
|
const lifetime = Math.min(defaultLifetimeSeconds, paymentRequirements.maxTimeoutSeconds);
|
|
203
281
|
const validAfter = signParams.validAfter ?? 0n;
|
|
204
282
|
const validBefore = signParams.validBefore ?? authorizationDeadlineFromNow(lifetime);
|
|
@@ -208,10 +286,22 @@ function createX402PaymentSigner(params) {
|
|
|
208
286
|
`\`validBefore\` (${validBefore}) must be greater than \`validAfter\` (${validAfter})`
|
|
209
287
|
);
|
|
210
288
|
}
|
|
211
|
-
const nonce =
|
|
289
|
+
const nonce = signParams.idempotencyKey !== void 0 ? deriveAuthorizationNonce(
|
|
290
|
+
{ idempotencyKey: signParams.idempotencyKey },
|
|
291
|
+
{
|
|
292
|
+
from: account.address,
|
|
293
|
+
verifyingContract: pinnedDomain.verifyingContract,
|
|
294
|
+
chainId
|
|
295
|
+
}
|
|
296
|
+
) : generateAuthorizationNonce();
|
|
212
297
|
const signed = await signTransferWithAuthorization(
|
|
213
298
|
account,
|
|
214
|
-
{
|
|
299
|
+
{
|
|
300
|
+
name: pinnedDomain.name,
|
|
301
|
+
version: pinnedDomain.version,
|
|
302
|
+
chainId,
|
|
303
|
+
verifyingContract: pinnedDomain.verifyingContract
|
|
304
|
+
},
|
|
215
305
|
{
|
|
216
306
|
from: account.address,
|
|
217
307
|
to: payTo,
|
|
@@ -302,7 +392,7 @@ var TRANSFER_AUTHORIZATION_TYPES = {
|
|
|
302
392
|
{ name: "nonce", type: "bytes32" }
|
|
303
393
|
]
|
|
304
394
|
};
|
|
305
|
-
function
|
|
395
|
+
function resolveDomain(requirements) {
|
|
306
396
|
const extra = requirements.extra;
|
|
307
397
|
if (typeof extra.name === "string" && typeof extra.version === "string") {
|
|
308
398
|
return { name: extra.name, version: extra.version };
|
|
@@ -509,7 +599,7 @@ function createSelfFacilitator(params) {
|
|
|
509
599
|
}
|
|
510
600
|
let recovered;
|
|
511
601
|
try {
|
|
512
|
-
const domain =
|
|
602
|
+
const domain = resolveDomain(req.paymentRequirements);
|
|
513
603
|
recovered = await recoverTypedDataAddress({
|
|
514
604
|
domain: {
|
|
515
605
|
name: domain.name,
|
|
@@ -811,19 +901,22 @@ function wrapFetch(params) {
|
|
|
811
901
|
emitFailure("no_acceptable_requirement", 402);
|
|
812
902
|
return initialResponse;
|
|
813
903
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return initialResponse;
|
|
819
|
-
}
|
|
904
|
+
const proceed = await onPayment(chosen, paymentRequired);
|
|
905
|
+
if (proceed === false) {
|
|
906
|
+
emitFailure("onPayment_declined", 402);
|
|
907
|
+
return initialResponse;
|
|
820
908
|
}
|
|
909
|
+
const idempotencyKey = params.idempotencyKeyFor?.(input, chosen, paymentRequired);
|
|
821
910
|
const paymentPayload = await params.signer.sign({
|
|
822
911
|
paymentRequirements: chosen,
|
|
823
|
-
...paymentRequired.resource ? { resource: paymentRequired.resource } : {}
|
|
912
|
+
...paymentRequired.resource ? { resource: paymentRequired.resource } : {},
|
|
913
|
+
...idempotencyKey !== void 0 ? { idempotencyKey } : {}
|
|
824
914
|
});
|
|
825
915
|
const retryHeaders = new Headers(init?.headers);
|
|
826
916
|
retryHeaders.set(X402_HEADER_PAYMENT_SIGNATURE, encodePaymentSignatureHeader(paymentPayload));
|
|
917
|
+
if (idempotencyKey !== void 0) {
|
|
918
|
+
retryHeaders.set(X402_HEADER_IDEMPOTENCY_KEY, idempotencyKey);
|
|
919
|
+
}
|
|
827
920
|
const retryResponse = await baseFetch(input, { ...init, headers: retryHeaders });
|
|
828
921
|
if (retryResponse.status >= 200 && retryResponse.status < 300) {
|
|
829
922
|
const settlementHeader = retryResponse.headers.get(X402_HEADER_PAYMENT_RESPONSE);
|
|
@@ -859,6 +952,6 @@ function wrapFetch(params) {
|
|
|
859
952
|
};
|
|
860
953
|
}
|
|
861
954
|
|
|
862
|
-
export { X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS, X402_FACILITATOR_ERROR_CODES, authorizationDeadlineFromNow, createCoinbaseFacilitator, createHttpFacilitator, createSelfFacilitator, createX402PaymentSigner, generateAuthorizationNonce, signCancelAuthorization, signReceiveWithAuthorization, signTransferWithAuthorization, wrapFetch };
|
|
863
|
-
//# sourceMappingURL=chunk-
|
|
864
|
-
//# sourceMappingURL=chunk-
|
|
955
|
+
export { X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS, X402_FACILITATOR_ERROR_CODES, authorizationDeadlineFromNow, createCoinbaseFacilitator, createHttpFacilitator, createSelfFacilitator, createX402PaymentSigner, deriveAuthorizationNonce, generateAuthorizationNonce, getKnownAssetDomain, listKnownAssetIds, signCancelAuthorization, signReceiveWithAuthorization, signTransferWithAuthorization, wrapFetch };
|
|
956
|
+
//# sourceMappingURL=chunk-2V3W4B64.js.map
|
|
957
|
+
//# sourceMappingURL=chunk-2V3W4B64.js.map
|