@primitivedotdev/sdk 1.6.0 → 1.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.
- package/README.md +137 -0
- package/dist/api/index.d.ts +4 -4
- package/dist/api/index.js +3 -3
- package/dist/{api-ClLMDF81.js → api-26ENR8Ph.js} +30 -1
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.js +1 -1
- package/dist/{errors-DyuAXctD.d.ts → errors-bXUNXAlf.d.ts} +1 -1
- package/dist/{index-iZWfb98V.d.ts → index-BDnY9HH-.d.ts} +30 -48
- package/dist/{index-BnbrY8kp.d.ts → index-C3ahy3ls.d.ts} +161 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{operations.generated-DQIrhCT5.js → operations.generated-Bu20uPOw.js} +304 -0
- package/dist/parser/index.d.ts +1 -1
- package/dist/{types-QT2ss9ho.d.ts → types-BjnIxPED.d.ts} +97 -2
- package/dist/webhook/index.d.ts +4 -4
- package/dist/webhook/index.js +2 -2
- package/dist/{webhook-CwjCyFv-.js → webhook-CiIPtegj.js} +155 -26
- package/dist/x402/index.d.ts +195 -1
- package/dist/x402/index.js +279 -27
- package/package.json +1 -1
package/dist/x402/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { concat, getAddress, hexToBytes, keccak256, stringToBytes } from "viem";
|
|
2
3
|
//#region src/x402/sign.ts
|
|
3
4
|
/**
|
|
4
5
|
* x402 client-side signing.
|
|
@@ -97,6 +98,44 @@ function buildPayoutRegistrationMessage(input) {
|
|
|
97
98
|
`issued: ${input.issuedAt}`
|
|
98
99
|
].join("\n");
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* The protocol the email-native payment interaction runs (`x402.payment/1`).
|
|
103
|
+
* The payer's reply carries the `payment` step of this protocol.
|
|
104
|
+
*/
|
|
105
|
+
const X402_INTERACTION_PROTOCOL = "x402.payment";
|
|
106
|
+
const X402_INTERACTION_PROTOCOL_VERSION = 1;
|
|
107
|
+
/** A UUID (used for `interaction_id`'s local part and the step ids). */
|
|
108
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
109
|
+
/** An interaction id is `uuid@domain`. */
|
|
110
|
+
const WIRE_ID_RE = new RegExp(`^${UUID_RE.source.slice(1, -1)}@[^\\s@]+$`, "i");
|
|
111
|
+
/**
|
|
112
|
+
* Build the section-2.3 interaction.json envelope for a `payment` step. Pure: no
|
|
113
|
+
* I/O. `payment` is the signed exact-EVM payload (from
|
|
114
|
+
* `buildExactEvmPaymentPayload`); `prevStepId` is the challenge step id this
|
|
115
|
+
* payment answers, and `stepId` is a fresh UUID for the payment step. Returns
|
|
116
|
+
* the envelope and its canonical JSON, so the bytes the platform reads back are
|
|
117
|
+
* exactly the ones produced here.
|
|
118
|
+
*/
|
|
119
|
+
function buildPaymentStepEnvelope(params) {
|
|
120
|
+
if (!WIRE_ID_RE.test(params.interactionId)) throw new Error("buildPaymentStepEnvelope: interactionId must be uuid@domain");
|
|
121
|
+
if (!UUID_RE.test(params.stepId)) throw new Error("buildPaymentStepEnvelope: stepId must be a uuid");
|
|
122
|
+
if (!UUID_RE.test(params.prevStepId)) throw new Error("buildPaymentStepEnvelope: prevStepId must be a uuid");
|
|
123
|
+
const envelope = {
|
|
124
|
+
interaction_version: 1,
|
|
125
|
+
interaction_id: params.interactionId,
|
|
126
|
+
protocol: X402_INTERACTION_PROTOCOL,
|
|
127
|
+
protocol_version: 1,
|
|
128
|
+
step: "payment",
|
|
129
|
+
step_id: params.stepId,
|
|
130
|
+
prev_step_id: params.prevStepId,
|
|
131
|
+
expires_at: params.expiresAt ?? null,
|
|
132
|
+
payload: { payment: params.payment }
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
envelope,
|
|
136
|
+
json: JSON.stringify(envelope)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
100
139
|
/** Assemble the wire payload from a signed authorization. */
|
|
101
140
|
function toPaymentPayload(network, auth, signature) {
|
|
102
141
|
return {
|
|
@@ -116,14 +155,87 @@ function toPaymentPayload(network, auth, signature) {
|
|
|
116
155
|
}
|
|
117
156
|
};
|
|
118
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Absolute ceiling on the total signed window (validBefore - validAfter). A
|
|
160
|
+
* signed EIP-3009 authorization stays settleable on-chain until validBefore
|
|
161
|
+
* regardless of the interaction state, so an unbounded window is a standing
|
|
162
|
+
* "funds committed" risk. The real window is minutes; this 24h cap is the hard
|
|
163
|
+
* safety ceiling, enforced so a caller-supplied window cannot bypass it.
|
|
164
|
+
*/
|
|
165
|
+
const DEFAULT_MAX_WINDOW_SEC = 1440 * 60;
|
|
166
|
+
/**
|
|
167
|
+
* Compute the EIP-3009 validity window for a payment. `validBefore` is the
|
|
168
|
+
* value that governs on-chain validity, so it MUST cover the challenge's
|
|
169
|
+
* `expires_at` plus a settlement margin; `validAfter` is set generously in the
|
|
170
|
+
* past for clock skew. The total window is hard-capped at `maxWindowSec` so
|
|
171
|
+
* neither a far-future `challengeExpiresAtSec` nor a widened margin can produce
|
|
172
|
+
* a window the platform verifier would later reject.
|
|
173
|
+
*/
|
|
174
|
+
function computePaymentValidityWindow(params) {
|
|
175
|
+
const margin = params.settlementMarginSec ?? 300;
|
|
176
|
+
const skew = params.clockSkewSec ?? 300;
|
|
177
|
+
const validBefore = BigInt(params.challengeExpiresAtSec + margin);
|
|
178
|
+
const validAfter = BigInt(params.nowSec - skew);
|
|
179
|
+
if (validBefore <= validAfter) throw new Error("invalid validity window: validBefore must be after validAfter (challenge already expired?)");
|
|
180
|
+
const maxWindow = BigInt(params.maxWindowSec ?? 86400);
|
|
181
|
+
if (validBefore - validAfter > maxWindow) throw new Error(`invalid validity window: total window exceeds the ${maxWindow}s cap (challenge expiry too far out?)`);
|
|
182
|
+
return {
|
|
183
|
+
validAfter,
|
|
184
|
+
validBefore
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The interaction-aware signer: derive the bound nonce, assemble the
|
|
189
|
+
* authorization, and sign it. This is the one piece a stock x402 signer cannot
|
|
190
|
+
* do (it generates the nonce internally with no injection point), so the payer
|
|
191
|
+
* side needs this Primitive-provided helper. The key never leaves the caller.
|
|
192
|
+
*/
|
|
193
|
+
async function signInteractionPayment(params) {
|
|
194
|
+
const authorization = {
|
|
195
|
+
from: getAddress(params.payer),
|
|
196
|
+
to: getAddress(params.payTo),
|
|
197
|
+
value: params.amount,
|
|
198
|
+
validAfter: params.validAfter,
|
|
199
|
+
validBefore: params.validBefore,
|
|
200
|
+
nonce: deriveEip3009Nonce(params.nonceBinding)
|
|
201
|
+
};
|
|
202
|
+
return {
|
|
203
|
+
authorization,
|
|
204
|
+
signature: await params.sign(transferWithAuthorizationTypedData(params.domain, authorization))
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** The authorization nonce is 32 bytes rendered as a 0x-prefixed 64-char hex string. */
|
|
208
|
+
const NONCE_HEX_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
209
|
+
/** A shape-valid EIP signature is 65 bytes (r,s,v) rendered as 130 hex chars. */
|
|
210
|
+
const SIGNATURE_HEX_RE = /^0x[0-9a-fA-F]{130}$/;
|
|
211
|
+
/**
|
|
212
|
+
* Assemble (and validate) the exact-EVM x402 wire payload from an
|
|
213
|
+
* interaction-bound, locally-signed authorization. The numeric authorization
|
|
214
|
+
* fields are decimal strings in the wire schema, so the bigints are stringified
|
|
215
|
+
* here; the nonce passes through as hex. Validation rejects a malformed nonce or
|
|
216
|
+
* signature loudly rather than emitting a payload the platform will reject.
|
|
217
|
+
*/
|
|
218
|
+
function buildExactEvmPaymentPayload(params) {
|
|
219
|
+
if (params.network !== "base" && params.network !== "base-sepolia") throw new Error(`buildExactEvmPaymentPayload: unsupported network ${params.network}`);
|
|
220
|
+
if (!SIGNATURE_HEX_RE.test(params.signature)) throw new Error("buildExactEvmPaymentPayload: signature must be a 0x-prefixed 65-byte (130 hex char) EIP signature");
|
|
221
|
+
if (!NONCE_HEX_RE.test(params.authorization.nonce)) throw new Error("buildExactEvmPaymentPayload: authorization.nonce must be a 0x-prefixed 32-byte (64 hex char) value");
|
|
222
|
+
return toPaymentPayload(params.network, params.authorization, params.signature);
|
|
223
|
+
}
|
|
119
224
|
//#endregion
|
|
120
225
|
//#region src/x402/client.ts
|
|
226
|
+
/**
|
|
227
|
+
* x402 agent-to-agent payments.
|
|
228
|
+
*
|
|
229
|
+
* `charge()` (payee) asks for a payment; `pay()` (payer) signs and settles it
|
|
230
|
+
* with the customer's own key. The signing is local and non-custodial; the key
|
|
231
|
+
* never leaves the caller. The server resolves the real payee address, verifies
|
|
232
|
+
* every signed field against its own records, and enforces the spend policy, so
|
|
233
|
+
* the SDK's job is just: derive the bound authorization, sign, and submit.
|
|
234
|
+
*/
|
|
121
235
|
const CHAIN_IDS = {
|
|
122
236
|
"base-sepolia": 84532,
|
|
123
237
|
base: 8453
|
|
124
238
|
};
|
|
125
|
-
const CLOCK_SKEW_SEC = 300;
|
|
126
|
-
const SETTLEMENT_MARGIN_SEC = 300;
|
|
127
239
|
const DEFAULT_BASE_URL = "https://api.primitive.dev";
|
|
128
240
|
const CHARGE_INPUT_KEYS = {
|
|
129
241
|
amount: true,
|
|
@@ -135,6 +247,17 @@ const CHARGE_INPUT_KEYS = {
|
|
|
135
247
|
expiresIn: true,
|
|
136
248
|
idempotencyKey: true
|
|
137
249
|
};
|
|
250
|
+
const EMAIL_CHARGE_INPUT_KEYS = {
|
|
251
|
+
from: true,
|
|
252
|
+
to: true,
|
|
253
|
+
amount: true,
|
|
254
|
+
amountUsdc: true,
|
|
255
|
+
network: true,
|
|
256
|
+
description: true,
|
|
257
|
+
resource: true,
|
|
258
|
+
expiresIn: true,
|
|
259
|
+
idempotencyKey: true
|
|
260
|
+
};
|
|
138
261
|
function usdcToBaseUnits(human) {
|
|
139
262
|
const trimmed = human.trim();
|
|
140
263
|
if (!/^\d+(\.\d+)?$/.test(trimmed)) return null;
|
|
@@ -171,13 +294,37 @@ function validateChallenge(c) {
|
|
|
171
294
|
if (!c.expires_at) bad("expires_at");
|
|
172
295
|
const nb = c.nonce_binding;
|
|
173
296
|
if (!nb?.interaction_id || !nb.challenge_step_id || !nb.challenge_nonce) bad("nonce_binding");
|
|
174
|
-
|
|
297
|
+
validatePaymentRequirements(c.payment_requirements, bad);
|
|
298
|
+
}
|
|
299
|
+
/** Validate the x402 PaymentRequirements shared by both challenge shapes. */
|
|
300
|
+
function validatePaymentRequirements(pr, bad) {
|
|
175
301
|
if (!pr) bad("payment_requirements");
|
|
176
|
-
if (
|
|
302
|
+
if (!/^[1-9][0-9]{0,38}$/.test(pr.maxAmountRequired ?? "")) bad("payment_requirements.maxAmountRequired (expected a positive integer string in token base units)");
|
|
177
303
|
if (!/^0x[0-9a-fA-F]{40}$/.test(pr.payTo ?? "")) bad("payment_requirements.payTo (expected a 0x address)");
|
|
178
304
|
if (!/^0x[0-9a-fA-F]{40}$/.test(pr.asset ?? "")) bad("payment_requirements.asset (expected a 0x address)");
|
|
179
305
|
if (!pr.extra?.name || !pr.extra.version) bad("payment_requirements.extra (name/version)");
|
|
180
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Assert an email-native challenge is fully hydrated before signing, so a
|
|
309
|
+
* missing field fails with a named X402Error instead of an opaque error
|
|
310
|
+
* mid-sign. The interaction_id and the challenge step id (the nonce binding's
|
|
311
|
+
* fields) drive both the bound nonce and the payment-step envelope, so they are
|
|
312
|
+
* checked here.
|
|
313
|
+
*/
|
|
314
|
+
function validateEmailChallenge(c) {
|
|
315
|
+
const bad = (field) => {
|
|
316
|
+
throw new X402Error(`email challenge is missing or malformed: ${field}`, 0);
|
|
317
|
+
};
|
|
318
|
+
if (!c || typeof c !== "object") bad("email challenge");
|
|
319
|
+
if (!c.interaction_id) bad("interaction_id");
|
|
320
|
+
const ch = c.challenge;
|
|
321
|
+
if (!ch || typeof ch !== "object") bad("challenge");
|
|
322
|
+
if (!ch.expires_at) bad("challenge.expires_at");
|
|
323
|
+
const nb = ch.nonce_binding;
|
|
324
|
+
if (!nb?.interaction_id || !nb.challenge_step_id || !nb.challenge_nonce) bad("challenge.nonce_binding");
|
|
325
|
+
if (nb.interaction_id !== c.interaction_id) bad("interaction_id (mismatch with challenge.nonce_binding.interaction_id)");
|
|
326
|
+
validatePaymentRequirements(ch.payment_requirements, bad);
|
|
327
|
+
}
|
|
181
328
|
var X402Client = class {
|
|
182
329
|
#apiKey;
|
|
183
330
|
#baseUrl;
|
|
@@ -237,6 +384,99 @@ var X402Client = class {
|
|
|
237
384
|
return this.#request("POST", "/v1/x402/challenges", body, { headers: input.idempotencyKey ? { "idempotency-key": input.idempotencyKey } : void 0 });
|
|
238
385
|
}
|
|
239
386
|
/**
|
|
387
|
+
* Issue a payment challenge over an email thread (payee side). Sends the
|
|
388
|
+
* challenge as an email from `from` to `to` and binds the payment to that
|
|
389
|
+
* thread. Returns the challenge (including the real `interaction_id`); deliver
|
|
390
|
+
* it to the payer, who calls `payEmailChallenge` to build the signed payment.
|
|
391
|
+
*
|
|
392
|
+
* Provide exactly one of `amount` (base units) or `amountUsdc` (human USDC).
|
|
393
|
+
*/
|
|
394
|
+
async createEmailChallenge(input) {
|
|
395
|
+
for (const key of Object.keys(input)) if (!(key in EMAIL_CHARGE_INPUT_KEYS)) throw new X402Error(`unknown createEmailChallenge() option "${key}"; expected one of: ${Object.keys(EMAIL_CHARGE_INPUT_KEYS).join(", ")}`, 0);
|
|
396
|
+
if (!input.from) throw new X402Error("createEmailChallenge() requires `from`", 0);
|
|
397
|
+
if (!input.to) throw new X402Error("createEmailChallenge() requires `to`", 0);
|
|
398
|
+
if (input.amount !== void 0 && input.amountUsdc !== void 0) throw new X402Error("createEmailChallenge() takes exactly one of `amount` (base units) or `amountUsdc` (human USDC), not both", 0);
|
|
399
|
+
const amount = input.amountUsdc !== void 0 ? usdcToBaseUnits(input.amountUsdc) : input.amount ?? null;
|
|
400
|
+
if (!amount || !/^[1-9][0-9]{0,38}$/.test(amount)) throw new X402Error("createEmailChallenge() requires `amount` as a positive integer string in token base units (e.g. \"10000\"), or `amountUsdc` as a positive USDC amount with at most 6 decimals (e.g. \"0.01\")", 0);
|
|
401
|
+
const body = {
|
|
402
|
+
from: input.from,
|
|
403
|
+
to: input.to,
|
|
404
|
+
amount,
|
|
405
|
+
network: input.network ?? "base-sepolia"
|
|
406
|
+
};
|
|
407
|
+
if (input.description) body.description = input.description;
|
|
408
|
+
if (input.resource) body.resource = input.resource;
|
|
409
|
+
if (input.expiresIn !== void 0) body.expires_in = input.expiresIn;
|
|
410
|
+
return this.#request("POST", "/v1/x402/email-challenges", body, { headers: input.idempotencyKey ? { "idempotency-key": input.idempotencyKey } : void 0 });
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Build the signed payment step for an email-native challenge (payer side).
|
|
414
|
+
* Given a received `X402EmailChallenge` and the caller's signer, this derives
|
|
415
|
+
* the interaction-bound authorization, signs it locally, and returns the
|
|
416
|
+
* signed `interaction.json` payment-step envelope plus its canonical JSON
|
|
417
|
+
* bytes. It does NOT send anything.
|
|
418
|
+
*
|
|
419
|
+
* The caller sends `result.json` back as an `interaction.json` attachment on a
|
|
420
|
+
* reply to the challenge email (e.g. via the SDK's `send` / `reply`); the
|
|
421
|
+
* platform reads the envelope from those exact bytes, re-derives the bound
|
|
422
|
+
* nonce, and settles.
|
|
423
|
+
*/
|
|
424
|
+
async payEmailChallenge(challenge, options) {
|
|
425
|
+
if (!options?.signer?.address || typeof options.signer.signTypedData !== "function") throw new X402Error("payEmailChallenge() requires options.signer with { address, signTypedData } (e.g. a viem LocalAccount)", 0);
|
|
426
|
+
validateEmailChallenge(challenge);
|
|
427
|
+
const details = challenge.challenge;
|
|
428
|
+
const pr = details.payment_requirements;
|
|
429
|
+
const network = pr.network;
|
|
430
|
+
const chainId = CHAIN_IDS[network];
|
|
431
|
+
if (chainId === void 0) throw new X402Error(`unsupported network: ${network}`, 0);
|
|
432
|
+
if (pr.scheme !== "exact") throw new X402Error(`unsupported payment scheme: ${pr.scheme}`, 0);
|
|
433
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
434
|
+
const expiresAtMs = Date.parse(details.expires_at);
|
|
435
|
+
if (Number.isNaN(expiresAtMs)) throw new X402Error(`challenge has an invalid expires_at: ${details.expires_at}`, 0);
|
|
436
|
+
const expiresAtSec = Math.floor(expiresAtMs / 1e3);
|
|
437
|
+
if (expiresAtSec <= nowSec) throw new X402Error(`challenge has already expired (expires_at ${details.expires_at}); not signing`, 0);
|
|
438
|
+
let validAfter;
|
|
439
|
+
let validBefore;
|
|
440
|
+
try {
|
|
441
|
+
({validAfter, validBefore} = computePaymentValidityWindow({
|
|
442
|
+
challengeExpiresAtSec: expiresAtSec,
|
|
443
|
+
nowSec
|
|
444
|
+
}));
|
|
445
|
+
} catch (cause) {
|
|
446
|
+
throw new X402Error(cause instanceof Error ? cause.message : String(cause), 0, void 0, { cause });
|
|
447
|
+
}
|
|
448
|
+
const { authorization, signature } = await signInteractionPayment({
|
|
449
|
+
sign: (typedData) => options.signer.signTypedData(typedData),
|
|
450
|
+
payer: options.signer.address,
|
|
451
|
+
domain: {
|
|
452
|
+
name: pr.extra.name,
|
|
453
|
+
version: pr.extra.version,
|
|
454
|
+
chainId,
|
|
455
|
+
verifyingContract: pr.asset
|
|
456
|
+
},
|
|
457
|
+
payTo: pr.payTo,
|
|
458
|
+
amount: BigInt(pr.maxAmountRequired),
|
|
459
|
+
nonceBinding: {
|
|
460
|
+
interactionId: details.nonce_binding.interaction_id,
|
|
461
|
+
challengeStepId: details.nonce_binding.challenge_step_id,
|
|
462
|
+
challengeNonce: details.nonce_binding.challenge_nonce
|
|
463
|
+
},
|
|
464
|
+
validAfter,
|
|
465
|
+
validBefore
|
|
466
|
+
});
|
|
467
|
+
const payment = buildExactEvmPaymentPayload({
|
|
468
|
+
network,
|
|
469
|
+
authorization,
|
|
470
|
+
signature
|
|
471
|
+
});
|
|
472
|
+
return buildPaymentStepEnvelope({
|
|
473
|
+
interactionId: challenge.interaction_id,
|
|
474
|
+
stepId: randomUUID(),
|
|
475
|
+
prevStepId: details.nonce_binding.challenge_step_id,
|
|
476
|
+
payment
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
240
480
|
* Pay a challenge (payer side). Derives the interaction-bound authorization,
|
|
241
481
|
* signs it locally with the caller's key, and submits it for settlement.
|
|
242
482
|
*/
|
|
@@ -248,33 +488,45 @@ var X402Client = class {
|
|
|
248
488
|
const pr = challenge.payment_requirements;
|
|
249
489
|
if (pr.network !== challenge.network) throw new X402Error(`challenge network mismatch: ${challenge.network} vs payment_requirements ${pr.network}`, 0);
|
|
250
490
|
if (pr.scheme !== "exact") throw new X402Error(`unsupported payment scheme: ${pr.scheme}`, 0);
|
|
251
|
-
const nonce = deriveEip3009Nonce({
|
|
252
|
-
interactionId: challenge.nonce_binding.interaction_id,
|
|
253
|
-
challengeStepId: challenge.nonce_binding.challenge_step_id,
|
|
254
|
-
challengeNonce: challenge.nonce_binding.challenge_nonce
|
|
255
|
-
});
|
|
256
491
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
257
492
|
const expiresAtMs = Date.parse(challenge.expires_at);
|
|
258
493
|
if (Number.isNaN(expiresAtMs)) throw new X402Error(`challenge has an invalid expires_at: ${challenge.expires_at}`, 0);
|
|
259
494
|
const expiresAtSec = Math.floor(expiresAtMs / 1e3);
|
|
260
495
|
if (expiresAtSec <= nowSec) throw new X402Error(`challenge has already expired (expires_at ${challenge.expires_at}); not signing`, 0);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
496
|
+
let validAfter;
|
|
497
|
+
let validBefore;
|
|
498
|
+
try {
|
|
499
|
+
({validAfter, validBefore} = computePaymentValidityWindow({
|
|
500
|
+
challengeExpiresAtSec: expiresAtSec,
|
|
501
|
+
nowSec
|
|
502
|
+
}));
|
|
503
|
+
} catch (cause) {
|
|
504
|
+
throw new X402Error(cause instanceof Error ? cause.message : String(cause), 0, void 0, { cause });
|
|
505
|
+
}
|
|
506
|
+
const { authorization, signature } = await signInteractionPayment({
|
|
507
|
+
sign: (typedData) => options.signer.signTypedData(typedData),
|
|
508
|
+
payer: options.signer.address,
|
|
509
|
+
domain: {
|
|
510
|
+
name: pr.extra.name,
|
|
511
|
+
version: pr.extra.version,
|
|
512
|
+
chainId,
|
|
513
|
+
verifyingContract: pr.asset
|
|
514
|
+
},
|
|
515
|
+
payTo: pr.payTo,
|
|
516
|
+
amount: BigInt(pr.maxAmountRequired),
|
|
517
|
+
nonceBinding: {
|
|
518
|
+
interactionId: challenge.nonce_binding.interaction_id,
|
|
519
|
+
challengeStepId: challenge.nonce_binding.challenge_step_id,
|
|
520
|
+
challengeNonce: challenge.nonce_binding.challenge_nonce
|
|
521
|
+
},
|
|
267
522
|
validAfter,
|
|
268
|
-
validBefore
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
verifyingContract: pr.asset
|
|
276
|
-
}, auth));
|
|
277
|
-
return this.#request("POST", `/v1/x402/challenges/${challenge.id}/pay`, { payment: toPaymentPayload(challenge.network, auth, signature) });
|
|
523
|
+
validBefore
|
|
524
|
+
});
|
|
525
|
+
return this.#request("POST", `/v1/x402/challenges/${challenge.id}/pay`, { payment: buildExactEvmPaymentPayload({
|
|
526
|
+
network: challenge.network,
|
|
527
|
+
authorization,
|
|
528
|
+
signature
|
|
529
|
+
}) });
|
|
278
530
|
}
|
|
279
531
|
/** Fetch a challenge by id (scoped to the challenger org that created it). */
|
|
280
532
|
async getChallenge(id) {
|
|
@@ -347,4 +599,4 @@ function createX402Client(options = {}) {
|
|
|
347
599
|
return new X402Client(options);
|
|
348
600
|
}
|
|
349
601
|
//#endregion
|
|
350
|
-
export { TRANSFER_WITH_AUTHORIZATION_TYPES, X402Client, X402Error, buildPayoutRegistrationMessage, createX402Client, deriveEip3009Nonce, toPaymentPayload, transferWithAuthorizationTypedData };
|
|
602
|
+
export { DEFAULT_MAX_WINDOW_SEC, TRANSFER_WITH_AUTHORIZATION_TYPES, X402Client, X402Error, X402_INTERACTION_PROTOCOL, X402_INTERACTION_PROTOCOL_VERSION, buildExactEvmPaymentPayload, buildPaymentStepEnvelope, buildPayoutRegistrationMessage, computePaymentValidityWindow, createX402Client, deriveEip3009Nonce, signInteractionPayment, toPaymentPayload, transferWithAuthorizationTypedData };
|
package/package.json
CHANGED