@primitivedotdev/sdk 1.5.0 → 1.7.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 +110 -19
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +3 -3
- package/dist/{api-DrrmrNDe.js → api-ClLMDF81.js} +168 -1
- package/dist/{index--2RzTxVl.d.ts → index-BnbrY8kp.d.ts} +665 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/openapi/index.js +1 -1
- package/dist/{operations.generated-CA3-eorF.js → operations.generated-DQIrhCT5.js} +1512 -43
- package/dist/x402/index.d.ts +95 -4
- package/dist/x402/index.js +140 -34
- package/package.json +1 -1
package/dist/x402/index.d.ts
CHANGED
|
@@ -132,6 +132,65 @@ interface X402PaymentPayload {
|
|
|
132
132
|
}
|
|
133
133
|
/** Assemble the wire payload from a signed authorization. */
|
|
134
134
|
declare function toPaymentPayload(network: string, auth: TransferAuthorization, signature: Hex): X402PaymentPayload;
|
|
135
|
+
/**
|
|
136
|
+
* Absolute ceiling on the total signed window (validBefore - validAfter). A
|
|
137
|
+
* signed EIP-3009 authorization stays settleable on-chain until validBefore
|
|
138
|
+
* regardless of the interaction state, so an unbounded window is a standing
|
|
139
|
+
* "funds committed" risk. The real window is minutes; this 24h cap is the hard
|
|
140
|
+
* safety ceiling, enforced so a caller-supplied window cannot bypass it.
|
|
141
|
+
*/
|
|
142
|
+
declare const DEFAULT_MAX_WINDOW_SEC: number;
|
|
143
|
+
/**
|
|
144
|
+
* Compute the EIP-3009 validity window for a payment. `validBefore` is the
|
|
145
|
+
* value that governs on-chain validity, so it MUST cover the challenge's
|
|
146
|
+
* `expires_at` plus a settlement margin; `validAfter` is set generously in the
|
|
147
|
+
* past for clock skew. The total window is hard-capped at `maxWindowSec` so
|
|
148
|
+
* neither a far-future `challengeExpiresAtSec` nor a widened margin can produce
|
|
149
|
+
* a window the platform verifier would later reject.
|
|
150
|
+
*/
|
|
151
|
+
declare function computePaymentValidityWindow(params: {
|
|
152
|
+
/** The challenge's expires_at, unix seconds. */challengeExpiresAtSec: number; /** Current time, unix seconds. */
|
|
153
|
+
nowSec: number; /** Headroom past expiry for verify+settle to complete. Default 5 min. */
|
|
154
|
+
settlementMarginSec?: number; /** How far in the past to set validAfter for clock skew. Default 5 min. */
|
|
155
|
+
clockSkewSec?: number; /** Hard ceiling on validBefore - validAfter. Default 24h. */
|
|
156
|
+
maxWindowSec?: number;
|
|
157
|
+
}): {
|
|
158
|
+
validAfter: bigint;
|
|
159
|
+
validBefore: bigint;
|
|
160
|
+
};
|
|
161
|
+
/** The x402 named networks supported in v1 (testnet first). */
|
|
162
|
+
type X402Network = "base-sepolia" | "base";
|
|
163
|
+
/**
|
|
164
|
+
* The interaction-aware signer: derive the bound nonce, assemble the
|
|
165
|
+
* authorization, and sign it. This is the one piece a stock x402 signer cannot
|
|
166
|
+
* do (it generates the nonce internally with no injection point), so the payer
|
|
167
|
+
* side needs this Primitive-provided helper. The key never leaves the caller.
|
|
168
|
+
*/
|
|
169
|
+
declare function signInteractionPayment(params: {
|
|
170
|
+
/** Sign EIP-712 typed data with the caller's own key. */sign: (typedData: TransferWithAuthorizationTypedData) => Promise<Hex>; /** Payer (from) address. */
|
|
171
|
+
payer: Address;
|
|
172
|
+
domain: TokenDomain; /** Recipient (the challenger's payTo). */
|
|
173
|
+
payTo: Address; /** Amount in token base units. */
|
|
174
|
+
amount: bigint; /** Inputs that derive the interaction-bound EIP-3009 nonce. */
|
|
175
|
+
nonceBinding: NonceBinding;
|
|
176
|
+
validAfter: bigint;
|
|
177
|
+
validBefore: bigint;
|
|
178
|
+
}): Promise<{
|
|
179
|
+
authorization: TransferAuthorization;
|
|
180
|
+
signature: Hex;
|
|
181
|
+
}>;
|
|
182
|
+
/**
|
|
183
|
+
* Assemble (and validate) the exact-EVM x402 wire payload from an
|
|
184
|
+
* interaction-bound, locally-signed authorization. The numeric authorization
|
|
185
|
+
* fields are decimal strings in the wire schema, so the bigints are stringified
|
|
186
|
+
* here; the nonce passes through as hex. Validation rejects a malformed nonce or
|
|
187
|
+
* signature loudly rather than emitting a payload the platform will reject.
|
|
188
|
+
*/
|
|
189
|
+
declare function buildExactEvmPaymentPayload(params: {
|
|
190
|
+
network: X402Network;
|
|
191
|
+
authorization: TransferAuthorization;
|
|
192
|
+
signature: Hex;
|
|
193
|
+
}): X402PaymentPayload;
|
|
135
194
|
//#endregion
|
|
136
195
|
//#region src/x402/client.d.ts
|
|
137
196
|
interface X402PaymentRequirements {
|
|
@@ -184,9 +243,27 @@ interface X402SpendPolicy {
|
|
|
184
243
|
/** Allowed payee org ids; null = any on-net payee, [] = deny all. */
|
|
185
244
|
allowlist: string[] | null;
|
|
186
245
|
}
|
|
187
|
-
|
|
188
|
-
|
|
246
|
+
/** A payment the org's spend policy refused (read shape). */
|
|
247
|
+
interface X402DeclinedPayment {
|
|
248
|
+
id: string;
|
|
249
|
+
challenge_id: string | null;
|
|
250
|
+
counterparty_org: string | null;
|
|
251
|
+
network: string;
|
|
189
252
|
amount: string;
|
|
253
|
+
reason: string;
|
|
254
|
+
declined_at: string;
|
|
255
|
+
}
|
|
256
|
+
interface X402ChargeInput {
|
|
257
|
+
/**
|
|
258
|
+
* Amount in token base units (USDC has 6 decimals, so "10000" = 0.01).
|
|
259
|
+
* Provide exactly one of `amount` or `amountUsdc`.
|
|
260
|
+
*/
|
|
261
|
+
amount?: string;
|
|
262
|
+
/**
|
|
263
|
+
* Amount as human USDC (e.g. "0.01"), converted to base units for you.
|
|
264
|
+
* Provide exactly one of `amount` or `amountUsdc`.
|
|
265
|
+
*/
|
|
266
|
+
amountUsdc?: string;
|
|
190
267
|
/** Defaults to "base-sepolia". */
|
|
191
268
|
network?: string;
|
|
192
269
|
/** The org id allowed to pay this challenge (on-net binding). */
|
|
@@ -196,6 +273,11 @@ interface X402ChargeInput {
|
|
|
196
273
|
resource?: string;
|
|
197
274
|
/** Seconds until the challenge expires (default 1h). */
|
|
198
275
|
expiresIn?: number;
|
|
276
|
+
/**
|
|
277
|
+
* Optional idempotency key. Retrying `charge()` with the same key returns the
|
|
278
|
+
* original challenge instead of creating a duplicate.
|
|
279
|
+
*/
|
|
280
|
+
idempotencyKey?: string;
|
|
199
281
|
}
|
|
200
282
|
declare class X402Error extends Error {
|
|
201
283
|
/** HTTP status, or 0 for a client-side / transport error that never reached the server. */
|
|
@@ -238,16 +320,25 @@ declare class X402Client {
|
|
|
238
320
|
* address becomes (or updates to) the default payout destination for the
|
|
239
321
|
* network. `charge()` resolves its `pay_to` from this directory, so a payee
|
|
240
322
|
* must register before requesting payments.
|
|
323
|
+
*
|
|
324
|
+
* `org` is optional: when omitted it is resolved from your authenticated
|
|
325
|
+
* account, so most callers never need to supply it.
|
|
241
326
|
*/
|
|
242
327
|
registerPayoutAddress(input: {
|
|
243
|
-
org
|
|
328
|
+
org?: string;
|
|
244
329
|
network?: string;
|
|
245
330
|
issuedAt?: string;
|
|
331
|
+
label?: string;
|
|
246
332
|
}, options: {
|
|
247
333
|
signer: X402Signer;
|
|
248
334
|
}): Promise<X402PayoutAddress>;
|
|
249
335
|
/** List your org's registered payout addresses. */
|
|
250
336
|
listPayoutAddresses(): Promise<X402PayoutAddress[]>;
|
|
337
|
+
/**
|
|
338
|
+
* List the most recent payments your org's spend policy declined (newest
|
|
339
|
+
* first). Use this to see why an outbound payment was refused.
|
|
340
|
+
*/
|
|
341
|
+
listDeclinedPayments(): Promise<X402DeclinedPayment[]>;
|
|
251
342
|
/** Read your org's spend policy (kill-switch + caps + allowlist). */
|
|
252
343
|
getSpendPolicy(): Promise<X402SpendPolicy>;
|
|
253
344
|
/**
|
|
@@ -260,4 +351,4 @@ declare class X402Client {
|
|
|
260
351
|
}
|
|
261
352
|
declare function createX402Client(options?: X402ClientOptions): X402Client;
|
|
262
353
|
//#endregion
|
|
263
|
-
export { NonceBinding, PayoutRegistrationMessageInput, TRANSFER_WITH_AUTHORIZATION_TYPES, TokenDomain, TransferAuthorization, TransferWithAuthorizationTypedData, X402Challenge, X402ChargeInput, X402Client, X402ClientOptions, X402Error, X402PaymentPayload, X402PaymentRequirements, X402PayoutAddress, X402Receipt, X402Signer, X402SpendPolicy, buildPayoutRegistrationMessage, createX402Client, deriveEip3009Nonce, toPaymentPayload, transferWithAuthorizationTypedData };
|
|
354
|
+
export { DEFAULT_MAX_WINDOW_SEC, NonceBinding, PayoutRegistrationMessageInput, TRANSFER_WITH_AUTHORIZATION_TYPES, TokenDomain, TransferAuthorization, TransferWithAuthorizationTypedData, X402Challenge, X402ChargeInput, X402Client, X402ClientOptions, X402DeclinedPayment, X402Error, X402Network, X402PaymentPayload, X402PaymentRequirements, X402PayoutAddress, X402Receipt, X402Signer, X402SpendPolicy, buildExactEvmPaymentPayload, buildPayoutRegistrationMessage, computePaymentValidityWindow, createX402Client, deriveEip3009Nonce, signInteractionPayment, toPaymentPayload, transferWithAuthorizationTypedData };
|
package/dist/x402/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { concat, hexToBytes, keccak256, stringToBytes } from "viem";
|
|
1
|
+
import { concat, getAddress, hexToBytes, keccak256, stringToBytes } from "viem";
|
|
2
2
|
//#region src/x402/sign.ts
|
|
3
3
|
/**
|
|
4
4
|
* x402 client-side signing.
|
|
@@ -116,23 +116,97 @@ function toPaymentPayload(network, auth, signature) {
|
|
|
116
116
|
}
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Absolute ceiling on the total signed window (validBefore - validAfter). A
|
|
121
|
+
* signed EIP-3009 authorization stays settleable on-chain until validBefore
|
|
122
|
+
* regardless of the interaction state, so an unbounded window is a standing
|
|
123
|
+
* "funds committed" risk. The real window is minutes; this 24h cap is the hard
|
|
124
|
+
* safety ceiling, enforced so a caller-supplied window cannot bypass it.
|
|
125
|
+
*/
|
|
126
|
+
const DEFAULT_MAX_WINDOW_SEC = 1440 * 60;
|
|
127
|
+
/**
|
|
128
|
+
* Compute the EIP-3009 validity window for a payment. `validBefore` is the
|
|
129
|
+
* value that governs on-chain validity, so it MUST cover the challenge's
|
|
130
|
+
* `expires_at` plus a settlement margin; `validAfter` is set generously in the
|
|
131
|
+
* past for clock skew. The total window is hard-capped at `maxWindowSec` so
|
|
132
|
+
* neither a far-future `challengeExpiresAtSec` nor a widened margin can produce
|
|
133
|
+
* a window the platform verifier would later reject.
|
|
134
|
+
*/
|
|
135
|
+
function computePaymentValidityWindow(params) {
|
|
136
|
+
const margin = params.settlementMarginSec ?? 300;
|
|
137
|
+
const skew = params.clockSkewSec ?? 300;
|
|
138
|
+
const validBefore = BigInt(params.challengeExpiresAtSec + margin);
|
|
139
|
+
const validAfter = BigInt(params.nowSec - skew);
|
|
140
|
+
if (validBefore <= validAfter) throw new Error("invalid validity window: validBefore must be after validAfter (challenge already expired?)");
|
|
141
|
+
const maxWindow = BigInt(params.maxWindowSec ?? 86400);
|
|
142
|
+
if (validBefore - validAfter > maxWindow) throw new Error(`invalid validity window: total window exceeds the ${maxWindow}s cap (challenge expiry too far out?)`);
|
|
143
|
+
return {
|
|
144
|
+
validAfter,
|
|
145
|
+
validBefore
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The interaction-aware signer: derive the bound nonce, assemble the
|
|
150
|
+
* authorization, and sign it. This is the one piece a stock x402 signer cannot
|
|
151
|
+
* do (it generates the nonce internally with no injection point), so the payer
|
|
152
|
+
* side needs this Primitive-provided helper. The key never leaves the caller.
|
|
153
|
+
*/
|
|
154
|
+
async function signInteractionPayment(params) {
|
|
155
|
+
const authorization = {
|
|
156
|
+
from: getAddress(params.payer),
|
|
157
|
+
to: getAddress(params.payTo),
|
|
158
|
+
value: params.amount,
|
|
159
|
+
validAfter: params.validAfter,
|
|
160
|
+
validBefore: params.validBefore,
|
|
161
|
+
nonce: deriveEip3009Nonce(params.nonceBinding)
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
authorization,
|
|
165
|
+
signature: await params.sign(transferWithAuthorizationTypedData(params.domain, authorization))
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** The authorization nonce is 32 bytes rendered as a 0x-prefixed 64-char hex string. */
|
|
169
|
+
const NONCE_HEX_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
170
|
+
/** A shape-valid EIP signature is 65 bytes (r,s,v) rendered as 130 hex chars. */
|
|
171
|
+
const SIGNATURE_HEX_RE = /^0x[0-9a-fA-F]{130}$/;
|
|
172
|
+
/**
|
|
173
|
+
* Assemble (and validate) the exact-EVM x402 wire payload from an
|
|
174
|
+
* interaction-bound, locally-signed authorization. The numeric authorization
|
|
175
|
+
* fields are decimal strings in the wire schema, so the bigints are stringified
|
|
176
|
+
* here; the nonce passes through as hex. Validation rejects a malformed nonce or
|
|
177
|
+
* signature loudly rather than emitting a payload the platform will reject.
|
|
178
|
+
*/
|
|
179
|
+
function buildExactEvmPaymentPayload(params) {
|
|
180
|
+
if (params.network !== "base" && params.network !== "base-sepolia") throw new Error(`buildExactEvmPaymentPayload: unsupported network ${params.network}`);
|
|
181
|
+
if (!SIGNATURE_HEX_RE.test(params.signature)) throw new Error("buildExactEvmPaymentPayload: signature must be a 0x-prefixed 65-byte (130 hex char) EIP signature");
|
|
182
|
+
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");
|
|
183
|
+
return toPaymentPayload(params.network, params.authorization, params.signature);
|
|
184
|
+
}
|
|
119
185
|
//#endregion
|
|
120
186
|
//#region src/x402/client.ts
|
|
121
187
|
const CHAIN_IDS = {
|
|
122
188
|
"base-sepolia": 84532,
|
|
123
189
|
base: 8453
|
|
124
190
|
};
|
|
125
|
-
const CLOCK_SKEW_SEC = 300;
|
|
126
|
-
const SETTLEMENT_MARGIN_SEC = 300;
|
|
127
191
|
const DEFAULT_BASE_URL = "https://api.primitive.dev";
|
|
128
192
|
const CHARGE_INPUT_KEYS = {
|
|
129
193
|
amount: true,
|
|
194
|
+
amountUsdc: true,
|
|
130
195
|
network: true,
|
|
131
196
|
payerOrg: true,
|
|
132
197
|
description: true,
|
|
133
198
|
resource: true,
|
|
134
|
-
expiresIn: true
|
|
199
|
+
expiresIn: true,
|
|
200
|
+
idempotencyKey: true
|
|
135
201
|
};
|
|
202
|
+
function usdcToBaseUnits(human) {
|
|
203
|
+
const trimmed = human.trim();
|
|
204
|
+
if (!/^\d+(\.\d+)?$/.test(trimmed)) return null;
|
|
205
|
+
const [whole, frac = ""] = trimmed.split(".");
|
|
206
|
+
if (frac.length > 6) return null;
|
|
207
|
+
const base = BigInt(whole) * 1000000n + BigInt(frac.padEnd(6, "0"));
|
|
208
|
+
return base > 0n ? base.toString() : null;
|
|
209
|
+
}
|
|
136
210
|
var X402Error = class extends Error {
|
|
137
211
|
/** HTTP status, or 0 for a client-side / transport error that never reached the server. */
|
|
138
212
|
status;
|
|
@@ -163,7 +237,7 @@ function validateChallenge(c) {
|
|
|
163
237
|
if (!nb?.interaction_id || !nb.challenge_step_id || !nb.challenge_nonce) bad("nonce_binding");
|
|
164
238
|
const pr = c.payment_requirements;
|
|
165
239
|
if (!pr) bad("payment_requirements");
|
|
166
|
-
if (
|
|
240
|
+
if (!/^[1-9][0-9]{0,38}$/.test(pr.maxAmountRequired ?? "")) bad("payment_requirements.maxAmountRequired (expected a positive integer string in token base units)");
|
|
167
241
|
if (!/^0x[0-9a-fA-F]{40}$/.test(pr.payTo ?? "")) bad("payment_requirements.payTo (expected a 0x address)");
|
|
168
242
|
if (!/^0x[0-9a-fA-F]{40}$/.test(pr.asset ?? "")) bad("payment_requirements.asset (expected a 0x address)");
|
|
169
243
|
if (!pr.extra?.name || !pr.extra.version) bad("payment_requirements.extra (name/version)");
|
|
@@ -189,7 +263,8 @@ var X402Client = class {
|
|
|
189
263
|
method,
|
|
190
264
|
headers: {
|
|
191
265
|
authorization: `Bearer ${this.#apiKey}`,
|
|
192
|
-
"content-type": "application/json"
|
|
266
|
+
"content-type": "application/json",
|
|
267
|
+
...init?.headers
|
|
193
268
|
},
|
|
194
269
|
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
195
270
|
signal
|
|
@@ -212,16 +287,18 @@ var X402Client = class {
|
|
|
212
287
|
/** Request a payment (payee side). Returns the challenge to hand to the payer. */
|
|
213
288
|
async charge(input) {
|
|
214
289
|
for (const key of Object.keys(input)) if (!(key in CHARGE_INPUT_KEYS)) throw new X402Error(`unknown charge() option "${key}"; expected one of: ${Object.keys(CHARGE_INPUT_KEYS).join(", ")}`, 0);
|
|
215
|
-
if (
|
|
290
|
+
if (input.amount !== void 0 && input.amountUsdc !== void 0) throw new X402Error("charge() takes exactly one of `amount` (base units) or `amountUsdc` (human USDC), not both", 0);
|
|
291
|
+
const amount = input.amountUsdc !== void 0 ? usdcToBaseUnits(input.amountUsdc) : input.amount ?? null;
|
|
292
|
+
if (!amount || !/^[1-9][0-9]{0,38}$/.test(amount)) throw new X402Error("charge() 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);
|
|
216
293
|
const body = {
|
|
217
|
-
amount
|
|
294
|
+
amount,
|
|
218
295
|
network: input.network ?? "base-sepolia"
|
|
219
296
|
};
|
|
220
297
|
if (input.payerOrg) body.payer_org = input.payerOrg;
|
|
221
298
|
if (input.description) body.description = input.description;
|
|
222
299
|
if (input.resource) body.resource = input.resource;
|
|
223
300
|
if (input.expiresIn !== void 0) body.expires_in = input.expiresIn;
|
|
224
|
-
return this.#request("POST", "/v1/x402/challenges", body);
|
|
301
|
+
return this.#request("POST", "/v1/x402/challenges", body, { headers: input.idempotencyKey ? { "idempotency-key": input.idempotencyKey } : void 0 });
|
|
225
302
|
}
|
|
226
303
|
/**
|
|
227
304
|
* Pay a challenge (payer side). Derives the interaction-bound authorization,
|
|
@@ -235,54 +312,75 @@ var X402Client = class {
|
|
|
235
312
|
const pr = challenge.payment_requirements;
|
|
236
313
|
if (pr.network !== challenge.network) throw new X402Error(`challenge network mismatch: ${challenge.network} vs payment_requirements ${pr.network}`, 0);
|
|
237
314
|
if (pr.scheme !== "exact") throw new X402Error(`unsupported payment scheme: ${pr.scheme}`, 0);
|
|
238
|
-
const nonce = deriveEip3009Nonce({
|
|
239
|
-
interactionId: challenge.nonce_binding.interaction_id,
|
|
240
|
-
challengeStepId: challenge.nonce_binding.challenge_step_id,
|
|
241
|
-
challengeNonce: challenge.nonce_binding.challenge_nonce
|
|
242
|
-
});
|
|
243
315
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
244
316
|
const expiresAtMs = Date.parse(challenge.expires_at);
|
|
245
317
|
if (Number.isNaN(expiresAtMs)) throw new X402Error(`challenge has an invalid expires_at: ${challenge.expires_at}`, 0);
|
|
246
318
|
const expiresAtSec = Math.floor(expiresAtMs / 1e3);
|
|
247
319
|
if (expiresAtSec <= nowSec) throw new X402Error(`challenge has already expired (expires_at ${challenge.expires_at}); not signing`, 0);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
320
|
+
let validAfter;
|
|
321
|
+
let validBefore;
|
|
322
|
+
try {
|
|
323
|
+
({validAfter, validBefore} = computePaymentValidityWindow({
|
|
324
|
+
challengeExpiresAtSec: expiresAtSec,
|
|
325
|
+
nowSec
|
|
326
|
+
}));
|
|
327
|
+
} catch (cause) {
|
|
328
|
+
throw new X402Error(cause instanceof Error ? cause.message : String(cause), 0, void 0, { cause });
|
|
329
|
+
}
|
|
330
|
+
const { authorization, signature } = await signInteractionPayment({
|
|
331
|
+
sign: (typedData) => options.signer.signTypedData(typedData),
|
|
332
|
+
payer: options.signer.address,
|
|
333
|
+
domain: {
|
|
334
|
+
name: pr.extra.name,
|
|
335
|
+
version: pr.extra.version,
|
|
336
|
+
chainId,
|
|
337
|
+
verifyingContract: pr.asset
|
|
338
|
+
},
|
|
339
|
+
payTo: pr.payTo,
|
|
340
|
+
amount: BigInt(pr.maxAmountRequired),
|
|
341
|
+
nonceBinding: {
|
|
342
|
+
interactionId: challenge.nonce_binding.interaction_id,
|
|
343
|
+
challengeStepId: challenge.nonce_binding.challenge_step_id,
|
|
344
|
+
challengeNonce: challenge.nonce_binding.challenge_nonce
|
|
345
|
+
},
|
|
254
346
|
validAfter,
|
|
255
|
-
validBefore
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
verifyingContract: pr.asset
|
|
263
|
-
}, auth));
|
|
264
|
-
return this.#request("POST", `/v1/x402/challenges/${challenge.id}/pay`, { payment: toPaymentPayload(challenge.network, auth, signature) });
|
|
347
|
+
validBefore
|
|
348
|
+
});
|
|
349
|
+
return this.#request("POST", `/v1/x402/challenges/${challenge.id}/pay`, { payment: buildExactEvmPaymentPayload({
|
|
350
|
+
network: challenge.network,
|
|
351
|
+
authorization,
|
|
352
|
+
signature
|
|
353
|
+
}) });
|
|
265
354
|
}
|
|
266
355
|
/** Fetch a challenge by id (scoped to the challenger org that created it). */
|
|
267
356
|
async getChallenge(id) {
|
|
268
357
|
if (!id) throw new X402Error("getChallenge() requires a challenge id", 0);
|
|
269
358
|
return this.#request("GET", `/v1/x402/challenges/${encodeURIComponent(id)}`);
|
|
270
359
|
}
|
|
360
|
+
/** Resolve the caller's own organization id from the account endpoint. */
|
|
361
|
+
async #resolveOrgId() {
|
|
362
|
+
const account = await this.#request("GET", "/v1/account");
|
|
363
|
+
if (!account?.id) throw new X402Error("could not resolve your organization id from /v1/account; pass { org } explicitly", 0);
|
|
364
|
+
return account.id;
|
|
365
|
+
}
|
|
271
366
|
/**
|
|
272
367
|
* Register a payout address for your org (payee side). The signer proves
|
|
273
368
|
* control of its own address with an org-bound `personal_sign`; the proven
|
|
274
369
|
* address becomes (or updates to) the default payout destination for the
|
|
275
370
|
* network. `charge()` resolves its `pay_to` from this directory, so a payee
|
|
276
371
|
* must register before requesting payments.
|
|
372
|
+
*
|
|
373
|
+
* `org` is optional: when omitted it is resolved from your authenticated
|
|
374
|
+
* account, so most callers never need to supply it.
|
|
277
375
|
*/
|
|
278
376
|
async registerPayoutAddress(input, options) {
|
|
279
|
-
if (!input?.org) throw new X402Error("registerPayoutAddress() requires an org id", 0);
|
|
280
377
|
if (typeof options?.signer?.signMessage !== "function") throw new X402Error("registerPayoutAddress() requires a signer with signMessage (e.g. a viem LocalAccount)", 0);
|
|
378
|
+
const org = input.org ?? await this.#resolveOrgId();
|
|
281
379
|
const network = input.network ?? "base-sepolia";
|
|
282
380
|
const issuedAt = input.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
283
381
|
const address = options.signer.address;
|
|
284
382
|
const message = buildPayoutRegistrationMessage({
|
|
285
|
-
org
|
|
383
|
+
org,
|
|
286
384
|
address,
|
|
287
385
|
network,
|
|
288
386
|
issuedAt
|
|
@@ -292,13 +390,21 @@ var X402Client = class {
|
|
|
292
390
|
address,
|
|
293
391
|
network,
|
|
294
392
|
signature,
|
|
295
|
-
issued_at: issuedAt
|
|
393
|
+
issued_at: issuedAt,
|
|
394
|
+
...input.label !== void 0 ? { label: input.label } : {}
|
|
296
395
|
});
|
|
297
396
|
}
|
|
298
397
|
/** List your org's registered payout addresses. */
|
|
299
398
|
async listPayoutAddresses() {
|
|
300
399
|
return this.#request("GET", "/v1/x402/payout-addresses");
|
|
301
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* List the most recent payments your org's spend policy declined (newest
|
|
403
|
+
* first). Use this to see why an outbound payment was refused.
|
|
404
|
+
*/
|
|
405
|
+
async listDeclinedPayments() {
|
|
406
|
+
return this.#request("GET", "/v1/x402/declined-payments");
|
|
407
|
+
}
|
|
302
408
|
/** Read your org's spend policy (kill-switch + caps + allowlist). */
|
|
303
409
|
async getSpendPolicy() {
|
|
304
410
|
return this.#request("GET", "/v1/x402/spend-policy");
|
|
@@ -317,4 +423,4 @@ function createX402Client(options = {}) {
|
|
|
317
423
|
return new X402Client(options);
|
|
318
424
|
}
|
|
319
425
|
//#endregion
|
|
320
|
-
export { TRANSFER_WITH_AUTHORIZATION_TYPES, X402Client, X402Error, buildPayoutRegistrationMessage, createX402Client, deriveEip3009Nonce, toPaymentPayload, transferWithAuthorizationTypedData };
|
|
426
|
+
export { DEFAULT_MAX_WINDOW_SEC, TRANSFER_WITH_AUTHORIZATION_TYPES, X402Client, X402Error, buildExactEvmPaymentPayload, buildPayoutRegistrationMessage, computePaymentValidityWindow, createX402Client, deriveEip3009Nonce, signInteractionPayment, toPaymentPayload, transferWithAuthorizationTypedData };
|
package/package.json
CHANGED