@bosonprotocol/x402-client 0.1.1-alpha-2 → 0.2.0-alpha-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +126 -3
- package/dist/cjs/index.js +280 -21
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +276 -22
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -3
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { P as Policy, X as X402bClientConfig, E as ExchangeSummary, S as Signer } from './types-DLOroMb1.js';
|
|
2
2
|
export { F as FulfillmentConfig, R as RedeemMode, T as TokenDomainResolver } from './types-DLOroMb1.js';
|
|
3
|
-
import { EscrowPaymentRequirements, BosonCommitActionId, BosonMetaTx, Hex as Hex$1 } from '@bosonprotocol/x402-core/schemes/escrow';
|
|
4
|
-
import { ActionId } from '@bosonprotocol/x402-core/state-machine';
|
|
3
|
+
import { EscrowPaymentRequirements, BosonCommitActionId, BosonMetaTx, Hex as Hex$1, NextAction, EscrowNextActions } from '@bosonprotocol/x402-core/schemes/escrow';
|
|
4
|
+
import { ActionId, ExchangeState, DisputeState } from '@bosonprotocol/x402-core/state-machine';
|
|
5
5
|
import { Address, Hex } from 'viem';
|
|
6
6
|
import '@bosonprotocol/x402-core/eip712/token-auth';
|
|
7
7
|
|
|
@@ -28,6 +28,11 @@ declare class MaxAmountExceededError extends Error {
|
|
|
28
28
|
*/
|
|
29
29
|
declare function pickAction(requirements: EscrowPaymentRequirements, policy?: Policy): BosonCommitActionId;
|
|
30
30
|
|
|
31
|
+
/** Decode a base64-encoded string to UTF-8. */
|
|
32
|
+
declare function decodeBase64(value: string): string;
|
|
33
|
+
/** Encode a UTF-8 string as base64. */
|
|
34
|
+
declare function encodeBase64(value: string): string;
|
|
35
|
+
|
|
31
36
|
interface ResolvedFulfillment {
|
|
32
37
|
option: string;
|
|
33
38
|
/**
|
|
@@ -121,6 +126,84 @@ interface SignedPostCommitAction {
|
|
|
121
126
|
signedPayload: Hex$1;
|
|
122
127
|
}
|
|
123
128
|
|
|
129
|
+
/** Channels this submitter drives over HTTP. */
|
|
130
|
+
type SubmitChannel = "server" | "facilitator";
|
|
131
|
+
/**
|
|
132
|
+
* Why a channel attempt didn't yield a 2xx. `"no-endpoint"` is a
|
|
133
|
+
* configuration gap (channel advertised, but no URL listed under
|
|
134
|
+
* `action.endpoints`) — distinct from a real transport-level
|
|
135
|
+
* `"network"` failure so callers branching on `reason` can tell the
|
|
136
|
+
* two apart. `"invalid-response"` covers 2xx replies whose body
|
|
137
|
+
* doesn't match the channel's expected shape — the server replied
|
|
138
|
+
* but with something we can't make sense of, treated as a
|
|
139
|
+
* recoverable failure (the next channel is tried).
|
|
140
|
+
*/
|
|
141
|
+
type ChannelFailureReason = "5xx" | "4xx" | "network" | "timeout" | "no-endpoint" | "invalid-response";
|
|
142
|
+
/** Per-channel attempt record — every walk-step appended to `attempts[]`. */
|
|
143
|
+
type ChannelAttempt = {
|
|
144
|
+
channel: SubmitChannel;
|
|
145
|
+
ok: true;
|
|
146
|
+
status: number;
|
|
147
|
+
} | {
|
|
148
|
+
channel: SubmitChannel;
|
|
149
|
+
ok: false;
|
|
150
|
+
reason: ChannelFailureReason;
|
|
151
|
+
status?: number;
|
|
152
|
+
message?: string;
|
|
153
|
+
};
|
|
154
|
+
/** Caller passes `{ option, data }` for the redeem-only fulfillment payload. */
|
|
155
|
+
interface FulfillmentRequest {
|
|
156
|
+
option: string;
|
|
157
|
+
data: Record<string, unknown> | null;
|
|
158
|
+
}
|
|
159
|
+
interface SubmitArgs {
|
|
160
|
+
action: NextAction;
|
|
161
|
+
signed: SignedPostCommitAction;
|
|
162
|
+
exchangeId: string;
|
|
163
|
+
/** CAIP-2 (e.g. `"eip155:31337"`). Required by the facilitator route. */
|
|
164
|
+
network: string;
|
|
165
|
+
/** Escrow contract address. Required by the facilitator route. */
|
|
166
|
+
escrowAddress: Address;
|
|
167
|
+
/** Redeem-only — forwarded to the `server` channel body; ignored by `facilitator`. */
|
|
168
|
+
fulfillment?: FulfillmentRequest;
|
|
169
|
+
/** Defaults to `globalThis.fetch`. */
|
|
170
|
+
fetch?: typeof globalThis.fetch;
|
|
171
|
+
/** Per-channel timeout in milliseconds. Defaults to 10000. */
|
|
172
|
+
timeoutMs?: number;
|
|
173
|
+
}
|
|
174
|
+
interface SubmitResult {
|
|
175
|
+
txHash: Hex;
|
|
176
|
+
newExchangeState: ExchangeState;
|
|
177
|
+
newDisputeState?: DisputeState;
|
|
178
|
+
/** Present only when the `server` channel handled the action. */
|
|
179
|
+
nextActions?: EscrowNextActions;
|
|
180
|
+
channelUsed: SubmitChannel;
|
|
181
|
+
attempts: readonly ChannelAttempt[];
|
|
182
|
+
}
|
|
183
|
+
/** Thrown when no advertised channel matches one this submitter can drive. */
|
|
184
|
+
declare class NoCompatibleChannelError extends Error {
|
|
185
|
+
readonly actionId: string;
|
|
186
|
+
readonly advertisedChannels: readonly string[];
|
|
187
|
+
constructor(actionId: string, advertisedChannels: readonly string[]);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Thrown when every attempted channel failed. Carries the per-channel
|
|
191
|
+
* attempt log so callers can branch on the final cause (e.g. 4xx from
|
|
192
|
+
* the server → buyer payload bug; 5xx from both → outage).
|
|
193
|
+
*/
|
|
194
|
+
declare class AllChannelsFailedError extends Error {
|
|
195
|
+
readonly attempts: readonly ChannelAttempt[];
|
|
196
|
+
constructor(actionId: string, attempts: readonly ChannelAttempt[]);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Submit a signed post-commit action through the first responsive HTTP
|
|
200
|
+
* channel advertised on `action.channels`. Returns the normalized result
|
|
201
|
+
* on first 2xx; throws `NoCompatibleChannelError` when no submittable
|
|
202
|
+
* channel is advertised, or `AllChannelsFailedError` when every attempt
|
|
203
|
+
* failed.
|
|
204
|
+
*/
|
|
205
|
+
declare function submitAction(args: SubmitArgs): Promise<SubmitResult>;
|
|
206
|
+
|
|
124
207
|
type BigNumberish = string | number | bigint;
|
|
125
208
|
/**
|
|
126
209
|
* Resolution targets accepted by the "withdraw all" helper. Either
|
|
@@ -165,6 +248,27 @@ interface SignedWithdrawFunds {
|
|
|
165
248
|
tokenAmounts: readonly string[];
|
|
166
249
|
}
|
|
167
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Args for `client.submitAction`. Layered over `SignActionArgs`: the
|
|
253
|
+
* client signs the meta-tx, then resolves the matching `NextAction`
|
|
254
|
+
* entry on `priorNextActions` and walks its `channels[]` (server →
|
|
255
|
+
* facilitator) until the first 2xx. See `submit.ts` for the channel
|
|
256
|
+
* walk semantics; see `signAction` for the signing args.
|
|
257
|
+
*/
|
|
258
|
+
type SubmitActionArgs = SignActionArgs & {
|
|
259
|
+
/**
|
|
260
|
+
* The `nextActions` envelope returned by the prior server response.
|
|
261
|
+
* Used to read `channels[]` + `endpoints[channel]` for the entry
|
|
262
|
+
* matching `actionId`.
|
|
263
|
+
*/
|
|
264
|
+
priorNextActions: EscrowNextActions;
|
|
265
|
+
/** Redeem-only fulfillment payload — forwarded to the `server` channel body. */
|
|
266
|
+
fulfillment?: FulfillmentRequest;
|
|
267
|
+
/** Override the default `globalThis.fetch`. Useful for tests / custom transports. */
|
|
268
|
+
fetch?: typeof globalThis.fetch;
|
|
269
|
+
/** Per-channel timeout in milliseconds. Default 10000. */
|
|
270
|
+
timeoutMs?: number;
|
|
271
|
+
};
|
|
168
272
|
interface X402bClient {
|
|
169
273
|
/**
|
|
170
274
|
* Consume a parsed escrow PaymentRequirements and return the base64
|
|
@@ -188,6 +292,25 @@ interface X402bClient {
|
|
|
188
292
|
* of MVP.
|
|
189
293
|
*/
|
|
190
294
|
signAction(args: SignActionArgs): Promise<SignedPostCommitAction>;
|
|
295
|
+
/**
|
|
296
|
+
* Sign a buyer post-commit action via {@link X402bClient.signAction} and
|
|
297
|
+
* submit it through the first responsive HTTP channel the seller
|
|
298
|
+
* advertised on the matching `nextActions.next[]` entry. Order is taken
|
|
299
|
+
* from `priorNextActions` (the envelope from the prior server response);
|
|
300
|
+
* the walk is intersected with `["server", "facilitator"]` — `onchain` /
|
|
301
|
+
* `mcp` / `xmtp` channels are out of scope for this method.
|
|
302
|
+
*
|
|
303
|
+
* Falls back to the next channel on **5xx / network error / timeout**.
|
|
304
|
+
* Stops and throws on **4xx** (a buyer-side payload error fallback
|
|
305
|
+
* can't fix — masking it would hide bugs). Throws
|
|
306
|
+
* `NoCompatibleChannelError` when the matching entry advertises only
|
|
307
|
+
* non-HTTP channels, and `AllChannelsFailedError` when every attempt
|
|
308
|
+
* failed.
|
|
309
|
+
*
|
|
310
|
+
* Pre-existing callers that prefer to keep submission outside the SDK
|
|
311
|
+
* can still use `signAction` + their own dispatch.
|
|
312
|
+
*/
|
|
313
|
+
submitAction(args: SubmitActionArgs): Promise<SubmitResult>;
|
|
191
314
|
/**
|
|
192
315
|
* Sign a `withdrawFunds(entityId, tokenList, tokenAmounts)` meta-tx.
|
|
193
316
|
* Caller-resolved entity + tokens snapshot — see
|
|
@@ -237,4 +360,4 @@ interface Web3LibAdapterLike {
|
|
|
237
360
|
*/
|
|
238
361
|
declare function signerFromEthersAdapter(adapter: Web3LibAdapterLike): Signer;
|
|
239
362
|
|
|
240
|
-
export { ExchangeSummary, FulfillmentValidationError, MaxAmountExceededError, NoCompatibleActionError, Policy, type ResolveDisputeArgs, type ResolvedFulfillment, type SignActionArgs, type SignWithdrawAllAvailableFundsArgs, type SignWithdrawFundsArgs, type SignedPostCommitAction, type SignedWithdrawFunds, Signer, type SimplePostCommitArgs, UnsupportedSchemeError, UnsupportedTokenAuthError, type Web3LibAdapterLike, type WithdrawEntitySelector, type X402bClient, X402bClientConfig, createX402bClient, parseChainId, parsePaymentResponse, pickAction, resolveFulfillment, signerFromEthersAdapter };
|
|
363
|
+
export { AllChannelsFailedError, type ChannelAttempt, type ChannelFailureReason, ExchangeSummary, type FulfillmentRequest, FulfillmentValidationError, MaxAmountExceededError, NoCompatibleActionError, NoCompatibleChannelError, Policy, type ResolveDisputeArgs, type ResolvedFulfillment, type SignActionArgs, type SignWithdrawAllAvailableFundsArgs, type SignWithdrawFundsArgs, type SignedPostCommitAction, type SignedWithdrawFunds, Signer, type SimplePostCommitArgs, type SubmitActionArgs, type SubmitArgs, type SubmitChannel, type SubmitResult, UnsupportedSchemeError, UnsupportedTokenAuthError, type Web3LibAdapterLike, type WithdrawEntitySelector, type X402bClient, X402bClientConfig, createX402bClient, decodeBase64, encodeBase64, parseChainId, parsePaymentResponse, pickAction, resolveFulfillment, signerFromEthersAdapter, submitAction };
|
package/dist/cjs/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var Ajv = require('ajv');
|
|
|
4
4
|
var coreSdk = require('@bosonprotocol/core-sdk');
|
|
5
5
|
var escrow = require('@bosonprotocol/x402-core/schemes/escrow');
|
|
6
6
|
var codec = require('@bosonprotocol/x402-evm/codec');
|
|
7
|
+
var stateMachine = require('@bosonprotocol/x402-core/state-machine');
|
|
7
8
|
var viem = require('viem');
|
|
8
9
|
var tokenAuth = require('@bosonprotocol/x402-core/eip712/token-auth');
|
|
9
10
|
|
|
@@ -72,6 +73,30 @@ function pickAction(requirements, policy) {
|
|
|
72
73
|
`no commit-time action ('${FLOW_A}' or '${FLOW_B}') with 'server' channel found in requirements.actions.next`
|
|
73
74
|
);
|
|
74
75
|
}
|
|
76
|
+
|
|
77
|
+
// src/base64.ts
|
|
78
|
+
function decodeBase64(value) {
|
|
79
|
+
if (typeof Buffer !== "undefined") {
|
|
80
|
+
return Buffer.from(value, "base64").toString("utf8");
|
|
81
|
+
}
|
|
82
|
+
const binary = atob(value);
|
|
83
|
+
const bytes = new Uint8Array(binary.length);
|
|
84
|
+
for (let i = 0; i < binary.length; i++) {
|
|
85
|
+
bytes[i] = binary.charCodeAt(i);
|
|
86
|
+
}
|
|
87
|
+
return new TextDecoder().decode(bytes);
|
|
88
|
+
}
|
|
89
|
+
function encodeBase64(value) {
|
|
90
|
+
if (typeof Buffer !== "undefined") {
|
|
91
|
+
return Buffer.from(value, "utf8").toString("base64");
|
|
92
|
+
}
|
|
93
|
+
const bytes = new TextEncoder().encode(value);
|
|
94
|
+
let binary = "";
|
|
95
|
+
for (const b of bytes) {
|
|
96
|
+
binary += String.fromCharCode(b);
|
|
97
|
+
}
|
|
98
|
+
return btoa(binary);
|
|
99
|
+
}
|
|
75
100
|
function resolveFulfillment(requirements, config) {
|
|
76
101
|
const required = requirements.fulfillment?.required ?? false;
|
|
77
102
|
if (!required) {
|
|
@@ -244,17 +269,6 @@ function isClientStateShape(v) {
|
|
|
244
269
|
}
|
|
245
270
|
return true;
|
|
246
271
|
}
|
|
247
|
-
function decodeBase64(value) {
|
|
248
|
-
if (typeof Buffer !== "undefined") {
|
|
249
|
-
return Buffer.from(value, "base64").toString("utf8");
|
|
250
|
-
}
|
|
251
|
-
const binary = atob(value);
|
|
252
|
-
const bytes = new Uint8Array(binary.length);
|
|
253
|
-
for (let i = 0; i < binary.length; i++) {
|
|
254
|
-
bytes[i] = binary.charCodeAt(i);
|
|
255
|
-
}
|
|
256
|
-
return new TextDecoder().decode(bytes);
|
|
257
|
-
}
|
|
258
272
|
function pickString(obj, keys) {
|
|
259
273
|
for (const key of keys) {
|
|
260
274
|
const v = obj[key];
|
|
@@ -297,16 +311,7 @@ function assemblePayload({
|
|
|
297
311
|
}
|
|
298
312
|
function assembleAndEncodePayload(args) {
|
|
299
313
|
const payload = assemblePayload(args);
|
|
300
|
-
|
|
301
|
-
if (typeof Buffer !== "undefined") {
|
|
302
|
-
return Buffer.from(json, "utf8").toString("base64");
|
|
303
|
-
}
|
|
304
|
-
const bytes = new TextEncoder().encode(json);
|
|
305
|
-
let binary = "";
|
|
306
|
-
for (const b of bytes) {
|
|
307
|
-
binary += String.fromCharCode(b);
|
|
308
|
-
}
|
|
309
|
-
return btoa(binary);
|
|
314
|
+
return encodeBase64(JSON.stringify(payload));
|
|
310
315
|
}
|
|
311
316
|
|
|
312
317
|
// src/utils/crypto.ts
|
|
@@ -425,6 +430,235 @@ async function callSignMetaTx(coreSdk, args, nonce) {
|
|
|
425
430
|
}
|
|
426
431
|
}
|
|
427
432
|
}
|
|
433
|
+
var SUBMIT_CHANNELS = ["server", "facilitator"];
|
|
434
|
+
var NoCompatibleChannelError = class extends Error {
|
|
435
|
+
constructor(actionId, advertisedChannels) {
|
|
436
|
+
super(
|
|
437
|
+
`x402-client: action '${actionId}' advertises channels [${advertisedChannels.join(", ")}] but none are submittable over HTTP (server / facilitator).`
|
|
438
|
+
);
|
|
439
|
+
this.name = "NoCompatibleChannelError";
|
|
440
|
+
this.actionId = actionId;
|
|
441
|
+
this.advertisedChannels = advertisedChannels;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
var AllChannelsFailedError = class extends Error {
|
|
445
|
+
constructor(actionId, attempts) {
|
|
446
|
+
super(
|
|
447
|
+
`x402-client: action '${actionId}' failed on every attempted channel \u2014 ${attempts.map((a) => formatAttempt(a)).join("; ")}`
|
|
448
|
+
);
|
|
449
|
+
this.name = "AllChannelsFailedError";
|
|
450
|
+
this.attempts = attempts;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
function formatAttempt(a) {
|
|
454
|
+
if (a.ok) return `${a.channel}=ok(${a.status})`;
|
|
455
|
+
const tail = a.status !== void 0 ? `(${a.status})` : "";
|
|
456
|
+
return `${a.channel}=${a.reason}${tail}`;
|
|
457
|
+
}
|
|
458
|
+
async function submitAction(args) {
|
|
459
|
+
const fetcher = args.fetch ?? globalThis.fetch.bind(globalThis);
|
|
460
|
+
const timeoutMs = args.timeoutMs ?? 1e4;
|
|
461
|
+
const ordered = orderedChannels(args.action);
|
|
462
|
+
if (ordered.length === 0) {
|
|
463
|
+
throw new NoCompatibleChannelError(args.action.id, args.action.channels);
|
|
464
|
+
}
|
|
465
|
+
const attempts = [];
|
|
466
|
+
for (const channel of ordered) {
|
|
467
|
+
const endpoint = args.action.endpoints?.[channel];
|
|
468
|
+
if (endpoint === void 0) {
|
|
469
|
+
attempts.push({
|
|
470
|
+
channel,
|
|
471
|
+
ok: false,
|
|
472
|
+
reason: "no-endpoint",
|
|
473
|
+
message: `no endpoint advertised for channel '${channel}'`
|
|
474
|
+
});
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const outcome = await attemptChannel({
|
|
478
|
+
channel,
|
|
479
|
+
endpoint,
|
|
480
|
+
args,
|
|
481
|
+
fetcher,
|
|
482
|
+
timeoutMs
|
|
483
|
+
});
|
|
484
|
+
attempts.push(outcome.attempt);
|
|
485
|
+
if (outcome.attempt.ok && outcome.result !== void 0) {
|
|
486
|
+
return { ...outcome.result, attempts };
|
|
487
|
+
}
|
|
488
|
+
if (!outcome.attempt.ok && outcome.attempt.reason === "4xx") {
|
|
489
|
+
throw new AllChannelsFailedError(args.action.id, attempts);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
throw new AllChannelsFailedError(args.action.id, attempts);
|
|
493
|
+
}
|
|
494
|
+
function orderedChannels(action) {
|
|
495
|
+
const seen = /* @__PURE__ */ new Set();
|
|
496
|
+
const out = [];
|
|
497
|
+
for (const c of action.channels) {
|
|
498
|
+
if (SUBMIT_CHANNELS.includes(c) && !seen.has(c)) {
|
|
499
|
+
seen.add(c);
|
|
500
|
+
out.push(c);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return out;
|
|
504
|
+
}
|
|
505
|
+
async function attemptChannel(input) {
|
|
506
|
+
const { channel, endpoint, args, fetcher, timeoutMs } = input;
|
|
507
|
+
const body = channel === "server" ? buildServerBody(args) : buildFacilitatorBody(args);
|
|
508
|
+
let res;
|
|
509
|
+
try {
|
|
510
|
+
res = await fetchWithTimeout(fetcher, endpoint, body, timeoutMs);
|
|
511
|
+
} catch (e) {
|
|
512
|
+
const reason = isTimeout(e) ? "timeout" : "network";
|
|
513
|
+
return {
|
|
514
|
+
attempt: {
|
|
515
|
+
channel,
|
|
516
|
+
ok: false,
|
|
517
|
+
reason,
|
|
518
|
+
message: e instanceof Error ? e.message : String(e)
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
const parsed = await res.json().catch(() => null);
|
|
523
|
+
if (res.status >= 500) {
|
|
524
|
+
return {
|
|
525
|
+
attempt: { channel, ok: false, reason: "5xx", status: res.status }
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
if (!res.ok) {
|
|
529
|
+
return {
|
|
530
|
+
attempt: { channel, ok: false, reason: "4xx", status: res.status }
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
534
|
+
return {
|
|
535
|
+
attempt: {
|
|
536
|
+
channel,
|
|
537
|
+
ok: false,
|
|
538
|
+
reason: "invalid-response",
|
|
539
|
+
status: res.status,
|
|
540
|
+
message: "response body is not a JSON object"
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
const result = channel === "server" ? parseServerResult(parsed) : parseFacilitatorResult(parsed);
|
|
546
|
+
return {
|
|
547
|
+
attempt: { channel, ok: true, status: res.status },
|
|
548
|
+
result: { ...result, channelUsed: channel }
|
|
549
|
+
};
|
|
550
|
+
} catch (e) {
|
|
551
|
+
return {
|
|
552
|
+
attempt: {
|
|
553
|
+
channel,
|
|
554
|
+
ok: false,
|
|
555
|
+
reason: "invalid-response",
|
|
556
|
+
status: res.status,
|
|
557
|
+
message: e instanceof Error ? e.message : String(e)
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function buildServerBody(args) {
|
|
563
|
+
const body = {
|
|
564
|
+
exchangeId: args.exchangeId,
|
|
565
|
+
signedPayload: args.signed.signedPayload
|
|
566
|
+
};
|
|
567
|
+
if (args.action.id === "boson-redeem" && args.fulfillment !== void 0) {
|
|
568
|
+
body.fulfillment = args.fulfillment;
|
|
569
|
+
}
|
|
570
|
+
return body;
|
|
571
|
+
}
|
|
572
|
+
function buildFacilitatorBody(args) {
|
|
573
|
+
return {
|
|
574
|
+
action: args.action.id,
|
|
575
|
+
exchangeId: args.exchangeId,
|
|
576
|
+
network: args.network,
|
|
577
|
+
escrowAddress: args.escrowAddress,
|
|
578
|
+
signedPayload: args.signed.signedPayload
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
async function fetchWithTimeout(fetcher, endpoint, body, timeoutMs) {
|
|
582
|
+
const controller = new AbortController();
|
|
583
|
+
const timer = setTimeout(() => controller.abort(new TimeoutError(timeoutMs)), timeoutMs);
|
|
584
|
+
try {
|
|
585
|
+
return await fetcher(endpoint, {
|
|
586
|
+
method: "POST",
|
|
587
|
+
headers: { "content-type": "application/json" },
|
|
588
|
+
body: JSON.stringify(body),
|
|
589
|
+
signal: controller.signal
|
|
590
|
+
});
|
|
591
|
+
} finally {
|
|
592
|
+
clearTimeout(timer);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
var TimeoutError = class extends Error {
|
|
596
|
+
constructor(timeoutMs) {
|
|
597
|
+
super(`x402-client/submit: channel attempt timed out after ${timeoutMs}ms`);
|
|
598
|
+
this.isTimeout = true;
|
|
599
|
+
this.name = "TimeoutError";
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
function isTimeout(e) {
|
|
603
|
+
if (e instanceof TimeoutError) return true;
|
|
604
|
+
if (typeof e !== "object" || e === null) return false;
|
|
605
|
+
const obj = e;
|
|
606
|
+
if (obj.isTimeout === true) return true;
|
|
607
|
+
if (obj.name === "AbortError" || obj.name === "TimeoutError") return true;
|
|
608
|
+
if (typeof obj.cause === "object" && obj.cause !== null) {
|
|
609
|
+
const cause = obj.cause;
|
|
610
|
+
if (cause.isTimeout === true) return true;
|
|
611
|
+
if (cause.name === "TimeoutError") return true;
|
|
612
|
+
}
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
function parseServerResult(body) {
|
|
616
|
+
if (typeof body !== "object" || body === null) {
|
|
617
|
+
throw new Error("server response body is not an object");
|
|
618
|
+
}
|
|
619
|
+
const raw = body;
|
|
620
|
+
const txHash = raw.txHash;
|
|
621
|
+
const exchangeState = raw.nextActions?.exchangeState;
|
|
622
|
+
if (!isHexHash(txHash) || !isExchangeState(exchangeState)) {
|
|
623
|
+
throw new Error("server response missing txHash or nextActions.exchangeState");
|
|
624
|
+
}
|
|
625
|
+
const out = {
|
|
626
|
+
txHash,
|
|
627
|
+
newExchangeState: exchangeState,
|
|
628
|
+
nextActions: raw.nextActions
|
|
629
|
+
};
|
|
630
|
+
if (isDisputeState(raw.nextActions?.disputeState)) {
|
|
631
|
+
out.newDisputeState = raw.nextActions.disputeState;
|
|
632
|
+
}
|
|
633
|
+
return out;
|
|
634
|
+
}
|
|
635
|
+
function parseFacilitatorResult(body) {
|
|
636
|
+
if (typeof body !== "object" || body === null) {
|
|
637
|
+
throw new Error("facilitator response body is not an object");
|
|
638
|
+
}
|
|
639
|
+
const raw = body;
|
|
640
|
+
if (raw.ok !== true || !isHexHash(raw.txHash) || !isExchangeState(raw.newExchangeState)) {
|
|
641
|
+
throw new Error("facilitator response missing ok/txHash/newExchangeState");
|
|
642
|
+
}
|
|
643
|
+
const out = {
|
|
644
|
+
txHash: raw.txHash,
|
|
645
|
+
newExchangeState: raw.newExchangeState
|
|
646
|
+
};
|
|
647
|
+
if (isDisputeState(raw.newDisputeState)) {
|
|
648
|
+
out.newDisputeState = raw.newDisputeState;
|
|
649
|
+
}
|
|
650
|
+
return out;
|
|
651
|
+
}
|
|
652
|
+
var TX_HASH_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
653
|
+
function isHexHash(v) {
|
|
654
|
+
return typeof v === "string" && TX_HASH_RE.test(v);
|
|
655
|
+
}
|
|
656
|
+
function isExchangeState(v) {
|
|
657
|
+
return typeof v === "string" && Object.values(stateMachine.ExchangeState).includes(v);
|
|
658
|
+
}
|
|
659
|
+
function isDisputeState(v) {
|
|
660
|
+
return typeof v === "string" && Object.values(stateMachine.DisputeState).includes(v);
|
|
661
|
+
}
|
|
428
662
|
function normalizeEntityId(value) {
|
|
429
663
|
if (typeof value === "bigint") {
|
|
430
664
|
if (value < 0n) throw new Error(`entityId must be non-negative, got ${value.toString()}`);
|
|
@@ -848,6 +1082,26 @@ function createX402bClient(config) {
|
|
|
848
1082
|
signAction(args) {
|
|
849
1083
|
return signPostCommitAction(args, { buildCoreSdk, getBuyerAddress });
|
|
850
1084
|
},
|
|
1085
|
+
async submitAction(args) {
|
|
1086
|
+
const signed = await signPostCommitAction(args, { buildCoreSdk, getBuyerAddress });
|
|
1087
|
+
const action = args.priorNextActions.next.find((entry) => entry.id === args.actionId);
|
|
1088
|
+
if (action === void 0) {
|
|
1089
|
+
throw new Error(
|
|
1090
|
+
`x402-client/submitAction: actionId '${args.actionId}' is not present in priorNextActions.next[]`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
const submitArgs = {
|
|
1094
|
+
action,
|
|
1095
|
+
signed,
|
|
1096
|
+
exchangeId: String(args.exchangeId),
|
|
1097
|
+
network: args.network,
|
|
1098
|
+
escrowAddress: args.escrowAddress,
|
|
1099
|
+
fulfillment: args.fulfillment,
|
|
1100
|
+
fetch: args.fetch,
|
|
1101
|
+
timeoutMs: args.timeoutMs
|
|
1102
|
+
};
|
|
1103
|
+
return submitAction(submitArgs);
|
|
1104
|
+
},
|
|
851
1105
|
signWithdrawFunds(args) {
|
|
852
1106
|
return signWithdrawFunds(args, { buildCoreSdk, getSignerAddress: getBuyerAddress });
|
|
853
1107
|
},
|
|
@@ -908,16 +1162,21 @@ function deriveEip712DomainType(domain) {
|
|
|
908
1162
|
return fields;
|
|
909
1163
|
}
|
|
910
1164
|
|
|
1165
|
+
exports.AllChannelsFailedError = AllChannelsFailedError;
|
|
911
1166
|
exports.FulfillmentValidationError = FulfillmentValidationError;
|
|
912
1167
|
exports.MaxAmountExceededError = MaxAmountExceededError;
|
|
913
1168
|
exports.NoCompatibleActionError = NoCompatibleActionError;
|
|
1169
|
+
exports.NoCompatibleChannelError = NoCompatibleChannelError;
|
|
914
1170
|
exports.UnsupportedSchemeError = UnsupportedSchemeError;
|
|
915
1171
|
exports.UnsupportedTokenAuthError = UnsupportedTokenAuthError;
|
|
916
1172
|
exports.createX402bClient = createX402bClient;
|
|
1173
|
+
exports.decodeBase64 = decodeBase64;
|
|
1174
|
+
exports.encodeBase64 = encodeBase64;
|
|
917
1175
|
exports.parseChainId = parseChainId;
|
|
918
1176
|
exports.parsePaymentResponse = parsePaymentResponse;
|
|
919
1177
|
exports.pickAction = pickAction;
|
|
920
1178
|
exports.resolveFulfillment = resolveFulfillment;
|
|
921
1179
|
exports.signerFromEthersAdapter = signerFromEthersAdapter;
|
|
1180
|
+
exports.submitAction = submitAction;
|
|
922
1181
|
//# sourceMappingURL=index.js.map
|
|
923
1182
|
//# sourceMappingURL=index.js.map
|