@atumlabs/mppx-atum-escrow 0.1.1 → 0.3.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/CHANGELOG.md +50 -0
- package/README.md +212 -47
- package/dist/chunk-4YD6566T.js +371 -0
- package/dist/chunk-Z3AUNEU5.js +2645 -0
- package/dist/{chunk-2MWWLU75.js → chunk-ZFA5SSUP.js} +1691 -1653
- package/dist/client.d.ts +21 -16
- package/dist/client.js +4 -2
- package/dist/index.d.ts +19 -18
- package/dist/index.js +19 -3
- package/dist/internal-9tB7y-A7.d.ts +365 -0
- package/dist/server.d.ts +234 -17
- package/dist/server.js +18 -2
- package/package.json +7 -4
- package/dist/chunk-L62WG2VU.js +0 -131
- package/dist/chunk-W6D2D767.js +0 -1410
- package/dist/internal-CjcEyEsm.d.ts +0 -842
package/dist/server.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as zod_v4_core from 'zod/v4/core';
|
|
2
2
|
import * as z from 'zod/mini';
|
|
3
|
-
import {
|
|
4
|
-
export { A as AtumEscrowChallenge, a as AtumEscrowCredential, C as ChargeCredentialPayload, d as ChargeRequestSchema, e as CredentialPayloadSchema, I as INTENT, M as METHOD_NAME,
|
|
5
|
-
import { Receipt, Method } from 'mppx';
|
|
3
|
+
import { j as ChainDefaults, c as ChargeRequest } from './internal-9tB7y-A7.js';
|
|
4
|
+
export { A as AtumEscrowChallenge, a as AtumEscrowCredential, C as ChargeCredentialPayload, d as ChargeRequestSchema, e as CredentialPayloadSchema, I as INTENT, f as INTENT_ID_META_KEY, M as METHOD_NAME, h as atumEscrowChargeMethod } from './internal-9tB7y-A7.js';
|
|
5
|
+
import { Errors, Receipt, Method } from 'mppx';
|
|
6
|
+
import { PaymentRequest } from './generated/index.js';
|
|
7
|
+
export { PaymentRequest } from './generated/index.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* This file was automatically generated by json-schema-to-typescript.
|
|
@@ -51,6 +53,131 @@ interface FulfillmentConfirmation {
|
|
|
51
53
|
destination_block_number?: number;
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Settlement outcomes that are not a receipt.
|
|
58
|
+
*
|
|
59
|
+
* MPP models a completed payment and nothing else: a receipt's status is the literal
|
|
60
|
+
* `"success"`, so a payment that has not settled cannot be expressed as one and has to leave
|
|
61
|
+
* `verify()` as a throw. What matters is that the two non-success outcomes stay
|
|
62
|
+
* distinguishable, because they call for opposite responses:
|
|
63
|
+
*
|
|
64
|
+
* - {@link SettlementPendingError} — the payment is still settling. Retrying the SAME purchase
|
|
65
|
+
* is safe and is how the payer collects the result: the identifier it derives from resolves to
|
|
66
|
+
* this same payment, so the gateway returns the original rather than charging again.
|
|
67
|
+
* - {@link SettlementFailedError} — the payment reached a terminal failure. Retrying the same
|
|
68
|
+
* purchase resolves to the dead payment forever, so recovering means starting a NEW purchase
|
|
69
|
+
* under a new identifier.
|
|
70
|
+
*
|
|
71
|
+
* Both carry the gateway's payment id when one was issued, so a merchant can reconcile the
|
|
72
|
+
* attempt against the gateway without parsing a message.
|
|
73
|
+
*
|
|
74
|
+
* Both extend the framework's error types, so a merchant that does not distinguish them still
|
|
75
|
+
* gets the standard `402` + problem-details response.
|
|
76
|
+
*
|
|
77
|
+
* @packageDocumentation
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/** Fields shared by both settlement outcomes. */
|
|
81
|
+
interface SettlementErrorDetails {
|
|
82
|
+
/** The gateway's payment id, when the payment was accepted and given one. */
|
|
83
|
+
readonly paymentId: string | undefined;
|
|
84
|
+
}
|
|
85
|
+
/** True if the payment is still settling — a retry of the same purchase is safe. */
|
|
86
|
+
declare function isSettlementPending(err: unknown): err is SettlementPendingError;
|
|
87
|
+
/** True if the payment reached a terminal failure — the same purchase can never settle. */
|
|
88
|
+
declare function isSettlementFailed(err: unknown): err is SettlementFailedError;
|
|
89
|
+
/** True if the gateway refused the request outright — nothing was charged. */
|
|
90
|
+
declare function isPaymentRejected(err: unknown): err is PaymentRejectedError;
|
|
91
|
+
/**
|
|
92
|
+
* The payment was accepted but had not settled when the merchant's synchronous window closed.
|
|
93
|
+
*
|
|
94
|
+
* Not a failure: the payment is in flight. Re-attempting the same purchase is safe — it resolves to
|
|
95
|
+
* this payment and returns its result once settled.
|
|
96
|
+
*
|
|
97
|
+
* **What re-attempting means.** Run the purchase again: request a fresh challenge and sign it again,
|
|
98
|
+
* keeping the same intent id. The intent id is what makes the re-attempt resolve onto the payment
|
|
99
|
+
* already in flight rather than taking a second one, so it must not change — while everything
|
|
100
|
+
* time-bound about the authorization must, because the deadlines are absolute timestamps fixed when
|
|
101
|
+
* it was signed. A stored credential therefore cannot simply be presented a second time; once its
|
|
102
|
+
* quote window has closed it is refused, and on Solana the deposit authorization carries its own
|
|
103
|
+
* replay window that expires with it.
|
|
104
|
+
*
|
|
105
|
+
* A payment-enabled `fetch` does this for the caller, since each attempt fetches a new challenge.
|
|
106
|
+
* Code that drives the client directly has to rebuild.
|
|
107
|
+
*/
|
|
108
|
+
declare class SettlementPendingError extends Errors.PaymentActionRequiredError implements SettlementErrorDetails {
|
|
109
|
+
/** Discriminant; test it with {@link isSettlementPending}. */
|
|
110
|
+
readonly isSettlementPending: true;
|
|
111
|
+
readonly paymentId: string | undefined;
|
|
112
|
+
constructor(options?: {
|
|
113
|
+
paymentId?: string | undefined;
|
|
114
|
+
});
|
|
115
|
+
/**
|
|
116
|
+
* Adds `paymentId` as its own field, not just interpolated into `detail`'s prose. A merchant
|
|
117
|
+
* catching this in-process already has `.paymentId`; a real cross-network payer only ever
|
|
118
|
+
* sees this JSON body, and without a dedicated field would have to parse it out of a sentence
|
|
119
|
+
* to reconcile the attempt against the gateway. The return type is declared (not left to
|
|
120
|
+
* inference) so a TypeScript merchant that re-serializes through the base type still sees
|
|
121
|
+
* `paymentId` in intellisense.
|
|
122
|
+
*/
|
|
123
|
+
toProblemDetails(challengeId?: string): Errors.PaymentError.ProblemDetails & {
|
|
124
|
+
paymentId?: string;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The payment reached a terminal failure state and will not settle.
|
|
129
|
+
*
|
|
130
|
+
* A retry of the same purchase cannot recover it: that purchase's identifier now resolves to
|
|
131
|
+
* this failed payment. Recovering means a new purchase under a new identifier.
|
|
132
|
+
*/
|
|
133
|
+
declare class SettlementFailedError extends Errors.VerificationFailedError implements SettlementErrorDetails {
|
|
134
|
+
/** Discriminant; test it with {@link isSettlementFailed}. */
|
|
135
|
+
readonly isSettlementFailed: true;
|
|
136
|
+
readonly paymentId: string | undefined;
|
|
137
|
+
/** The terminal state the gateway reported. */
|
|
138
|
+
readonly state: string;
|
|
139
|
+
constructor(options: {
|
|
140
|
+
paymentId?: string | undefined;
|
|
141
|
+
state: string;
|
|
142
|
+
});
|
|
143
|
+
/**
|
|
144
|
+
* Adds `paymentId` and `state` as their own fields, not just interpolated into `detail`'s
|
|
145
|
+
* prose — see {@link SettlementPendingError.toProblemDetails} for why a payer needs this on
|
|
146
|
+
* the wire, not only a same-process merchant. Return type declared for the same reason: so a
|
|
147
|
+
* TypeScript merchant sees `paymentId`/`state` in intellisense, not only at runtime.
|
|
148
|
+
*/
|
|
149
|
+
toProblemDetails(challengeId?: string): Errors.PaymentError.ProblemDetails & {
|
|
150
|
+
paymentId?: string;
|
|
151
|
+
state: string;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The gateway refused the payment outright — it never entered settlement.
|
|
156
|
+
*
|
|
157
|
+
* Distinct from {@link SettlementFailedError}: nothing was charged and nothing is in flight, so
|
|
158
|
+
* the fault is in the request and fixing it makes the same purchase payable. The most common
|
|
159
|
+
* cause is reusing one purchase identifier for two different sets of terms, which the gateway
|
|
160
|
+
* rejects rather than silently resolving to the first payment.
|
|
161
|
+
*
|
|
162
|
+
* Thrown by a {@link ../server!PaymentSubmitter | PaymentSubmitter} — the submitter owns the
|
|
163
|
+
* gateway call, so it is the only place that can tell a refusal (the request was rejected) from
|
|
164
|
+
* a transport or server failure (the outcome is unknown, and reporting it as a refusal would
|
|
165
|
+
* invite a second payment). Classify on the response status rather than on the body, so an
|
|
166
|
+
* intermediary that rewrites the error payload cannot turn a refusal into an unknown outcome.
|
|
167
|
+
* Raising it preserves the gateway's error code through to the payer instead of flattening every
|
|
168
|
+
* cause into one generic verification failure.
|
|
169
|
+
*/
|
|
170
|
+
declare class PaymentRejectedError extends Errors.BadRequestError {
|
|
171
|
+
/** Discriminant; test it with {@link isPaymentRejected}. */
|
|
172
|
+
readonly isPaymentRejected: true;
|
|
173
|
+
/** The gateway's error code, for programmatic handling. */
|
|
174
|
+
readonly code: string;
|
|
175
|
+
constructor(options: {
|
|
176
|
+
code: string;
|
|
177
|
+
detail: string;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
54
181
|
/** One source chain/token the merchant accepts payment from. */
|
|
55
182
|
interface AtumEscrowSource {
|
|
56
183
|
/** CAIP-2 source chain id (e.g. `eip155:8453`, `tron:mainnet`, `solana:<genesis>`). */
|
|
@@ -69,8 +196,6 @@ interface AtumEscrowSource {
|
|
|
69
196
|
releaser: string;
|
|
70
197
|
/** Source-chain endpoint used to verify fulfillment. */
|
|
71
198
|
fulfillmentVerifierEndpoint: string;
|
|
72
|
-
/** Tron-only: the Permit2 contract the deposit is signed against. Defaults to the known address for the cluster when omitted. */
|
|
73
|
-
permit2?: string;
|
|
74
199
|
/** Solana-only: cluster id that domain-separates the deposit authorization. */
|
|
75
200
|
svmSignatureClusterId?: string;
|
|
76
201
|
/** Solana-only: signature domain version that domain-separates the deposit authorization. */
|
|
@@ -95,15 +220,37 @@ interface AtumEscrowCorridor {
|
|
|
95
220
|
/** Accepted source options; selected per charge by `(network, asset)`. */
|
|
96
221
|
sources: AtumEscrowSource[];
|
|
97
222
|
}
|
|
223
|
+
/** Lifecycle state the gateway reports for a payment. */
|
|
224
|
+
type PaymentSettlementStatus = "pending" | "completed" | "failed" | "cancelled";
|
|
225
|
+
/** What a {@link PaymentSubmitter} resolves with. */
|
|
226
|
+
interface PaymentSubmitResult {
|
|
227
|
+
/** The gateway's payment id. Present whenever the payment was accepted. */
|
|
228
|
+
payment_id?: string;
|
|
229
|
+
/**
|
|
230
|
+
* The payment's state. When absent, an unsettled payment is treated as still in flight —
|
|
231
|
+
* the conservative reading, since calling an in-flight payment failed would tell the payer to
|
|
232
|
+
* start a second one.
|
|
233
|
+
*/
|
|
234
|
+
status?: PaymentSettlementStatus;
|
|
235
|
+
/** The settlement confirmation, present once the payment has completed. */
|
|
236
|
+
fulfillment_confirmation?: FulfillmentConfirmation;
|
|
237
|
+
}
|
|
98
238
|
/**
|
|
99
239
|
* Submits a signed payment request to the Atum Payment Gateway and resolves with the
|
|
100
240
|
* settlement result. Satisfied by an adapter over `@atum-labs/payment-gateway-client`.
|
|
241
|
+
*
|
|
242
|
+
* Submit and return what the gateway said; do not poll for completion inside the submitter.
|
|
243
|
+
* A payment that outlives the gateway's synchronous window surfaces to the payer as
|
|
244
|
+
* {@link SettlementPendingError}, and the payer's retry of the same purchase is what collects the
|
|
245
|
+
* result — the gateway resolves that retry to the same payment. Polling inside the submitter
|
|
246
|
+
* instead holds the merchant's request open for the full settlement window and hides the pending
|
|
247
|
+
* state that makes the retry safe.
|
|
248
|
+
*
|
|
249
|
+
* Throw {@link PaymentRejectedError} when the gateway refuses the request, so the refusal keeps
|
|
250
|
+
* its code. Any other throw is treated as an unknown outcome.
|
|
101
251
|
*/
|
|
102
252
|
interface PaymentSubmitter {
|
|
103
|
-
submit(request: PaymentRequest): Promise<
|
|
104
|
-
payment_id?: string;
|
|
105
|
-
fulfillment_confirmation?: FulfillmentConfirmation;
|
|
106
|
-
}>;
|
|
253
|
+
submit(request: PaymentRequest): Promise<PaymentSubmitResult>;
|
|
107
254
|
}
|
|
108
255
|
/** Configuration for the merchant side of the method. */
|
|
109
256
|
interface AtumEscrowServerConfig {
|
|
@@ -151,7 +298,7 @@ declare function registerServer(config: AtumEscrowServerConfig): Method.Server<{
|
|
|
151
298
|
destination: z.ZodMiniObject<{
|
|
152
299
|
network: z.ZodMiniString<string>;
|
|
153
300
|
asset: z.ZodMiniString<string>;
|
|
154
|
-
|
|
301
|
+
address: z.ZodMiniString<string>;
|
|
155
302
|
}, zod_v4_core.$strip>;
|
|
156
303
|
fulfillmentAmount: z.ZodMiniString<string>;
|
|
157
304
|
escrow: z.ZodMiniString<string>;
|
|
@@ -161,9 +308,9 @@ declare function registerServer(config: AtumEscrowServerConfig): Method.Server<{
|
|
|
161
308
|
fulfillmentVerifierEndpoint: z.ZodMiniString<string>;
|
|
162
309
|
quoteDeadlineSeconds: z.ZodMiniNumber<number>;
|
|
163
310
|
fulfillmentDeadlineSeconds: z.ZodMiniNumber<number>;
|
|
164
|
-
permit2: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
165
311
|
svmSignatureClusterId: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
166
312
|
svmSignatureDomainVersion: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
313
|
+
issuedAt: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
167
314
|
}, zod_v4_core.$strip>;
|
|
168
315
|
}, zod_v4_core.$strip>;
|
|
169
316
|
readonly credential: {
|
|
@@ -182,21 +329,91 @@ declare function registerServer(config: AtumEscrowServerConfig): Method.Server<{
|
|
|
182
329
|
*/
|
|
183
330
|
declare function validateCorridor(corridor: AtumEscrowCorridor): void;
|
|
184
331
|
/**
|
|
185
|
-
* Build the `charge` challenge request for a specific source option from a corridor
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
332
|
+
* Build the `charge` challenge request for a specific source option from a corridor — the
|
|
333
|
+
* payment terms alone. The source cap is `fulfillmentAmount + markup`, quoted in the source
|
|
334
|
+
* token. The corridor is validated first, so a misconfiguration fails here rather than at
|
|
335
|
+
* signing time.
|
|
336
|
+
*
|
|
337
|
+
* Prefer {@link buildChargeChallenge}, which pairs these terms with the per-purchase identifier
|
|
338
|
+
* the payer requires. A challenge built from this function alone carries no such identifier and
|
|
339
|
+
* the payer's client refuses it — at signing time, not at build time, so code that calls this
|
|
340
|
+
* directly and worked before only surfaces the problem when a real payer's client refuses the
|
|
341
|
+
* payment. Reach for it directly only when supplying the per-purchase metadata by hand.
|
|
189
342
|
*
|
|
190
343
|
* @param corridor - the corridor to serve.
|
|
191
344
|
* @param select - which configured source to fund from, by `(network, asset)`.
|
|
192
345
|
* @param fulfillmentAmount - exact amount the merchant receives, atomic units of the
|
|
193
346
|
* destination token. Set per charge so one registration serves any price.
|
|
347
|
+
* @param options.issuedAt - Solana-source only: the merchant's cluster-clock reading (epoch
|
|
348
|
+
* seconds) to stamp into `extra.issuedAt`, so the payer's client builds the deposit fully
|
|
349
|
+
* offline (no RPC). Omit to let the client stamp `issued_at` from its own clock at signing
|
|
350
|
+
* time — which keeps the full replay window; a merchant-stamped value anchors the window
|
|
351
|
+
* earlier (at 402-build time), so only set it when the client genuinely cannot read a clock.
|
|
352
|
+
* Ignored for EVM/Tron sources.
|
|
194
353
|
* @throws if no source option matches `select`, or the corridor is invalid.
|
|
195
354
|
*/
|
|
196
355
|
declare function buildChargeRequest(corridor: AtumEscrowCorridor, select: {
|
|
197
356
|
network: string;
|
|
198
357
|
asset: string;
|
|
199
|
-
}, fulfillmentAmount: string
|
|
358
|
+
}, fulfillmentAmount: string, options?: {
|
|
359
|
+
issuedAt?: string;
|
|
360
|
+
}): ChargeRequest;
|
|
361
|
+
/**
|
|
362
|
+
* A charge challenge's two halves, ready to hand to the framework.
|
|
363
|
+
*
|
|
364
|
+
* They are separate because the framework keeps them separate: `request` is the
|
|
365
|
+
* method-specific payment terms, `meta` is challenge metadata. Both are covered by the
|
|
366
|
+
* challenge-id HMAC, but nesting the metadata inside the request would bury the purchase
|
|
367
|
+
* identifier in the terms blob where the payer does not look for it — so this type keeps them in
|
|
368
|
+
* the shape the framework expects.
|
|
369
|
+
*/
|
|
370
|
+
interface ChargeChallenge {
|
|
371
|
+
/** The method-specific charge request (the payment terms). */
|
|
372
|
+
request: ChargeRequest;
|
|
373
|
+
/** Challenge metadata carrying the per-intent identifier. */
|
|
374
|
+
meta: Record<string, string>;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Build a charge challenge for a specific source option from a corridor.
|
|
378
|
+
*
|
|
379
|
+
* This is the entry point a merchant should use: it pairs the payment terms with the per-purchase
|
|
380
|
+
* identifier the payer needs to make the payment retry-safe. A challenge built without that
|
|
381
|
+
* identifier is refused by the payer's client rather than paid unsafely.
|
|
382
|
+
*
|
|
383
|
+
* Pass the result straight through to the framework:
|
|
384
|
+
*
|
|
385
|
+
* ```ts
|
|
386
|
+
* const { request, meta } = buildChargeChallenge(corridor, source, amount, { intentId: order.id })
|
|
387
|
+
*
|
|
388
|
+
* // Route-handler style:
|
|
389
|
+
* mppx.compose(['atum-escrow/charge', { ...request, meta }])(input)
|
|
390
|
+
*
|
|
391
|
+
* // Or building the challenge directly:
|
|
392
|
+
* Challenge.fromMethod(atumEscrowChargeMethod, { request, meta, realm, secretKey })
|
|
393
|
+
* ```
|
|
394
|
+
*
|
|
395
|
+
* @param corridor - the corridor to serve.
|
|
396
|
+
* @param select - which configured source to fund from, by `(network, asset)`.
|
|
397
|
+
* @param fulfillmentAmount - exact amount the merchant receives, atomic units of the
|
|
398
|
+
* destination token.
|
|
399
|
+
* @param options.intentId - identifies the thing being bought. Use the merchant's own order,
|
|
400
|
+
* invoice, or cart identifier: one value per purchase, the SAME value every time that purchase
|
|
401
|
+
* is re-offered or retried, and never shared between two purchases. The payer derives the
|
|
402
|
+
* payment's identity from it and the gateway de-duplicates on that, so its lifetime is what
|
|
403
|
+
* decides whether a retry is recognized (a value that changes per attempt causes a second
|
|
404
|
+
* charge) and whether two purchases stay distinct (a value shared between them leaves the
|
|
405
|
+
* second unpaid). Do not derive it from the terms: two orders for the same item have identical
|
|
406
|
+
* terms.
|
|
407
|
+
* @param options.issuedAt - Solana-source only; see {@link buildChargeRequest}.
|
|
408
|
+
* @throws if `intentId` is empty, or the corridor/source selection is invalid.
|
|
409
|
+
*/
|
|
410
|
+
declare function buildChargeChallenge(corridor: AtumEscrowCorridor, select: {
|
|
411
|
+
network: string;
|
|
412
|
+
asset: string;
|
|
413
|
+
}, fulfillmentAmount: string, options: {
|
|
414
|
+
intentId: string;
|
|
415
|
+
issuedAt?: string;
|
|
416
|
+
}): ChargeChallenge;
|
|
200
417
|
/** The minimal defaults source `corridorFromDefaults` needs; satisfied by the gateway client. */
|
|
201
418
|
interface ChainDefaultsSource {
|
|
202
419
|
fetchChainDefaults(chainId: string): Promise<ChainDefaults>;
|
|
@@ -227,4 +444,4 @@ declare function corridorFromDefaults(defaults: ChainDefaultsSource, params: {
|
|
|
227
444
|
fulfillmentDeadlineSeconds: number;
|
|
228
445
|
}): Promise<AtumEscrowCorridor>;
|
|
229
446
|
|
|
230
|
-
export { type AtumEscrowCorridor, type AtumEscrowReceipt, type AtumEscrowServerConfig, type AtumEscrowSource, type ChainDefaultsSource, ChargeRequest, type FulfillmentConfirmation,
|
|
447
|
+
export { type AtumEscrowCorridor, type AtumEscrowReceipt, type AtumEscrowServerConfig, type AtumEscrowSource, type ChainDefaultsSource, type ChargeChallenge, ChargeRequest, type FulfillmentConfirmation, PaymentRejectedError, type PaymentSettlementStatus, type PaymentSubmitResult, type PaymentSubmitter, type SettlementErrorDetails, SettlementFailedError, SettlementPendingError, buildChargeChallenge, buildChargeRequest, corridorFromDefaults, isPaymentRejected, isSettlementFailed, isSettlementPending, registerServer, validateCorridor };
|
package/dist/server.js
CHANGED
|
@@ -6,26 +6,42 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createRequire as __atumCreateRequire } from 'module'; const require = __atumCreateRequire(import.meta.url);
|
|
8
8
|
import {
|
|
9
|
+
PaymentRejectedError,
|
|
10
|
+
SettlementFailedError,
|
|
11
|
+
SettlementPendingError,
|
|
12
|
+
buildChargeChallenge,
|
|
9
13
|
buildChargeRequest,
|
|
10
14
|
corridorFromDefaults,
|
|
15
|
+
isPaymentRejected,
|
|
16
|
+
isSettlementFailed,
|
|
17
|
+
isSettlementPending,
|
|
11
18
|
registerServer,
|
|
12
19
|
validateCorridor
|
|
13
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-4YD6566T.js";
|
|
14
21
|
import {
|
|
15
22
|
ChargeRequestSchema,
|
|
16
23
|
CredentialPayloadSchema,
|
|
17
24
|
INTENT,
|
|
25
|
+
INTENT_ID_META_KEY,
|
|
18
26
|
METHOD_NAME,
|
|
19
27
|
atumEscrowChargeMethod
|
|
20
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-Z3AUNEU5.js";
|
|
21
29
|
export {
|
|
22
30
|
ChargeRequestSchema,
|
|
23
31
|
CredentialPayloadSchema,
|
|
24
32
|
INTENT,
|
|
33
|
+
INTENT_ID_META_KEY,
|
|
25
34
|
METHOD_NAME,
|
|
35
|
+
PaymentRejectedError,
|
|
36
|
+
SettlementFailedError,
|
|
37
|
+
SettlementPendingError,
|
|
26
38
|
atumEscrowChargeMethod,
|
|
39
|
+
buildChargeChallenge,
|
|
27
40
|
buildChargeRequest,
|
|
28
41
|
corridorFromDefaults,
|
|
42
|
+
isPaymentRejected,
|
|
43
|
+
isSettlementFailed,
|
|
44
|
+
isSettlementPending,
|
|
29
45
|
registerServer,
|
|
30
46
|
validateCorridor
|
|
31
47
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atumlabs/mppx-atum-escrow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Atum cross-chain escrow payment method for the Machine Payments Protocol (MPP).",
|
|
5
5
|
"author": "Atum Labs, Inc.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -49,17 +49,18 @@
|
|
|
49
49
|
"files": [
|
|
50
50
|
"dist/**/*",
|
|
51
51
|
"README.md",
|
|
52
|
+
"CHANGELOG.md",
|
|
52
53
|
"LICENSE",
|
|
53
54
|
"THIRD-PARTY-NOTICES.txt"
|
|
54
55
|
],
|
|
55
56
|
"scripts": {
|
|
56
|
-
"prebuild:deps": "node -e \"const {existsSync}=require('fs');for(const p of ['../contracts/evm/escrow-encoding/typescript/tsconfig.json','../contracts/tvm/escrow-encoding/typescript/tsconfig.json','../contracts/svm/escrow-encoding/typescript/tsconfig.json','../schemas/apis/
|
|
57
|
-
"build:deps": "tsc -p ../contracts/evm/escrow-encoding/typescript/tsconfig.json && tsc -p ../contracts/tvm/escrow-encoding/typescript/tsconfig.json && tsc -p ../contracts/svm/escrow-encoding/typescript/tsconfig.json && tsc -p ../schemas/apis/
|
|
57
|
+
"prebuild:deps": "node -e \"const {existsSync}=require('fs');for(const p of ['../contracts/evm/escrow-encoding/typescript/tsconfig.json','../contracts/tvm/escrow-encoding/typescript/tsconfig.json','../contracts/svm/escrow-encoding/typescript/tsconfig.json','../schemas/apis/x402/v1/bindings/typescript/tsconfig.json','../schemas/apis/fulfillment-confirmation/v1/bindings/typescript/tsconfig.json','../schemas/declarations/PaymentRequest/v1/bindings/typescript/sender-auth/tsconfig.json']){if(!existsSync(p)){console.error('bundled sibling not found at '+p+'. Is this checked out inside the protocol monorepo?');process.exit(1)}}if(!existsSync('../payment-gateway-client/dist/index.js')){console.error('@atum-labs/payment-gateway-client is not built. Run its build first (pnpm --filter @atum-labs/payment-gateway-client build).');process.exit(1)}\"",
|
|
58
|
+
"build:deps": "tsc -p ../contracts/evm/escrow-encoding/typescript/tsconfig.json && tsc -p ../contracts/tvm/escrow-encoding/typescript/tsconfig.json && tsc -p ../contracts/svm/escrow-encoding/typescript/tsconfig.json && tsc -p ../schemas/apis/x402/v1/bindings/typescript/tsconfig.json && tsc -p ../schemas/apis/fulfillment-confirmation/v1/bindings/typescript/tsconfig.json && tsc -p ../schemas/declarations/PaymentRequest/v1/bindings/typescript/sender-auth/tsconfig.json",
|
|
58
59
|
"build": "pnpm run build:deps && tsup && node scripts/stamp-entry-headers.mjs",
|
|
59
60
|
"typecheck": "pnpm run build:deps && tsc --noEmit",
|
|
60
61
|
"pretest": "pnpm run build:deps",
|
|
61
62
|
"test": "vitest run",
|
|
62
|
-
"clean": "rm -rf dist ../contracts/evm/escrow-encoding/typescript/dist ../contracts/tvm/escrow-encoding/typescript/dist ../contracts/svm/escrow-encoding/typescript/dist ../schemas/apis/
|
|
63
|
+
"clean": "rm -rf dist ../contracts/evm/escrow-encoding/typescript/dist ../contracts/tvm/escrow-encoding/typescript/dist ../contracts/svm/escrow-encoding/typescript/dist ../schemas/apis/x402/v1/bindings/typescript/dist ../schemas/apis/fulfillment-confirmation/v1/bindings/typescript/dist ../schemas/declarations/PaymentRequest/v1/bindings/typescript/sender-auth/dist",
|
|
63
64
|
"check:publishable": "node scripts/check-publishable.mjs",
|
|
64
65
|
"gen:notices": "node scripts/gen-third-party-notices.mjs",
|
|
65
66
|
"prepare": "pnpm run build",
|
|
@@ -80,6 +81,8 @@
|
|
|
80
81
|
"@atum-labs/evm-escrow-encoding": "workspace:*",
|
|
81
82
|
"@atum-labs/fulfillment-confirmation": "workspace:*",
|
|
82
83
|
"@atum-labs/payment-gateway-client": "workspace:*",
|
|
84
|
+
"@atum-labs/payment-request-sender-auth": "workspace:*",
|
|
85
|
+
"@atum-labs/publish-guard": "workspace:*",
|
|
83
86
|
"@atum-labs/schema-declarations": "workspace:*",
|
|
84
87
|
"@atum-labs/solana-escrow-encoding": "workspace:*",
|
|
85
88
|
"@atum-labs/tvm-escrow-encoding": "workspace:*",
|
package/dist/chunk-L62WG2VU.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { createRequire as __atumCreateRequire } from 'module'; const require = __atumCreateRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
__toESM,
|
|
4
|
-
assetIdentifier,
|
|
5
|
-
atumEscrowChargeMethod,
|
|
6
|
-
chainDefaultsFromExtra,
|
|
7
|
-
namespaceOf,
|
|
8
|
-
require_dist,
|
|
9
|
-
require_dist2
|
|
10
|
-
} from "./chunk-2MWWLU75.js";
|
|
11
|
-
|
|
12
|
-
// src/client.ts
|
|
13
|
-
var import_evm_escrow_encoding = __toESM(require_dist(), 1);
|
|
14
|
-
var import_payment_gateway_client = __toESM(require_dist2(), 1);
|
|
15
|
-
import { keccak256, toUtf8Bytes, Contract, MaxUint256 } from "ethers";
|
|
16
|
-
import { Credential, Method } from "mppx";
|
|
17
|
-
function registerClient(config) {
|
|
18
|
-
const now = config.now ?? (() => Date.now());
|
|
19
|
-
const gateway = new import_payment_gateway_client.PaymentGatewayClient();
|
|
20
|
-
return Method.toClient(atumEscrowChargeMethod, {
|
|
21
|
-
async createCredential({ challenge }) {
|
|
22
|
-
const { source, extra } = challenge.request;
|
|
23
|
-
const namespace = namespaceOf(source.network);
|
|
24
|
-
const account = config.account;
|
|
25
|
-
const nowMs = now();
|
|
26
|
-
if (!(extra.quoteDeadlineSeconds > 0 && extra.quoteDeadlineSeconds < extra.fulfillmentDeadlineSeconds)) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
`atum-escrow: deadline ordering violated (now < quote_deadline < fulfillment_deadline); quoteDeadlineSeconds=${extra.quoteDeadlineSeconds}, fulfillmentDeadlineSeconds=${extra.fulfillmentDeadlineSeconds}`
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
const anchorAccount = namespace === "eip155" ? account.toLowerCase() : account;
|
|
32
|
-
const idempotencyAnchor = keccak256(
|
|
33
|
-
toUtf8Bytes(`atum-escrow:${challenge.id}:${anchorAccount}`)
|
|
34
|
-
);
|
|
35
|
-
const requestId = `req_mpp_${idempotencyAnchor.slice(2, 26)}`;
|
|
36
|
-
const sourceDefaults = chainDefaultsFromExtra(extra, source.network);
|
|
37
|
-
const sourceAssetId = assetIdentifier(source.network, source.asset);
|
|
38
|
-
const isSolana = namespace === "solana";
|
|
39
|
-
const paymentRequestGw = await gateway.preparePaymentRequest({
|
|
40
|
-
depositor: account,
|
|
41
|
-
fulfillmentAmount: extra.fulfillmentAmount,
|
|
42
|
-
sourceAsset: sourceAssetId,
|
|
43
|
-
destinationAccount: extra.destination.account,
|
|
44
|
-
destinationAsset: assetIdentifier(extra.destination.network, extra.destination.asset),
|
|
45
|
-
// Sign the full advertised cap. The payer MAY sign a lower value, but that only
|
|
46
|
-
// lowers its own escrow lock and risks the auction not clearing; the full cap is
|
|
47
|
-
// the safe default and never affects the receive side.
|
|
48
|
-
maxSourceAmount: source.amount,
|
|
49
|
-
requestId,
|
|
50
|
-
quoteDeadlineSeconds: extra.quoteDeadlineSeconds,
|
|
51
|
-
fulfillmentDeadlineSeconds: extra.fulfillmentDeadlineSeconds,
|
|
52
|
-
now: () => nowMs,
|
|
53
|
-
resolvedDefaults: {
|
|
54
|
-
source: sourceDefaults,
|
|
55
|
-
destination: { fulfillmentProxy: extra.fulfillmentProxy }
|
|
56
|
-
},
|
|
57
|
-
// EVM/Tron: thread the deterministic nonce, and keep the deposit deadline at least
|
|
58
|
-
// as late as the fulfillment deadline. The adapter floors the relative deadline, so
|
|
59
|
-
// +1s guards against truncating below fulfillment_deadline.
|
|
60
|
-
...isSolana ? {
|
|
61
|
-
// Solana stamps issued_at from the (injectable) clock and derives its own
|
|
62
|
-
// replay-window deadline; the offline build reads the client clock.
|
|
63
|
-
solanaRpcUrl: "atum-escrow:offline",
|
|
64
|
-
solanaClockReader: config.solanaClockReader ?? (async () => BigInt(Math.floor(nowMs / 1e3)))
|
|
65
|
-
} : {
|
|
66
|
-
nonce: BigInt(idempotencyAnchor),
|
|
67
|
-
// Keep the deposit deadline at least as late as fulfillment. The adapter
|
|
68
|
-
// floors its relative deadline, so round the budget up and add a second to
|
|
69
|
-
// guard against truncating below fulfillment_deadline (and to keep an
|
|
70
|
-
// integer, since the adapter derives a bigint from it).
|
|
71
|
-
deadlineSeconds: Math.ceil(extra.fulfillmentDeadlineSeconds) + 1
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
const signer = buildSenderSigner(source.network, config.signer, account);
|
|
75
|
-
await (0, import_payment_gateway_client.signPaymentRequest)(paymentRequestGw, signer);
|
|
76
|
-
const paymentRequest = paymentRequestGw;
|
|
77
|
-
return Credential.serialize({
|
|
78
|
-
challenge,
|
|
79
|
-
payload: { paymentRequest },
|
|
80
|
-
source: `did:pkh:${source.network}:${anchorAccount}`
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
function isSenderSigner(signer) {
|
|
86
|
-
return typeof signer.sign === "function";
|
|
87
|
-
}
|
|
88
|
-
function buildSenderSigner(network, signer, account) {
|
|
89
|
-
if (isSenderSigner(signer)) {
|
|
90
|
-
return signer;
|
|
91
|
-
}
|
|
92
|
-
if (signer.provider === "turnkey") {
|
|
93
|
-
throw new Error(
|
|
94
|
-
"atum-escrow: construct a Turnkey SenderSigner with the payment-gateway client and pass it as `signer`"
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
return (0, import_payment_gateway_client.createSenderSigner)(network, {
|
|
98
|
-
provider: "raw",
|
|
99
|
-
privateKey: signer.privateKey,
|
|
100
|
-
pinnedAddress: signer.pinnedAddress ?? account
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
104
|
-
"function allowance(address owner, address spender) view returns (uint256)",
|
|
105
|
-
"function approve(address spender, uint256 amount) returns (bool)"
|
|
106
|
-
];
|
|
107
|
-
async function ensureSourceApproval(params) {
|
|
108
|
-
if (namespaceOf(params.network) === "solana") {
|
|
109
|
-
return { alreadySufficient: true };
|
|
110
|
-
}
|
|
111
|
-
const spender = params.spender ?? (namespaceOf(params.network) === "eip155" ? import_evm_escrow_encoding.PERMIT2_CONTRACT_ADDRESS : void 0);
|
|
112
|
-
if (!spender) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`atum-escrow: a spender (Permit2 address) is required to approve on ${params.network}`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
const erc20 = new Contract(params.token, ERC20_ALLOWANCE_ABI, params.signer);
|
|
118
|
-
const current = await erc20.allowance(params.owner, spender);
|
|
119
|
-
const sufficient = params.requiredAllowance !== void 0 ? current >= params.requiredAllowance : current >= MaxUint256;
|
|
120
|
-
if (sufficient) {
|
|
121
|
-
return { alreadySufficient: true };
|
|
122
|
-
}
|
|
123
|
-
const tx = await erc20.approve(spender, MaxUint256);
|
|
124
|
-
await tx.wait?.();
|
|
125
|
-
return { alreadySufficient: false, txHash: tx.hash };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export {
|
|
129
|
-
registerClient,
|
|
130
|
-
ensureSourceApproval
|
|
131
|
-
};
|