@atxp/client 0.5.1 → 0.6.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/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { Currency, AuthorizationServerUrl, Network, OAuthDb, FetchLike, Logger, OAuthResourceClient, ClientCredentials, PKCEValues, AccessToken, PaymentRequestData } from '@atxp/common';
1
+ import { Currency, AuthorizationServerUrl, Network, OAuthDb, FetchLike, Logger, OAuthResourceClient, ClientCredentials, PKCEValues, AccessToken } from '@atxp/common';
2
2
  import { ClientOptions, Client } from '@modelcontextprotocol/sdk/client/index.js';
3
- import { Implementation, McpError } from '@modelcontextprotocol/sdk/types.js';
3
+ import { Implementation } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5
5
  import * as oauth from 'oauth4webapi';
6
6
  import { ValidateTransferError as ValidateTransferError$1 } from '@solana/pay';
7
7
  import BigNumber$1 from 'bignumber.js';
8
- import { WalletClient, PublicActions } from 'viem';
8
+ import { WalletClient, PublicActions, LocalAccount, Address, TypedData, Hex as Hex$1, SignableMessage, TransactionSerializable } from 'viem';
9
9
 
10
10
  type Hex = `0x${string}`;
11
11
  type AccountPrefix = Network;
@@ -54,6 +54,11 @@ type ClientConfig = {
54
54
  error: Error;
55
55
  }) => Promise<void>;
56
56
  };
57
+ type RequiredClientConfigFields$1 = 'mcpServer' | 'account';
58
+ type RequiredClientConfig = Pick<ClientConfig, RequiredClientConfigFields$1>;
59
+ type OptionalClientConfig$1 = Omit<ClientConfig, RequiredClientConfigFields$1>;
60
+ type ClientArgs = RequiredClientConfig & Partial<OptionalClientConfig$1>;
61
+ type FetchWrapper = (config: ClientArgs) => FetchLike;
57
62
  declare class InsufficientFundsError extends Error {
58
63
  readonly currency: Currency;
59
64
  readonly required: BigNumber;
@@ -74,9 +79,7 @@ interface PaymentMaker {
74
79
  }
75
80
 
76
81
  type RequiredClientConfigFields = 'mcpServer' | 'account';
77
- type RequiredClientConfig = Pick<ClientConfig, RequiredClientConfigFields>;
78
82
  type OptionalClientConfig = Omit<ClientConfig, RequiredClientConfigFields>;
79
- type ClientArgs = RequiredClientConfig & Partial<OptionalClientConfig>;
80
83
  type BuildableClientConfigFields = 'oAuthDb' | 'logger';
81
84
  declare const DEFAULT_CLIENT_CONFIG: Required<Omit<OptionalClientConfig, BuildableClientConfigFields>>;
82
85
  declare function buildClientConfig(args: ClientArgs): ClientConfig;
@@ -124,73 +127,14 @@ declare class OAuthClient extends OAuthResourceClient {
124
127
  protected _doFetch: FetchLike;
125
128
  }
126
129
 
127
- interface ATXPFetcherConfig {
128
- accountId: string;
129
- db: OAuthDb;
130
- paymentMakers: {
131
- [key: string]: PaymentMaker;
132
- };
133
- fetchFn?: FetchLike;
134
- sideChannelFetch?: FetchLike;
135
- strict?: boolean;
136
- allowInsecureRequests?: boolean;
137
- allowedAuthorizationServers?: AuthorizationServerUrl[];
138
- approvePayment?: (payment: ProspectivePayment) => Promise<boolean>;
139
- logger?: Logger;
140
- onAuthorize?: (args: {
141
- authorizationServer: AuthorizationServerUrl;
142
- userId: string;
143
- }) => Promise<void>;
144
- onAuthorizeFailure?: (args: {
145
- authorizationServer: AuthorizationServerUrl;
146
- userId: string;
147
- error: Error;
148
- }) => Promise<void>;
149
- onPayment?: (args: {
150
- payment: ProspectivePayment;
151
- }) => Promise<void>;
152
- onPaymentFailure?: (args: {
153
- payment: ProspectivePayment;
154
- error: Error;
155
- }) => Promise<void>;
156
- }
157
- declare function atxpFetch(config: ATXPFetcherConfig): FetchLike;
158
- declare class ATXPFetcher {
159
- protected oauthClient: OAuthClient;
160
- protected paymentMakers: Map<string, PaymentMaker>;
161
- protected sideChannelFetch: FetchLike;
162
- protected db: OAuthDb;
163
- protected accountId: string;
164
- protected allowedAuthorizationServers: AuthorizationServerUrl[];
165
- protected approvePayment: (payment: ProspectivePayment) => Promise<boolean>;
166
- protected logger: Logger;
167
- protected onAuthorize: (args: {
168
- authorizationServer: AuthorizationServerUrl;
169
- userId: string;
170
- }) => Promise<void>;
171
- protected onAuthorizeFailure: (args: {
172
- authorizationServer: AuthorizationServerUrl;
173
- userId: string;
174
- error: Error;
175
- }) => Promise<void>;
176
- protected onPayment: (args: {
177
- payment: ProspectivePayment;
178
- }) => Promise<void>;
179
- protected onPaymentFailure: (args: {
180
- payment: ProspectivePayment;
181
- error: Error;
182
- }) => Promise<void>;
183
- constructor({ accountId, db, paymentMakers, fetchFn, sideChannelFetch, strict, allowInsecureRequests, allowedAuthorizationServers, approvePayment, logger, onAuthorize, onAuthorizeFailure, onPayment, onPaymentFailure }: ATXPFetcherConfig);
184
- private defaultPaymentFailureHandler;
185
- protected handlePaymentRequestError: (paymentRequestError: McpError) => Promise<boolean>;
186
- protected getPaymentRequestData: (paymentRequestUrl: string) => Promise<PaymentRequestData | null>;
187
- protected isAllowedAuthServer: (url: string | URL) => boolean;
188
- protected makeAuthRequestWithPaymentMaker: (authorizationUrl: URL, paymentMaker: PaymentMaker) => Promise<string>;
189
- protected authToService: (error: OAuthAuthenticationRequiredError) => Promise<void>;
190
- protected exchangeToken: (myToken: AccessToken, newResourceUrl: string) => Promise<AccessToken>;
191
- protected checkForATXPResponse: (response: Response) => Promise<void>;
192
- fetch: FetchLike;
193
- }
130
+ /**
131
+ * Creates an ATXP fetch wrapper that handles OAuth authentication and payments.
132
+ * This follows the wrapper pattern for fetch functions.
133
+ *
134
+ * @param config - The client configuration
135
+ * @returns A wrapped fetch function that handles ATXP protocol
136
+ */
137
+ declare function atxpFetch(config: ClientConfig): FetchLike;
194
138
 
195
139
  declare const ValidateTransferError: typeof ValidateTransferError$1;
196
140
  declare class SolanaPaymentMaker implements PaymentMaker {
@@ -230,10 +174,14 @@ declare class ATXPAccount implements Account {
230
174
  paymentMakers: {
231
175
  [key: string]: PaymentMaker;
232
176
  };
177
+ origin: string;
178
+ token: string;
179
+ fetchFn: FetchLike;
233
180
  constructor(connectionString: string, opts?: {
234
181
  fetchFn?: FetchLike;
235
182
  network?: Network;
236
183
  });
184
+ getSigner(): Promise<LocalAccount>;
237
185
  }
238
186
 
239
187
  declare const USDC_CONTRACT_ADDRESS_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
@@ -243,8 +191,63 @@ declare class BaseAccount implements Account {
243
191
  paymentMakers: {
244
192
  [key: string]: PaymentMaker;
245
193
  };
194
+ private walletClient;
195
+ private account;
246
196
  constructor(baseRPCUrl: string, sourceSecretKey: string);
197
+ /**
198
+ * Get a signer that can be used with the x402 library
199
+ * This is only available for EVM-based accounts
200
+ */
201
+ getSigner(): LocalAccount;
202
+ }
203
+
204
+ /**
205
+ * ATXP implementation of viem's LocalAccount interface.
206
+ * Delegates signing operations to the accounts-x402 API.
207
+ * Includes properties needed by x402 library for wallet client compatibility.
208
+ */
209
+ declare class ATXPLocalAccount implements LocalAccount {
210
+ readonly address: Address;
211
+ private origin;
212
+ private token;
213
+ private fetchFn;
214
+ readonly type: "local";
215
+ readonly account: LocalAccount;
216
+ readonly chain: {
217
+ id: number;
218
+ };
219
+ readonly transport: object;
220
+ constructor(address: Address, origin: string, token: string, fetchFn?: FetchLike);
221
+ /**
222
+ * Fetch the wallet address from the /address endpoint
223
+ */
224
+ static create(origin: string, token: string, fetchFn?: FetchLike): Promise<ATXPLocalAccount>;
225
+ /**
226
+ * Sign a typed data structure using EIP-712
227
+ * This is what x402 library will call for EIP-3009 authorization
228
+ */
229
+ signTypedData<const TTypedData extends TypedData | {
230
+ [key: string]: unknown;
231
+ }>(typedData: TTypedData): Promise<Hex$1>;
232
+ /**
233
+ * Sign a message - required by LocalAccount interface but not used for X402
234
+ */
235
+ signMessage(_: {
236
+ message: SignableMessage;
237
+ }): Promise<Hex$1>;
238
+ /**
239
+ * Sign a transaction - required by LocalAccount interface but not used for X402
240
+ */
241
+ signTransaction(_transaction: TransactionSerializable, _args?: unknown): Promise<Hex$1>;
242
+ /**
243
+ * Get public key - required by LocalAccount interface
244
+ */
245
+ readonly publicKey: Hex$1;
246
+ /**
247
+ * Source - required by LocalAccount interface (set to 'custom')
248
+ */
249
+ readonly source: "custom";
247
250
  }
248
251
 
249
- export { ATXPAccount, ATXPFetcher, BaseAccount, BasePaymentMaker, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, PaymentNetworkError, SolanaAccount, SolanaPaymentMaker, USDC_CONTRACT_ADDRESS_BASE, ValidateTransferError, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport };
250
- export type { ATXPFetcherConfig, Account, AccountIdString, ClientArgs, ClientConfig, Hex, OAuthClientConfig, PaymentMaker, ProspectivePayment };
252
+ export { ATXPAccount, ATXPLocalAccount, BaseAccount, BasePaymentMaker, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, PaymentNetworkError, SolanaAccount, SolanaPaymentMaker, USDC_CONTRACT_ADDRESS_BASE, ValidateTransferError, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport };
253
+ export type { Account, AccountIdString, ClientArgs, ClientConfig, FetchWrapper, Hex, OAuthClientConfig, PaymentMaker, ProspectivePayment };
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, getIsReactNative, createReactNativeSafeFetch, DEFAULT_AUTHORIZATION_SERVER, MemoryOAuthDb, generateJWT } from '@atxp/common';
1
+ import { crypto as crypto$1, OAuthResourceClient, ConsoleLogger, PAYMENT_REQUIRED_ERROR_CODE, isSSEResponse, parseMcpMessages, parsePaymentRequests, paymentRequiredError, DEFAULT_AUTHORIZATION_SERVER, getIsReactNative, createReactNativeSafeFetch, MemoryOAuthDb, generateJWT } from '@atxp/common';
2
2
  import BigNumber$1, { BigNumber } from 'bignumber.js';
3
3
  import * as oauth from 'oauth4webapi';
4
4
  import { PublicKey, ComputeBudgetProgram, sendAndConfirmTransaction, Connection, Keypair } from '@solana/web3.js';
@@ -297,18 +297,39 @@ class InsufficientFundsError extends Error {
297
297
  }
298
298
  class PaymentNetworkError extends Error {
299
299
  constructor(message, originalError) {
300
- super(`Payment failed due to network error: ${message}. Please check your network connection and try again.`);
300
+ super(`Payment failed due to network error: ${message}`);
301
301
  this.originalError = originalError;
302
302
  this.name = 'PaymentNetworkError';
303
303
  }
304
304
  }
305
305
 
306
+ /**
307
+ * Creates an ATXP fetch wrapper that handles OAuth authentication and payments.
308
+ * This follows the wrapper pattern for fetch functions.
309
+ *
310
+ * @param config - The client configuration
311
+ * @returns A wrapped fetch function that handles ATXP protocol
312
+ */
306
313
  function atxpFetch(config) {
307
- const fetcher = new ATXPFetcher(config);
314
+ const fetcher = new ATXPFetcher({
315
+ accountId: config.account.accountId,
316
+ db: config.oAuthDb,
317
+ paymentMakers: config.account.paymentMakers,
318
+ fetchFn: config.fetchFn,
319
+ sideChannelFetch: config.oAuthChannelFetch,
320
+ allowInsecureRequests: config.allowHttp,
321
+ allowedAuthorizationServers: config.allowedAuthorizationServers,
322
+ approvePayment: config.approvePayment,
323
+ logger: config.logger,
324
+ onAuthorize: config.onAuthorize,
325
+ onAuthorizeFailure: config.onAuthorizeFailure,
326
+ onPayment: config.onPayment,
327
+ onPaymentFailure: config.onPaymentFailure
328
+ });
308
329
  return fetcher.fetch;
309
330
  }
310
331
  class ATXPFetcher {
311
- constructor({ accountId, db, paymentMakers, 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 = async () => { } }) {
332
+ constructor(config) {
312
333
  this.defaultPaymentFailureHandler = async ({ payment, error }) => {
313
334
  if (error instanceof InsufficientFundsError) {
314
335
  this.logger.info(`PAYMENT FAILED: Insufficient ${error.currency} funds on ${payment.network}`);
@@ -325,6 +346,70 @@ class ATXPFetcher {
325
346
  this.logger.info(`PAYMENT FAILED: ${error.message}`);
326
347
  }
327
348
  };
349
+ this.handleMultiDestinationPayment = async (paymentRequestData, paymentRequestUrl, paymentRequestId) => {
350
+ if (!paymentRequestData.destinations || paymentRequestData.destinations.length === 0) {
351
+ return false;
352
+ }
353
+ // Try each destination in order
354
+ for (const dest of paymentRequestData.destinations) {
355
+ const paymentMaker = this.paymentMakers.get(dest.network);
356
+ if (!paymentMaker) {
357
+ this.logger.debug(`ATXP: payment network '${dest.network}' not available, trying next destination`);
358
+ continue;
359
+ }
360
+ // Convert amount to BigNumber since it comes as a string from JSON
361
+ const amount = new BigNumber(dest.amount);
362
+ const prospectivePayment = {
363
+ accountId: this.accountId,
364
+ resourceUrl: paymentRequestData.resource?.toString() ?? '',
365
+ resourceName: paymentRequestData.resourceName ?? '',
366
+ network: dest.network,
367
+ currency: dest.currency,
368
+ amount: amount,
369
+ iss: paymentRequestData.iss ?? '',
370
+ };
371
+ if (!await this.approvePayment(prospectivePayment)) {
372
+ this.logger.info(`ATXP: payment request denied by callback function for destination on ${dest.network}`);
373
+ continue;
374
+ }
375
+ let paymentId;
376
+ try {
377
+ paymentId = await paymentMaker.makePayment(amount, dest.currency, dest.address, paymentRequestData.iss);
378
+ this.logger.info(`ATXP: made payment of ${amount.toString()} ${dest.currency} on ${dest.network}: ${paymentId}`);
379
+ await this.onPayment({ payment: prospectivePayment });
380
+ // Submit payment to the server
381
+ const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '' });
382
+ const response = await this.sideChannelFetch(paymentRequestUrl.toString(), {
383
+ method: 'PUT',
384
+ headers: {
385
+ 'Authorization': `Bearer ${jwt}`,
386
+ 'Content-Type': 'application/json'
387
+ },
388
+ body: JSON.stringify({
389
+ transactionId: paymentId,
390
+ network: dest.network,
391
+ currency: dest.currency
392
+ })
393
+ });
394
+ this.logger.debug(`ATXP: payment was ${response.ok ? 'successfully' : 'not successfully'} PUT to ${paymentRequestUrl} : status ${response.status} ${response.statusText}`);
395
+ if (!response.ok) {
396
+ const msg = `ATXP: payment to ${paymentRequestUrl} failed: HTTP ${response.status} ${await response.text()}`;
397
+ this.logger.info(msg);
398
+ throw new Error(msg);
399
+ }
400
+ return true;
401
+ }
402
+ catch (error) {
403
+ const typedError = error;
404
+ this.logger.warn(`ATXP: payment failed on ${dest.network}: ${typedError.message}`);
405
+ await this.onPaymentFailure({ payment: prospectivePayment, error: typedError });
406
+ // Try next destination
407
+ continue;
408
+ }
409
+ }
410
+ this.logger.info(`ATXP: no suitable payment destination found among ${paymentRequestData.destinations.length} options`);
411
+ return false;
412
+ };
328
413
  this.handlePaymentRequestError = async (paymentRequestError) => {
329
414
  if (paymentRequestError.code !== PAYMENT_REQUIRED_ERROR_CODE) {
330
415
  throw new Error(`ATXP: expected payment required error (code ${PAYMENT_REQUIRED_ERROR_CODE}); got code ${paymentRequestError.code}`);
@@ -345,6 +430,11 @@ class ATXPFetcher {
345
430
  if (!paymentRequestData) {
346
431
  throw new Error(`ATXP: payment request ${paymentRequestId} not found on server ${paymentRequestUrl}`);
347
432
  }
433
+ // Handle multi-destination format
434
+ if (paymentRequestData.destinations && paymentRequestData.destinations.length > 0) {
435
+ return this.handleMultiDestinationPayment(paymentRequestData, paymentRequestUrl, paymentRequestId);
436
+ }
437
+ // Handle legacy single destination format
348
438
  const requestedNetwork = paymentRequestData.network;
349
439
  if (!requestedNetwork) {
350
440
  throw new Error(`Payment network not provided`);
@@ -421,7 +511,8 @@ class ATXPFetcher {
421
511
  },
422
512
  body: JSON.stringify({
423
513
  transactionId: paymentId,
424
- network: requestedNetwork
514
+ network: requestedNetwork,
515
+ currency: currency
425
516
  })
426
517
  });
427
518
  this.logger.debug(`ATXP: payment was ${response.ok ? 'successfully' : 'not successfully'} PUT to ${paymentRequestUrl} : status ${response.status} ${response.statusText}`);
@@ -640,6 +731,7 @@ class ATXPFetcher {
640
731
  throw error;
641
732
  }
642
733
  };
734
+ const { accountId, db, paymentMakers, 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 = async () => { } } = config;
643
735
  // Use React Native safe fetch if in React Native environment
644
736
  const safeFetchFn = getIsReactNative() ? createReactNativeSafeFetch(fetchFn) : fetchFn;
645
737
  const safeSideChannelFetch = getIsReactNative() ? createReactNativeSafeFetch(sideChannelFetch) : sideChannelFetch;
@@ -15798,22 +15890,9 @@ function buildClientConfig(args) {
15798
15890
  }
15799
15891
  function buildStreamableTransport(args) {
15800
15892
  const config = buildClientConfig(args);
15801
- const fetcher = new ATXPFetcher({
15802
- accountId: args.account.accountId,
15803
- db: config.oAuthDb,
15804
- paymentMakers: args.account.paymentMakers,
15805
- fetchFn: config.fetchFn,
15806
- sideChannelFetch: config.oAuthChannelFetch,
15807
- allowInsecureRequests: config.allowHttp,
15808
- allowedAuthorizationServers: config.allowedAuthorizationServers,
15809
- approvePayment: config.approvePayment,
15810
- logger: config.logger,
15811
- onAuthorize: config.onAuthorize,
15812
- onAuthorizeFailure: config.onAuthorizeFailure,
15813
- onPayment: config.onPayment,
15814
- onPaymentFailure: config.onPaymentFailure
15815
- });
15816
- const transport = new StreamableHTTPClientTransport(new URL(args.mcpServer), { fetch: fetcher.fetch });
15893
+ // Apply the ATXP wrapper to the fetch function
15894
+ const wrappedFetch = atxpFetch(config);
15895
+ const transport = new StreamableHTTPClientTransport(new URL(args.mcpServer), { fetch: wrappedFetch });
15817
15896
  return transport;
15818
15897
  }
15819
15898
  async function atxpClient(args) {
@@ -15830,7 +15909,7 @@ const ValidateTransferError = ValidateTransferError$1;
15830
15909
  class SolanaPaymentMaker {
15831
15910
  constructor(solanaEndpoint, sourceSecretKey, logger) {
15832
15911
  this.generateJWT = async ({ paymentRequestId, codeChallenge }) => {
15833
- // Solana/Web3.js secretKey is 64 bytes:
15912
+ // Solana/Web3.js secretKey is 64 bytes:
15834
15913
  // first 32 bytes are the private scalar, last 32 are the public key.
15835
15914
  // JWK expects only the 32-byte private scalar for 'd'
15836
15915
  const jwk = {
@@ -15860,8 +15939,10 @@ class SolanaPaymentMaker {
15860
15939
  this.logger.warn(`Insufficient ${currency} balance for payment. Required: ${amount}, Available: ${balance}`);
15861
15940
  throw new InsufficientFundsError(currency, amount, balance, 'solana');
15862
15941
  }
15942
+ // Increase compute units to handle both memo and token transfer
15943
+ // Memo uses ~6000 CUs, token transfer needs ~6500 CUs
15863
15944
  const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
15864
- units: 10000,
15945
+ units: 50000,
15865
15946
  });
15866
15947
  const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
15867
15948
  microLamports: 20000,
@@ -16055,6 +16136,106 @@ class SolanaAccount {
16055
16136
  }
16056
16137
  }
16057
16138
 
16139
+ function toBasicAuth$1(token) {
16140
+ // Basic auth is base64("username:password"), password is blank
16141
+ const b64 = Buffer.from(`${token}:`).toString('base64');
16142
+ return `Basic ${b64}`;
16143
+ }
16144
+ /**
16145
+ * ATXP implementation of viem's LocalAccount interface.
16146
+ * Delegates signing operations to the accounts-x402 API.
16147
+ * Includes properties needed by x402 library for wallet client compatibility.
16148
+ */
16149
+ class ATXPLocalAccount {
16150
+ constructor(address, origin, token, fetchFn = fetch) {
16151
+ this.address = address;
16152
+ this.origin = origin;
16153
+ this.token = token;
16154
+ this.fetchFn = fetchFn;
16155
+ this.type = 'local';
16156
+ /**
16157
+ * Get public key - required by LocalAccount interface
16158
+ */
16159
+ this.publicKey = '0x0000000000000000000000000000000000000000000000000000000000000000';
16160
+ /**
16161
+ * Source - required by LocalAccount interface (set to 'custom')
16162
+ */
16163
+ this.source = 'custom';
16164
+ // x402 library expects these properties for wallet client compatibility
16165
+ this.account = this; // Self-reference for x402's isSignerWallet check
16166
+ this.chain = { id: 8453 }; // Base mainnet - could make this configurable
16167
+ this.transport = {}; // Empty transport object for x402 compatibility
16168
+ }
16169
+ /**
16170
+ * Fetch the wallet address from the /address endpoint
16171
+ */
16172
+ static async create(origin, token, fetchFn = fetch) {
16173
+ // The /address endpoint uses Basic auth like other authenticated endpoints
16174
+ // For X402, we need the Ethereum/Base address with USDC currency
16175
+ const url = new URL(`${origin}/address`);
16176
+ url.searchParams.set('network', 'base'); // X402 operates on Base
16177
+ url.searchParams.set('currency', 'USDC'); // Always USDC for X402
16178
+ const response = await fetchFn(url.toString(), {
16179
+ method: 'GET',
16180
+ headers: {
16181
+ 'Authorization': toBasicAuth$1(token)
16182
+ }
16183
+ });
16184
+ if (!response.ok) {
16185
+ const errorText = await response.text();
16186
+ throw new Error(`Failed to fetch destination address: ${response.status} ${response.statusText} ${errorText}`);
16187
+ }
16188
+ const data = await response.json();
16189
+ const address = data.address;
16190
+ if (!address) {
16191
+ throw new Error('Address endpoint did not return an address');
16192
+ }
16193
+ // Check that the account is an Ethereum/Base account (required for X402/EVM operations)
16194
+ const network = data.network;
16195
+ if (!network) {
16196
+ throw new Error('Address endpoint did not return a network');
16197
+ }
16198
+ if (network !== 'ethereum' && network !== 'base') {
16199
+ throw new Error(`ATXPLocalAccount requires an Ethereum/Base account, but got ${network} account`);
16200
+ }
16201
+ return new ATXPLocalAccount(address, origin, token, fetchFn);
16202
+ }
16203
+ /**
16204
+ * Sign a typed data structure using EIP-712
16205
+ * This is what x402 library will call for EIP-3009 authorization
16206
+ */
16207
+ async signTypedData(typedData) {
16208
+ const response = await this.fetchFn(`${this.origin}/sign-typed-data`, {
16209
+ method: 'POST',
16210
+ headers: {
16211
+ 'Authorization': toBasicAuth$1(this.token),
16212
+ 'Content-Type': 'application/json',
16213
+ },
16214
+ body: JSON.stringify({
16215
+ typedData
16216
+ })
16217
+ });
16218
+ if (!response.ok) {
16219
+ const errorText = await response.text();
16220
+ throw new Error(`Failed to sign typed data: ${response.status} ${response.statusText} ${errorText}`);
16221
+ }
16222
+ const result = await response.json();
16223
+ return result.signature;
16224
+ }
16225
+ /**
16226
+ * Sign a message - required by LocalAccount interface but not used for X402
16227
+ */
16228
+ async signMessage(_) {
16229
+ throw new Error('Message signing not implemented for ATXP local account');
16230
+ }
16231
+ /**
16232
+ * Sign a transaction - required by LocalAccount interface but not used for X402
16233
+ */
16234
+ async signTransaction(_transaction, _args) {
16235
+ throw new Error('Transaction signing not implemented for ATXP local account');
16236
+ }
16237
+ }
16238
+
16058
16239
  function toBasicAuth(token) {
16059
16240
  // Basic auth is base64("username:password"), password is blank
16060
16241
  const b64 = Buffer.from(`${token}:`).toString('base64');
@@ -16064,10 +16245,11 @@ function parseConnectionString(connectionString) {
16064
16245
  const url = new URL(connectionString);
16065
16246
  const origin = url.origin;
16066
16247
  const token = url.searchParams.get('connection_token') || '';
16248
+ const accountId = url.searchParams.get('account_id');
16067
16249
  if (!token) {
16068
16250
  throw new Error('ATXPAccount: connection string missing connection token');
16069
16251
  }
16070
- return { origin, token };
16252
+ return { origin, token, accountId };
16071
16253
  }
16072
16254
  class ATXPHttpPaymentMaker {
16073
16255
  constructor(origin, token, fetchFn = fetch) {
@@ -16076,19 +16258,19 @@ class ATXPHttpPaymentMaker {
16076
16258
  this.fetchFn = fetchFn;
16077
16259
  }
16078
16260
  async makePayment(amount, currency, receiver, memo) {
16079
- const body = {
16080
- amount: amount.toString(),
16081
- currency,
16082
- receiver,
16083
- memo,
16084
- };
16261
+ // Make a regular payment via the /pay endpoint
16085
16262
  const response = await this.fetchFn(`${this.origin}/pay`, {
16086
16263
  method: 'POST',
16087
16264
  headers: {
16088
16265
  'Authorization': toBasicAuth(this.token),
16089
16266
  'Content-Type': 'application/json',
16090
16267
  },
16091
- body: JSON.stringify(body),
16268
+ body: JSON.stringify({
16269
+ amount: amount.toString(),
16270
+ currency,
16271
+ receiver,
16272
+ memo,
16273
+ }),
16092
16274
  });
16093
16275
  if (!response.ok) {
16094
16276
  const text = await response.text();
@@ -16125,15 +16307,26 @@ class ATXPHttpPaymentMaker {
16125
16307
  }
16126
16308
  class ATXPAccount {
16127
16309
  constructor(connectionString, opts) {
16128
- const { origin, token } = parseConnectionString(connectionString);
16310
+ const { origin, token, accountId } = parseConnectionString(connectionString);
16129
16311
  const fetchFn = opts?.fetchFn ?? fetch;
16130
16312
  const network = opts?.network ?? 'base';
16131
- // Use token as a stable accountId namespace to keep OAuth/ATXP state per-connection
16132
- this.accountId = `atxp:${token}`;
16313
+ // Store for use in X402 payment creation
16314
+ this.origin = origin;
16315
+ this.token = token;
16316
+ this.fetchFn = fetchFn;
16317
+ if (accountId) {
16318
+ this.accountId = `atxp:${accountId}`;
16319
+ }
16320
+ else {
16321
+ this.accountId = `atxp:${crypto$1.randomUUID()}`;
16322
+ }
16133
16323
  this.paymentMakers = {
16134
16324
  [network]: new ATXPHttpPaymentMaker(origin, token, fetchFn),
16135
16325
  };
16136
16326
  }
16327
+ async getSigner() {
16328
+ return ATXPLocalAccount.create(this.origin, this.token, this.fetchFn);
16329
+ }
16137
16330
  }
16138
16331
 
16139
16332
  class BaseAccount {
@@ -16144,18 +16337,26 @@ class BaseAccount {
16144
16337
  if (!sourceSecretKey) {
16145
16338
  throw new Error('Source secret key is required');
16146
16339
  }
16147
- const account = privateKeyToAccount(sourceSecretKey);
16148
- this.accountId = account.address;
16149
- const walletClient = createWalletClient({
16150
- account: account,
16340
+ this.account = privateKeyToAccount(sourceSecretKey);
16341
+ this.accountId = this.account.address;
16342
+ this.walletClient = createWalletClient({
16343
+ account: this.account,
16151
16344
  chain: base,
16152
16345
  transport: http(baseRPCUrl),
16153
16346
  });
16154
16347
  this.paymentMakers = {
16155
- 'base': new BasePaymentMaker(baseRPCUrl, walletClient),
16348
+ 'base': new BasePaymentMaker(baseRPCUrl, this.walletClient),
16156
16349
  };
16157
16350
  }
16351
+ /**
16352
+ * Get a signer that can be used with the x402 library
16353
+ * This is only available for EVM-based accounts
16354
+ */
16355
+ getSigner() {
16356
+ // Return the viem account directly - it implements LocalAccount interface
16357
+ return this.account;
16358
+ }
16158
16359
  }
16159
16360
 
16160
- export { ATXPAccount, ATXPFetcher, BaseAccount, BasePaymentMaker, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, PaymentNetworkError, SolanaAccount, SolanaPaymentMaker, USDC_CONTRACT_ADDRESS_BASE, ValidateTransferError, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport };
16361
+ export { ATXPAccount, ATXPLocalAccount, BaseAccount, BasePaymentMaker, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, PaymentNetworkError, SolanaAccount, SolanaPaymentMaker, USDC_CONTRACT_ADDRESS_BASE, ValidateTransferError, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport };
16161
16362
  //# sourceMappingURL=index.js.map