@atxp/client 0.9.0 → 0.9.2
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/atxpClient.d.ts.map +1 -1
- package/dist/atxpClient.js +2 -1
- package/dist/atxpClient.js.map +1 -1
- package/dist/atxpFetcher.d.ts +13 -5
- package/dist/atxpFetcher.d.ts.map +1 -1
- package/dist/atxpFetcher.js +67 -18
- package/dist/atxpFetcher.js.map +1 -1
- package/dist/errors.d.ts +117 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +152 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +210 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +143 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +204 -26
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +25 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/types.js +0 -24
- package/dist/types.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Account, Network, DestinationMaker, AuthorizationServerUrl, AccountId,
|
|
1
|
+
import { Currency, Account, Network, DestinationMaker, AuthorizationServerUrl, AccountId, OAuthDb, FetchLike, Logger, OAuthResourceClient, ClientCredentials, PKCEValues, AccessToken, PaymentRequestOption, Source, Destination } from '@atxp/common';
|
|
2
2
|
export { ATXPAccount, Account, PaymentMaker } from '@atxp/common';
|
|
3
3
|
import { ClientOptions, Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
4
4
|
import { Implementation } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -6,6 +6,122 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
|
|
|
6
6
|
import * as oauth from 'oauth4webapi';
|
|
7
7
|
import { LocalAccount, Address, TypedData, Hex as Hex$1, SignableMessage, TransactionSerializable } from 'viem';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Base class for all ATXP payment errors with structured error codes and actionable guidance
|
|
11
|
+
*/
|
|
12
|
+
declare abstract class ATXPPaymentError extends Error {
|
|
13
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
14
|
+
abstract readonly code: string;
|
|
15
|
+
abstract readonly retryable: boolean;
|
|
16
|
+
abstract readonly actionableMessage: string;
|
|
17
|
+
constructor(message: string, context?: Record<string, unknown> | undefined);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Thrown when the user's wallet has insufficient funds for a payment
|
|
21
|
+
*/
|
|
22
|
+
declare class InsufficientFundsError extends ATXPPaymentError {
|
|
23
|
+
readonly currency: Currency;
|
|
24
|
+
readonly required: BigNumber;
|
|
25
|
+
readonly available?: BigNumber | undefined;
|
|
26
|
+
readonly network?: string | undefined;
|
|
27
|
+
readonly code = "INSUFFICIENT_FUNDS";
|
|
28
|
+
readonly retryable = true;
|
|
29
|
+
readonly actionableMessage: string;
|
|
30
|
+
constructor(currency: Currency, required: BigNumber, available?: BigNumber | undefined, network?: string | undefined);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Thrown when a blockchain transaction is reverted
|
|
34
|
+
*/
|
|
35
|
+
declare class TransactionRevertedError extends ATXPPaymentError {
|
|
36
|
+
readonly transactionHash: string;
|
|
37
|
+
readonly network: string;
|
|
38
|
+
readonly revertReason?: string | undefined;
|
|
39
|
+
readonly code = "TRANSACTION_REVERTED";
|
|
40
|
+
readonly retryable = false;
|
|
41
|
+
readonly actionableMessage: string;
|
|
42
|
+
constructor(transactionHash: string, network: string, revertReason?: string | undefined);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Thrown when an unsupported currency is requested
|
|
46
|
+
*/
|
|
47
|
+
declare class UnsupportedCurrencyError extends ATXPPaymentError {
|
|
48
|
+
readonly currency: string;
|
|
49
|
+
readonly network: string;
|
|
50
|
+
readonly supportedCurrencies: string[];
|
|
51
|
+
readonly code = "UNSUPPORTED_CURRENCY";
|
|
52
|
+
readonly retryable = false;
|
|
53
|
+
readonly actionableMessage: string;
|
|
54
|
+
constructor(currency: string, network: string, supportedCurrencies: string[]);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Thrown when gas estimation fails for a transaction
|
|
58
|
+
*/
|
|
59
|
+
declare class GasEstimationError extends ATXPPaymentError {
|
|
60
|
+
readonly network: string;
|
|
61
|
+
readonly reason?: string | undefined;
|
|
62
|
+
readonly code = "GAS_ESTIMATION_FAILED";
|
|
63
|
+
readonly retryable = true;
|
|
64
|
+
readonly actionableMessage = "Unable to estimate gas for this transaction. Ensure you have sufficient funds for both the payment amount and gas fees, then try again.";
|
|
65
|
+
constructor(network: string, reason?: string | undefined);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Thrown when RPC/network connectivity fails
|
|
69
|
+
*/
|
|
70
|
+
declare class RpcError extends ATXPPaymentError {
|
|
71
|
+
readonly network: string;
|
|
72
|
+
readonly rpcUrl?: string | undefined;
|
|
73
|
+
readonly originalError?: Error | undefined;
|
|
74
|
+
readonly code = "RPC_ERROR";
|
|
75
|
+
readonly retryable = true;
|
|
76
|
+
readonly actionableMessage = "Unable to connect to the blockchain network. Please check your internet connection and try again.";
|
|
77
|
+
constructor(network: string, rpcUrl?: string | undefined, originalError?: Error | undefined);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Thrown when the user rejects a transaction in their wallet
|
|
81
|
+
*/
|
|
82
|
+
declare class UserRejectedError extends ATXPPaymentError {
|
|
83
|
+
readonly network: string;
|
|
84
|
+
readonly code = "USER_REJECTED";
|
|
85
|
+
readonly retryable = true;
|
|
86
|
+
readonly actionableMessage = "You cancelled the transaction. To complete the payment, please approve the transaction in your wallet.";
|
|
87
|
+
constructor(network: string);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Thrown when the payment server returns an error
|
|
91
|
+
*/
|
|
92
|
+
declare class PaymentServerError extends ATXPPaymentError {
|
|
93
|
+
readonly statusCode: number;
|
|
94
|
+
readonly endpoint: string;
|
|
95
|
+
readonly serverMessage?: string | undefined;
|
|
96
|
+
readonly details?: unknown | undefined;
|
|
97
|
+
readonly code: string;
|
|
98
|
+
readonly retryable = true;
|
|
99
|
+
readonly actionableMessage = "The payment server encountered an error. Please try again in a few moments.";
|
|
100
|
+
constructor(statusCode: number, endpoint: string, serverMessage?: string | undefined, errorCode?: string, details?: unknown | undefined);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Thrown when a payment request has expired
|
|
104
|
+
*/
|
|
105
|
+
declare class PaymentExpiredError extends ATXPPaymentError {
|
|
106
|
+
readonly paymentRequestId: string;
|
|
107
|
+
readonly expiresAt?: Date | undefined;
|
|
108
|
+
readonly code = "PAYMENT_EXPIRED";
|
|
109
|
+
readonly retryable = false;
|
|
110
|
+
readonly actionableMessage = "This payment request has expired. Please make a new request to the service.";
|
|
111
|
+
constructor(paymentRequestId: string, expiresAt?: Date | undefined);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Generic network error for backward compatibility and uncategorized errors
|
|
115
|
+
*/
|
|
116
|
+
declare class PaymentNetworkError extends ATXPPaymentError {
|
|
117
|
+
readonly network: string;
|
|
118
|
+
readonly originalError?: Error | undefined;
|
|
119
|
+
readonly code = "NETWORK_ERROR";
|
|
120
|
+
readonly retryable = true;
|
|
121
|
+
readonly actionableMessage = "A network error occurred during payment processing. Please try again.";
|
|
122
|
+
constructor(network: string, message: string, originalError?: Error | undefined);
|
|
123
|
+
}
|
|
124
|
+
|
|
9
125
|
type Hex = `0x${string}`;
|
|
10
126
|
type AccountPrefix = Network;
|
|
11
127
|
type AccountIdString = `${AccountPrefix}${string}`;
|
|
@@ -17,6 +133,23 @@ type ProspectivePayment = {
|
|
|
17
133
|
amount: BigNumber;
|
|
18
134
|
iss: string;
|
|
19
135
|
};
|
|
136
|
+
/**
|
|
137
|
+
* Rich context provided when a payment fails
|
|
138
|
+
*/
|
|
139
|
+
interface PaymentFailureContext {
|
|
140
|
+
/** The payment that failed */
|
|
141
|
+
payment: ProspectivePayment;
|
|
142
|
+
/** The error that caused the failure */
|
|
143
|
+
error: Error;
|
|
144
|
+
/** Networks that were attempted for payment */
|
|
145
|
+
attemptedNetworks: string[];
|
|
146
|
+
/** Map of network to error for each failed attempt */
|
|
147
|
+
failureReasons: Map<string, Error>;
|
|
148
|
+
/** Whether the payment can be retried */
|
|
149
|
+
retryable: boolean;
|
|
150
|
+
/** Timestamp when the failure occurred */
|
|
151
|
+
timestamp: Date;
|
|
152
|
+
}
|
|
20
153
|
type ClientConfig = {
|
|
21
154
|
mcpServer: string;
|
|
22
155
|
account: Account;
|
|
@@ -42,10 +175,15 @@ type ClientConfig = {
|
|
|
42
175
|
}) => Promise<void>;
|
|
43
176
|
onPayment: (args: {
|
|
44
177
|
payment: ProspectivePayment;
|
|
178
|
+
transactionHash: string;
|
|
179
|
+
network: string;
|
|
45
180
|
}) => Promise<void>;
|
|
46
|
-
onPaymentFailure: (
|
|
47
|
-
|
|
181
|
+
onPaymentFailure: (context: PaymentFailureContext) => Promise<void>;
|
|
182
|
+
/** Optional callback when a single payment attempt fails (before trying other networks) */
|
|
183
|
+
onPaymentAttemptFailed?: (args: {
|
|
184
|
+
network: string;
|
|
48
185
|
error: Error;
|
|
186
|
+
remainingNetworks: string[];
|
|
49
187
|
}) => Promise<void>;
|
|
50
188
|
};
|
|
51
189
|
type RequiredClientConfigFields$1 = 'mcpServer' | 'account';
|
|
@@ -53,17 +191,6 @@ type RequiredClientConfig = Pick<ClientConfig, RequiredClientConfigFields$1>;
|
|
|
53
191
|
type OptionalClientConfig$1 = Omit<ClientConfig, RequiredClientConfigFields$1>;
|
|
54
192
|
type ClientArgs = RequiredClientConfig & Partial<OptionalClientConfig$1>;
|
|
55
193
|
type FetchWrapper = (config: ClientArgs) => FetchLike;
|
|
56
|
-
declare class InsufficientFundsError extends Error {
|
|
57
|
-
readonly currency: Currency;
|
|
58
|
-
readonly required: BigNumber;
|
|
59
|
-
readonly available?: BigNumber | undefined;
|
|
60
|
-
readonly network?: string | undefined;
|
|
61
|
-
constructor(currency: Currency, required: BigNumber, available?: BigNumber | undefined, network?: string | undefined);
|
|
62
|
-
}
|
|
63
|
-
declare class PaymentNetworkError extends Error {
|
|
64
|
-
readonly originalError?: Error | undefined;
|
|
65
|
-
constructor(message: string, originalError?: Error | undefined);
|
|
66
|
-
}
|
|
67
194
|
|
|
68
195
|
type RequiredClientConfigFields = 'mcpServer' | 'account';
|
|
69
196
|
type OptionalClientConfig = Omit<ClientConfig, RequiredClientConfigFields>;
|
|
@@ -296,5 +423,5 @@ declare class PassthroughDestinationMaker implements DestinationMaker {
|
|
|
296
423
|
makeDestinations(option: PaymentRequestOption, _logger: Logger, _paymentRequestId: string, _sources: Source[]): Promise<Destination[]>;
|
|
297
424
|
}
|
|
298
425
|
|
|
299
|
-
export { ATXPDestinationMaker, ATXPLocalAccount, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, POLYGON_AMOY, POLYGON_MAINNET, PassthroughDestinationMaker, PaymentNetworkError, USDC_CONTRACT_ADDRESS_POLYGON_AMOY, USDC_CONTRACT_ADDRESS_POLYGON_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_SEPOLIA, WORLD_CHAIN_MAINNET, WORLD_CHAIN_SEPOLIA, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport, getPolygonAmoyWithRPC, getPolygonByChainId, getPolygonMainnetWithRPC, getPolygonUSDCAddress, getWorldChainByChainId, getWorldChainMainnetWithRPC, getWorldChainSepoliaWithRPC, getWorldChainUSDCAddress };
|
|
300
|
-
export type { AccountIdString, ClientArgs, ClientConfig, FetchWrapper, Hex, OAuthClientConfig, PolygonChain, ProspectivePayment, WorldChain };
|
|
426
|
+
export { ATXPDestinationMaker, ATXPLocalAccount, ATXPPaymentError, DEFAULT_CLIENT_CONFIG, GasEstimationError, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, POLYGON_AMOY, POLYGON_MAINNET, PassthroughDestinationMaker, PaymentExpiredError, PaymentNetworkError, PaymentServerError, RpcError, TransactionRevertedError, USDC_CONTRACT_ADDRESS_POLYGON_AMOY, USDC_CONTRACT_ADDRESS_POLYGON_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_SEPOLIA, UnsupportedCurrencyError, UserRejectedError, WORLD_CHAIN_MAINNET, WORLD_CHAIN_SEPOLIA, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport, getPolygonAmoyWithRPC, getPolygonByChainId, getPolygonMainnetWithRPC, getPolygonUSDCAddress, getWorldChainByChainId, getWorldChainMainnetWithRPC, getWorldChainSepoliaWithRPC, getWorldChainUSDCAddress };
|
|
427
|
+
export type { AccountIdString, ClientArgs, ClientConfig, FetchWrapper, Hex, OAuthClientConfig, PaymentFailureContext, PolygonChain, ProspectivePayment, WorldChain };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,UAAU,EACX,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,gCAAgC,EAChC,KAAK,iBAAiB,EACtB,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,SAAS,EACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,2BAA2B,EAC3B,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qCAAqC,EACrC,kCAAkC,EAClC,eAAe,EACf,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,GAAG,EACR,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,UAAU,EACX,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,gCAAgC,EAChC,KAAK,iBAAiB,EACtB,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,SAAS,EACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,2BAA2B,EAC3B,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qCAAqC,EACrC,kCAAkC,EAClC,eAAe,EACf,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,GAAG,EACR,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { crypto as crypto$1, OAuthResourceClient, ConsoleLogger, PAYMENT_REQUIRED_ERROR_CODE, isSSEResponse, parseMcpMessages, parsePaymentRequests, paymentRequiredError, DEFAULT_AUTHORIZATION_SERVER, getIsReactNative, createReactNativeSafeFetch, isEnumValue, ChainEnum, CurrencyEnum, NetworkEnum, assertNever, DEFAULT_ATXP_ACCOUNTS_SERVER, MemoryOAuthDb, ATXPAccount } from '@atxp/common';
|
|
1
|
+
import { crypto as crypto$1, OAuthResourceClient, ConsoleLogger, getErrorRecoveryHint, PAYMENT_REQUIRED_ERROR_CODE, isSSEResponse, parseMcpMessages, parsePaymentRequests, paymentRequiredError, DEFAULT_AUTHORIZATION_SERVER, getIsReactNative, createReactNativeSafeFetch, isEnumValue, ChainEnum, CurrencyEnum, NetworkEnum, assertNever, DEFAULT_ATXP_ACCOUNTS_SERVER, MemoryOAuthDb, ATXPAccount } from '@atxp/common';
|
|
2
2
|
export { ATXPAccount } from '@atxp/common';
|
|
3
3
|
import * as oauth from 'oauth4webapi';
|
|
4
4
|
import { BigNumber } from 'bignumber.js';
|
|
@@ -275,25 +275,153 @@ class OAuthClient extends OAuthResourceClient {
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Base class for all ATXP payment errors with structured error codes and actionable guidance
|
|
280
|
+
*/
|
|
281
|
+
class ATXPPaymentError extends Error {
|
|
282
|
+
constructor(message, context) {
|
|
283
|
+
super(message);
|
|
284
|
+
this.context = context;
|
|
285
|
+
this.name = this.constructor.name;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Thrown when the user's wallet has insufficient funds for a payment
|
|
290
|
+
*/
|
|
291
|
+
class InsufficientFundsError extends ATXPPaymentError {
|
|
279
292
|
constructor(currency, required, available, network) {
|
|
293
|
+
const shortfall = available ? required.minus(available).toString() : required.toString();
|
|
280
294
|
const availableText = available ? `, Available: ${available}` : '';
|
|
281
295
|
const networkText = network ? ` on ${network}` : '';
|
|
282
296
|
super(`Payment failed due to insufficient ${currency} funds${networkText}. ` +
|
|
283
297
|
`Required: ${required}${availableText}. ` +
|
|
284
|
-
`Please ensure your account has adequate balance before retrying
|
|
298
|
+
`Please ensure your account has adequate balance before retrying.`, { currency, required: required.toString(), available: available?.toString(), network, shortfall });
|
|
285
299
|
this.currency = currency;
|
|
286
300
|
this.required = required;
|
|
287
301
|
this.available = available;
|
|
288
302
|
this.network = network;
|
|
289
|
-
this.
|
|
303
|
+
this.code = 'INSUFFICIENT_FUNDS';
|
|
304
|
+
this.retryable = true;
|
|
305
|
+
this.actionableMessage = available
|
|
306
|
+
? `Add at least ${shortfall} ${currency} to your ${network} wallet and try again.`
|
|
307
|
+
: `Ensure your ${network} wallet has at least ${required} ${currency} and try again.`;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Thrown when a blockchain transaction is reverted
|
|
312
|
+
*/
|
|
313
|
+
class TransactionRevertedError extends ATXPPaymentError {
|
|
314
|
+
constructor(transactionHash, network, revertReason) {
|
|
315
|
+
super(`Transaction ${transactionHash} reverted on ${network}${revertReason ? `: ${revertReason}` : ''}`, { transactionHash, network, revertReason });
|
|
316
|
+
this.transactionHash = transactionHash;
|
|
317
|
+
this.network = network;
|
|
318
|
+
this.revertReason = revertReason;
|
|
319
|
+
this.code = 'TRANSACTION_REVERTED';
|
|
320
|
+
this.retryable = false;
|
|
321
|
+
// Provide specific guidance based on revert reason
|
|
322
|
+
if (revertReason?.toLowerCase().includes('allowance')) {
|
|
323
|
+
this.actionableMessage = 'Approve token spending before making the payment. You may need to increase the token allowance.';
|
|
324
|
+
}
|
|
325
|
+
else if (revertReason?.toLowerCase().includes('balance')) {
|
|
326
|
+
this.actionableMessage = 'Ensure your wallet has sufficient token balance and native token for gas fees.';
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
this.actionableMessage = 'The transaction was rejected by the blockchain. Check the transaction details on a block explorer and verify your wallet settings.';
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Thrown when an unsupported currency is requested
|
|
335
|
+
*/
|
|
336
|
+
class UnsupportedCurrencyError extends ATXPPaymentError {
|
|
337
|
+
constructor(currency, network, supportedCurrencies) {
|
|
338
|
+
super(`Currency ${currency} is not supported on ${network}`, { currency, network, supportedCurrencies });
|
|
339
|
+
this.currency = currency;
|
|
340
|
+
this.network = network;
|
|
341
|
+
this.supportedCurrencies = supportedCurrencies;
|
|
342
|
+
this.code = 'UNSUPPORTED_CURRENCY';
|
|
343
|
+
this.retryable = false;
|
|
344
|
+
this.actionableMessage = `Please use one of the supported currencies: ${supportedCurrencies.join(', ')}`;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Thrown when gas estimation fails for a transaction
|
|
349
|
+
*/
|
|
350
|
+
class GasEstimationError extends ATXPPaymentError {
|
|
351
|
+
constructor(network, reason) {
|
|
352
|
+
super(`Failed to estimate gas on ${network}${reason ? `: ${reason}` : ''}`, { network, reason });
|
|
353
|
+
this.network = network;
|
|
354
|
+
this.reason = reason;
|
|
355
|
+
this.code = 'GAS_ESTIMATION_FAILED';
|
|
356
|
+
this.retryable = true;
|
|
357
|
+
this.actionableMessage = 'Unable to estimate gas for this transaction. Ensure you have sufficient funds for both the payment amount and gas fees, then try again.';
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Thrown when RPC/network connectivity fails
|
|
362
|
+
*/
|
|
363
|
+
class RpcError extends ATXPPaymentError {
|
|
364
|
+
constructor(network, rpcUrl, originalError) {
|
|
365
|
+
super(`RPC call failed on ${network}${rpcUrl ? ` (${rpcUrl})` : ''}`, { network, rpcUrl, originalError: originalError?.message });
|
|
366
|
+
this.network = network;
|
|
367
|
+
this.rpcUrl = rpcUrl;
|
|
368
|
+
this.originalError = originalError;
|
|
369
|
+
this.code = 'RPC_ERROR';
|
|
370
|
+
this.retryable = true;
|
|
371
|
+
this.actionableMessage = 'Unable to connect to the blockchain network. Please check your internet connection and try again.';
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Thrown when the user rejects a transaction in their wallet
|
|
376
|
+
*/
|
|
377
|
+
class UserRejectedError extends ATXPPaymentError {
|
|
378
|
+
constructor(network) {
|
|
379
|
+
super(`User rejected transaction on ${network}`, { network });
|
|
380
|
+
this.network = network;
|
|
381
|
+
this.code = 'USER_REJECTED';
|
|
382
|
+
this.retryable = true;
|
|
383
|
+
this.actionableMessage = 'You cancelled the transaction. To complete the payment, please approve the transaction in your wallet.';
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Thrown when the payment server returns an error
|
|
388
|
+
*/
|
|
389
|
+
class PaymentServerError extends ATXPPaymentError {
|
|
390
|
+
constructor(statusCode, endpoint, serverMessage, errorCode, details) {
|
|
391
|
+
super(`Payment server returned ${statusCode} from ${endpoint}${serverMessage ? `: ${serverMessage}` : ''}`, { statusCode, endpoint, serverMessage, errorCode, details });
|
|
392
|
+
this.statusCode = statusCode;
|
|
393
|
+
this.endpoint = endpoint;
|
|
394
|
+
this.serverMessage = serverMessage;
|
|
395
|
+
this.details = details;
|
|
396
|
+
this.retryable = true;
|
|
397
|
+
this.actionableMessage = 'The payment server encountered an error. Please try again in a few moments.';
|
|
398
|
+
this.code = errorCode || 'PAYMENT_SERVER_ERROR';
|
|
290
399
|
}
|
|
291
400
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Thrown when a payment request has expired
|
|
403
|
+
*/
|
|
404
|
+
class PaymentExpiredError extends ATXPPaymentError {
|
|
405
|
+
constructor(paymentRequestId, expiresAt) {
|
|
406
|
+
super(`Payment request ${paymentRequestId} has expired`, { paymentRequestId, expiresAt: expiresAt?.toISOString() });
|
|
407
|
+
this.paymentRequestId = paymentRequestId;
|
|
408
|
+
this.expiresAt = expiresAt;
|
|
409
|
+
this.code = 'PAYMENT_EXPIRED';
|
|
410
|
+
this.retryable = false;
|
|
411
|
+
this.actionableMessage = 'This payment request has expired. Please make a new request to the service.';
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Generic network error for backward compatibility and uncategorized errors
|
|
416
|
+
*/
|
|
417
|
+
class PaymentNetworkError extends ATXPPaymentError {
|
|
418
|
+
constructor(network, message, originalError) {
|
|
419
|
+
super(`Payment failed on ${network} network: ${message}`, { network, originalError: originalError?.message });
|
|
420
|
+
this.network = network;
|
|
295
421
|
this.originalError = originalError;
|
|
296
|
-
this.
|
|
422
|
+
this.code = 'NETWORK_ERROR';
|
|
423
|
+
this.retryable = true;
|
|
424
|
+
this.actionableMessage = 'A network error occurred during payment processing. Please try again.';
|
|
297
425
|
}
|
|
298
426
|
}
|
|
299
427
|
|
|
@@ -318,27 +446,41 @@ function atxpFetch(config) {
|
|
|
318
446
|
onAuthorize: config.onAuthorize,
|
|
319
447
|
onAuthorizeFailure: config.onAuthorizeFailure,
|
|
320
448
|
onPayment: config.onPayment,
|
|
321
|
-
onPaymentFailure: config.onPaymentFailure
|
|
449
|
+
onPaymentFailure: config.onPaymentFailure,
|
|
450
|
+
onPaymentAttemptFailed: config.onPaymentAttemptFailed
|
|
322
451
|
});
|
|
323
452
|
return fetcher.fetch;
|
|
324
453
|
}
|
|
325
454
|
class ATXPFetcher {
|
|
326
455
|
constructor(config) {
|
|
327
|
-
this.defaultPaymentFailureHandler = async (
|
|
456
|
+
this.defaultPaymentFailureHandler = async (context) => {
|
|
457
|
+
const { payment, error, attemptedNetworks, retryable } = context;
|
|
458
|
+
const recoveryHint = getErrorRecoveryHint(error);
|
|
459
|
+
this.logger.info(`PAYMENT FAILED: ${recoveryHint.title}`);
|
|
460
|
+
this.logger.info(`Description: ${recoveryHint.description}`);
|
|
461
|
+
if (attemptedNetworks.length > 0) {
|
|
462
|
+
this.logger.info(`Attempted networks: ${attemptedNetworks.join(', ')}`);
|
|
463
|
+
}
|
|
464
|
+
this.logger.info(`Account: ${payment.accountId}`);
|
|
465
|
+
// Log actionable guidance
|
|
466
|
+
if (recoveryHint.actions.length > 0) {
|
|
467
|
+
this.logger.info(`What to do:`);
|
|
468
|
+
recoveryHint.actions.forEach((action, index) => {
|
|
469
|
+
this.logger.info(` ${index + 1}. ${action}`);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (retryable) {
|
|
473
|
+
this.logger.info(`This payment can be retried.`);
|
|
474
|
+
}
|
|
475
|
+
// Log additional context for specific error types
|
|
328
476
|
if (error instanceof InsufficientFundsError) {
|
|
329
|
-
const networkText = error.network ? ` on ${error.network}` : '';
|
|
330
|
-
this.logger.info(`PAYMENT FAILED: Insufficient ${error.currency} funds${networkText}`);
|
|
331
477
|
this.logger.info(`Required: ${error.required} ${error.currency}`);
|
|
332
478
|
if (error.available) {
|
|
333
479
|
this.logger.info(`Available: ${error.available} ${error.currency}`);
|
|
334
480
|
}
|
|
335
|
-
this.logger.info(`Account: ${payment.accountId}`);
|
|
336
481
|
}
|
|
337
|
-
else if (error instanceof
|
|
338
|
-
this.logger.
|
|
339
|
-
}
|
|
340
|
-
else {
|
|
341
|
-
this.logger.info(`PAYMENT FAILED: ${error.message}`);
|
|
482
|
+
else if (error instanceof ATXPPaymentError && error.context) {
|
|
483
|
+
this.logger.debug(`Error context: ${JSON.stringify(error.context)}`);
|
|
342
484
|
}
|
|
343
485
|
};
|
|
344
486
|
this.handleMultiDestinationPayment = async (paymentRequest, paymentRequestUrl, paymentRequestId) => {
|
|
@@ -383,9 +525,11 @@ class ATXPFetcher {
|
|
|
383
525
|
this.logger.info(`ATXP: payment request denied by callback function`);
|
|
384
526
|
return false;
|
|
385
527
|
}
|
|
386
|
-
// Try each payment maker in order
|
|
528
|
+
// Try each payment maker in order, tracking attempts
|
|
387
529
|
let lastPaymentError = null;
|
|
388
530
|
let paymentAttempted = false;
|
|
531
|
+
const attemptedNetworks = [];
|
|
532
|
+
const failureReasons = new Map();
|
|
389
533
|
for (const paymentMaker of this.account.paymentMakers) {
|
|
390
534
|
try {
|
|
391
535
|
// Pass all destinations to payment maker - it will filter and pick the one it can handle
|
|
@@ -397,7 +541,11 @@ class ATXPFetcher {
|
|
|
397
541
|
paymentAttempted = true;
|
|
398
542
|
// Payment was successful
|
|
399
543
|
this.logger.info(`ATXP: made payment of ${firstDest.amount.toString()} ${firstDest.currency} on ${result.chain}: ${result.transactionId}`);
|
|
400
|
-
await this.onPayment({
|
|
544
|
+
await this.onPayment({
|
|
545
|
+
payment: prospectivePayment,
|
|
546
|
+
transactionHash: result.transactionId,
|
|
547
|
+
network: result.chain
|
|
548
|
+
});
|
|
401
549
|
// Submit payment to the server
|
|
402
550
|
const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '', accountId: this.account.accountId });
|
|
403
551
|
const response = await this.sideChannelFetch(paymentRequestUrl.toString(), {
|
|
@@ -425,13 +573,41 @@ class ATXPFetcher {
|
|
|
425
573
|
const typedError = error;
|
|
426
574
|
paymentAttempted = true;
|
|
427
575
|
lastPaymentError = typedError;
|
|
428
|
-
|
|
429
|
-
|
|
576
|
+
// Extract network from error context if available
|
|
577
|
+
let network = 'unknown';
|
|
578
|
+
if (typedError instanceof ATXPPaymentError && typedError.context?.network) {
|
|
579
|
+
network = typeof typedError.context.network === 'string' ? typedError.context.network : 'unknown';
|
|
580
|
+
}
|
|
581
|
+
attemptedNetworks.push(network);
|
|
582
|
+
failureReasons.set(network, typedError);
|
|
583
|
+
this.logger.warn(`ATXP: payment maker failed on ${network}: ${typedError.message}`);
|
|
584
|
+
// Call optional per-attempt failure callback
|
|
585
|
+
if (this.onPaymentAttemptFailed) {
|
|
586
|
+
const remainingMakers = this.account.paymentMakers.length - (attemptedNetworks.length);
|
|
587
|
+
const remainingNetworks = remainingMakers > 0 ? ['next available'] : [];
|
|
588
|
+
await this.onPaymentAttemptFailed({
|
|
589
|
+
network,
|
|
590
|
+
error: typedError,
|
|
591
|
+
remainingNetworks
|
|
592
|
+
});
|
|
593
|
+
}
|
|
430
594
|
// Continue to next payment maker
|
|
431
595
|
}
|
|
432
596
|
}
|
|
433
|
-
// If payment was attempted but all failed,
|
|
597
|
+
// If payment was attempted but all failed, create full context and call onPaymentFailure
|
|
434
598
|
if (paymentAttempted && lastPaymentError) {
|
|
599
|
+
const isRetryable = lastPaymentError instanceof ATXPPaymentError
|
|
600
|
+
? lastPaymentError.retryable
|
|
601
|
+
: true; // Default to retryable for unknown errors
|
|
602
|
+
const failureContext = {
|
|
603
|
+
payment: prospectivePayment,
|
|
604
|
+
error: lastPaymentError,
|
|
605
|
+
attemptedNetworks,
|
|
606
|
+
failureReasons,
|
|
607
|
+
retryable: isRetryable,
|
|
608
|
+
timestamp: new Date()
|
|
609
|
+
};
|
|
610
|
+
await this.onPaymentFailure(failureContext);
|
|
435
611
|
throw lastPaymentError;
|
|
436
612
|
}
|
|
437
613
|
this.logger.info(`ATXP: no payment maker could handle these destinations`);
|
|
@@ -679,7 +855,7 @@ class ATXPFetcher {
|
|
|
679
855
|
throw error;
|
|
680
856
|
}
|
|
681
857
|
};
|
|
682
|
-
const { account, db, destinationMakers, fetchFn = fetch, sideChannelFetch = fetchFn, strict = true, allowInsecureRequests = process.env.NODE_ENV === 'development', allowedAuthorizationServers = [DEFAULT_AUTHORIZATION_SERVER], approvePayment = async () => true, logger = new ConsoleLogger(), onAuthorize = async () => { }, onAuthorizeFailure = async () => { }, onPayment = async () => { }, onPaymentFailure
|
|
858
|
+
const { account, db, destinationMakers, fetchFn = fetch, sideChannelFetch = fetchFn, strict = true, allowInsecureRequests = process.env.NODE_ENV === 'development', allowedAuthorizationServers = [DEFAULT_AUTHORIZATION_SERVER], approvePayment = async () => true, logger = new ConsoleLogger(), onAuthorize = async () => { }, onAuthorizeFailure = async () => { }, onPayment = async () => { }, onPaymentFailure, onPaymentAttemptFailed } = config;
|
|
683
859
|
// Use React Native safe fetch if in React Native environment
|
|
684
860
|
const safeFetchFn = getIsReactNative() ? createReactNativeSafeFetch(fetchFn) : fetchFn;
|
|
685
861
|
const safeSideChannelFetch = getIsReactNative() ? createReactNativeSafeFetch(sideChannelFetch) : sideChannelFetch;
|
|
@@ -708,6 +884,7 @@ class ATXPFetcher {
|
|
|
708
884
|
this.onAuthorizeFailure = onAuthorizeFailure;
|
|
709
885
|
this.onPayment = onPayment;
|
|
710
886
|
this.onPaymentFailure = onPaymentFailure || this.defaultPaymentFailureHandler;
|
|
887
|
+
this.onPaymentAttemptFailed = onPaymentAttemptFailed;
|
|
711
888
|
}
|
|
712
889
|
}
|
|
713
890
|
|
|
@@ -16004,7 +16181,8 @@ const DEFAULT_CLIENT_CONFIG = {
|
|
|
16004
16181
|
onAuthorize: async () => { },
|
|
16005
16182
|
onAuthorizeFailure: async () => { },
|
|
16006
16183
|
onPayment: async () => { },
|
|
16007
|
-
onPaymentFailure: async () => { }
|
|
16184
|
+
onPaymentFailure: async () => { },
|
|
16185
|
+
onPaymentAttemptFailed: async () => { }
|
|
16008
16186
|
};
|
|
16009
16187
|
function buildClientConfig(args) {
|
|
16010
16188
|
// Use fetchFn for oAuthChannelFetch if the latter isn't explicitly set
|
|
@@ -16312,5 +16490,5 @@ class ATXPLocalAccount {
|
|
|
16312
16490
|
}
|
|
16313
16491
|
}
|
|
16314
16492
|
|
|
16315
|
-
export { ATXPDestinationMaker, ATXPLocalAccount, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, POLYGON_AMOY, POLYGON_MAINNET, PassthroughDestinationMaker, PaymentNetworkError, USDC_CONTRACT_ADDRESS_POLYGON_AMOY, USDC_CONTRACT_ADDRESS_POLYGON_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_SEPOLIA, WORLD_CHAIN_MAINNET, WORLD_CHAIN_SEPOLIA, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport, getPolygonAmoyWithRPC, getPolygonByChainId, getPolygonMainnetWithRPC, getPolygonUSDCAddress, getWorldChainByChainId, getWorldChainMainnetWithRPC, getWorldChainSepoliaWithRPC, getWorldChainUSDCAddress };
|
|
16493
|
+
export { ATXPDestinationMaker, ATXPLocalAccount, ATXPPaymentError, DEFAULT_CLIENT_CONFIG, GasEstimationError, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, POLYGON_AMOY, POLYGON_MAINNET, PassthroughDestinationMaker, PaymentExpiredError, PaymentNetworkError, PaymentServerError, RpcError, TransactionRevertedError, USDC_CONTRACT_ADDRESS_POLYGON_AMOY, USDC_CONTRACT_ADDRESS_POLYGON_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_SEPOLIA, UnsupportedCurrencyError, UserRejectedError, WORLD_CHAIN_MAINNET, WORLD_CHAIN_SEPOLIA, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport, getPolygonAmoyWithRPC, getPolygonByChainId, getPolygonMainnetWithRPC, getPolygonUSDCAddress, getWorldChainByChainId, getWorldChainMainnetWithRPC, getWorldChainSepoliaWithRPC, getWorldChainUSDCAddress };
|
|
16316
16494
|
//# sourceMappingURL=index.js.map
|