@atxp/client 0.7.4 → 0.8.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/dist/atxpAccount.d.ts +8 -6
- package/dist/atxpAccount.d.ts.map +1 -1
- package/dist/atxpAccount.js +57 -19
- package/dist/atxpAccount.js.map +1 -1
- package/dist/atxpClient.d.ts +1 -1
- package/dist/atxpClient.d.ts.map +1 -1
- package/dist/atxpClient.js +18 -2
- package/dist/atxpClient.js.map +1 -1
- package/dist/atxpFetcher.d.ts +5 -15
- package/dist/atxpFetcher.d.ts.map +1 -1
- package/dist/atxpFetcher.js +95 -229
- package/dist/atxpFetcher.js.map +1 -1
- package/dist/baseAccount.d.ts +7 -4
- package/dist/baseAccount.d.ts.map +1 -1
- package/dist/baseAccount.js +15 -4
- package/dist/baseAccount.js.map +1 -1
- package/dist/basePaymentMaker.d.ts +4 -3
- package/dist/basePaymentMaker.d.ts.map +1 -1
- package/dist/basePaymentMaker.js +20 -3
- package/dist/basePaymentMaker.js.map +1 -1
- package/dist/clientTestHelpers.d.ts.map +1 -1
- package/dist/destinationMakers/atxpDestinationMaker.d.ts +15 -0
- package/dist/destinationMakers/atxpDestinationMaker.d.ts.map +1 -0
- package/dist/destinationMakers/atxpDestinationMaker.js +128 -0
- package/dist/destinationMakers/atxpDestinationMaker.js.map +1 -0
- package/dist/destinationMakers/index.d.ts +9 -0
- package/dist/destinationMakers/index.d.ts.map +1 -0
- package/dist/destinationMakers/index.js +42 -0
- package/dist/destinationMakers/index.js.map +1 -0
- package/dist/destinationMakers/passthroughDestinationMaker.d.ts +8 -0
- package/dist/destinationMakers/passthroughDestinationMaker.d.ts.map +1 -0
- package/dist/destinationMakers/passthroughDestinationMaker.js +27 -0
- package/dist/destinationMakers/passthroughDestinationMaker.js.map +1 -0
- package/dist/index.cjs +803 -584
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +99 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +798 -586
- package/dist/index.js.map +1 -1
- package/dist/polygonConstants.d.ts +46 -0
- package/dist/polygonConstants.d.ts.map +1 -0
- package/dist/polygonConstants.js +54 -0
- package/dist/polygonConstants.js.map +1 -0
- package/dist/solanaAccount.d.ts +8 -4
- package/dist/solanaAccount.d.ts.map +1 -1
- package/dist/solanaAccount.js +16 -4
- package/dist/solanaAccount.js.map +1 -1
- package/dist/solanaPaymentMaker.d.ts +4 -3
- package/dist/solanaPaymentMaker.d.ts.map +1 -1
- package/dist/solanaPaymentMaker.js +24 -5
- package/dist/solanaPaymentMaker.js.map +1 -1
- package/dist/types.d.ts +5 -23
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
-
import BigNumber$1, { BigNumber } from 'bignumber.js';
|
|
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, generateJWT } from '@atxp/common';
|
|
3
2
|
import * as oauth from 'oauth4webapi';
|
|
3
|
+
import BigNumber$1, { BigNumber } from 'bignumber.js';
|
|
4
4
|
import { PublicKey, ComputeBudgetProgram, sendAndConfirmTransaction, Connection, Keypair } from '@solana/web3.js';
|
|
5
5
|
import { ValidateTransferError as ValidateTransferError$1, createTransfer } from '@solana/pay';
|
|
6
6
|
import { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';
|
|
@@ -312,9 +312,9 @@ class PaymentNetworkError extends Error {
|
|
|
312
312
|
*/
|
|
313
313
|
function atxpFetch(config) {
|
|
314
314
|
const fetcher = new ATXPFetcher({
|
|
315
|
-
|
|
315
|
+
account: config.account,
|
|
316
316
|
db: config.oAuthDb,
|
|
317
|
-
|
|
317
|
+
destinationMakers: config.destinationMakers,
|
|
318
318
|
fetchFn: config.fetchFn,
|
|
319
319
|
sideChannelFetch: config.oAuthChannelFetch,
|
|
320
320
|
allowInsecureRequests: config.allowHttp,
|
|
@@ -332,7 +332,8 @@ class ATXPFetcher {
|
|
|
332
332
|
constructor(config) {
|
|
333
333
|
this.defaultPaymentFailureHandler = async ({ payment, error }) => {
|
|
334
334
|
if (error instanceof InsufficientFundsError) {
|
|
335
|
-
|
|
335
|
+
const networkText = error.network ? ` on ${error.network}` : '';
|
|
336
|
+
this.logger.info(`PAYMENT FAILED: Insufficient ${error.currency} funds${networkText}`);
|
|
336
337
|
this.logger.info(`Required: ${error.required} ${error.currency}`);
|
|
337
338
|
if (error.available) {
|
|
338
339
|
this.logger.info(`Available: ${error.available} ${error.currency}`);
|
|
@@ -340,7 +341,7 @@ class ATXPFetcher {
|
|
|
340
341
|
this.logger.info(`Account: ${payment.accountId}`);
|
|
341
342
|
}
|
|
342
343
|
else if (error instanceof PaymentNetworkError) {
|
|
343
|
-
this.logger.info(`PAYMENT FAILED: Network error
|
|
344
|
+
this.logger.info(`PAYMENT FAILED: Network error: ${error.message}`);
|
|
344
345
|
}
|
|
345
346
|
else {
|
|
346
347
|
this.logger.info(`PAYMENT FAILED: ${error.message}`);
|
|
@@ -350,43 +351,61 @@ class ATXPFetcher {
|
|
|
350
351
|
if (!paymentRequestData.destinations || paymentRequestData.destinations.length === 0) {
|
|
351
352
|
return false;
|
|
352
353
|
}
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
destinationAddress = resolved.destinationAddress;
|
|
363
|
-
destinationNetwork = resolved.network;
|
|
364
|
-
}
|
|
365
|
-
const paymentMaker = this.paymentMakers.get(destinationNetwork);
|
|
366
|
-
if (!paymentMaker) {
|
|
367
|
-
this.logger.debug(`ATXP: payment network '${destinationNetwork}' not available, trying next destination`);
|
|
354
|
+
// Get sources from the account
|
|
355
|
+
const sources = await this.account.getSources();
|
|
356
|
+
// Apply destination mappers to transform destinations
|
|
357
|
+
// Convert PaymentRequestDestination[] to Destination[] for mapper compatibility
|
|
358
|
+
const mappedDestinations = [];
|
|
359
|
+
for (const option of paymentRequestData.destinations) {
|
|
360
|
+
const destinationMaker = this.destinationMakers.get(option.network);
|
|
361
|
+
if (!destinationMaker) {
|
|
362
|
+
this.logger.debug(`ATXP: destination maker for network '${option.network}' not available, trying next destination`);
|
|
368
363
|
continue;
|
|
369
364
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
this.logger.info(`ATXP: payment request denied by callback function for destination on ${destinationNetwork}`);
|
|
381
|
-
continue;
|
|
365
|
+
mappedDestinations.push(...(await destinationMaker.makeDestinations(option, this.logger, paymentRequestId, sources)));
|
|
366
|
+
}
|
|
367
|
+
if (mappedDestinations.length === 0) {
|
|
368
|
+
this.logger.info(`ATXP: no destinations found after mapping`);
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
// Validate amounts are not negative
|
|
372
|
+
for (const dest of mappedDestinations) {
|
|
373
|
+
if (dest.amount.isLessThan(0)) {
|
|
374
|
+
throw new Error(`ATXP: payment amount cannot be negative: ${dest.amount.toString()} ${dest.currency}`);
|
|
382
375
|
}
|
|
383
|
-
|
|
376
|
+
}
|
|
377
|
+
// Create prospective payment for approval (using first destination for display)
|
|
378
|
+
const firstDest = mappedDestinations[0];
|
|
379
|
+
const prospectivePayment = {
|
|
380
|
+
accountId: this.account.accountId,
|
|
381
|
+
resourceUrl: paymentRequestData.resource?.toString() ?? '',
|
|
382
|
+
resourceName: paymentRequestData.resourceName ?? '',
|
|
383
|
+
currency: firstDest.currency,
|
|
384
|
+
amount: firstDest.amount,
|
|
385
|
+
iss: paymentRequestData.iss ?? '',
|
|
386
|
+
};
|
|
387
|
+
// Ask for approval once for all payment attempts
|
|
388
|
+
if (!await this.approvePayment(prospectivePayment)) {
|
|
389
|
+
this.logger.info(`ATXP: payment request denied by callback function`);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
// Try each payment maker in order
|
|
393
|
+
let lastPaymentError = null;
|
|
394
|
+
let paymentAttempted = false;
|
|
395
|
+
for (const paymentMaker of this.account.paymentMakers) {
|
|
384
396
|
try {
|
|
385
|
-
|
|
386
|
-
|
|
397
|
+
// Pass all destinations to payment maker - it will filter and pick the one it can handle
|
|
398
|
+
const result = await paymentMaker.makePayment(mappedDestinations, paymentRequestData.iss, paymentRequestId);
|
|
399
|
+
if (result === null) {
|
|
400
|
+
this.logger.debug(`ATXP: payment maker cannot handle these destinations, trying next`);
|
|
401
|
+
continue; // Try next payment maker
|
|
402
|
+
}
|
|
403
|
+
paymentAttempted = true;
|
|
404
|
+
// Payment was successful
|
|
405
|
+
this.logger.info(`ATXP: made payment of ${firstDest.amount.toString()} ${firstDest.currency} on ${result.chain}: ${result.transactionId}`);
|
|
387
406
|
await this.onPayment({ payment: prospectivePayment });
|
|
388
407
|
// Submit payment to the server
|
|
389
|
-
const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '' });
|
|
408
|
+
const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '', accountId: this.account.accountId });
|
|
390
409
|
const response = await this.sideChannelFetch(paymentRequestUrl.toString(), {
|
|
391
410
|
method: 'PUT',
|
|
392
411
|
headers: {
|
|
@@ -394,9 +413,10 @@ class ATXPFetcher {
|
|
|
394
413
|
'Content-Type': 'application/json'
|
|
395
414
|
},
|
|
396
415
|
body: JSON.stringify({
|
|
397
|
-
transactionId:
|
|
398
|
-
|
|
399
|
-
|
|
416
|
+
transactionId: result.transactionId,
|
|
417
|
+
...(result.transactionSubId ? { transactionSubId: result.transactionSubId } : {}),
|
|
418
|
+
chain: result.chain,
|
|
419
|
+
currency: result.currency
|
|
400
420
|
})
|
|
401
421
|
});
|
|
402
422
|
this.logger.debug(`ATXP: payment was ${response.ok ? 'successfully' : 'not successfully'} PUT to ${paymentRequestUrl} : status ${response.status} ${response.statusText}`);
|
|
@@ -409,13 +429,18 @@ class ATXPFetcher {
|
|
|
409
429
|
}
|
|
410
430
|
catch (error) {
|
|
411
431
|
const typedError = error;
|
|
412
|
-
|
|
432
|
+
paymentAttempted = true;
|
|
433
|
+
lastPaymentError = typedError;
|
|
434
|
+
this.logger.warn(`ATXP: payment maker failed: ${typedError.message}`);
|
|
413
435
|
await this.onPaymentFailure({ payment: prospectivePayment, error: typedError });
|
|
414
|
-
//
|
|
415
|
-
continue;
|
|
436
|
+
// Continue to next payment maker
|
|
416
437
|
}
|
|
417
438
|
}
|
|
418
|
-
|
|
439
|
+
// If payment was attempted but all failed, rethrow the last error
|
|
440
|
+
if (paymentAttempted && lastPaymentError) {
|
|
441
|
+
throw lastPaymentError;
|
|
442
|
+
}
|
|
443
|
+
this.logger.info(`ATXP: no payment maker could handle these destinations`);
|
|
419
444
|
return false;
|
|
420
445
|
};
|
|
421
446
|
this.handlePaymentRequestError = async (paymentRequestError) => {
|
|
@@ -442,102 +467,8 @@ class ATXPFetcher {
|
|
|
442
467
|
if (paymentRequestData.destinations && paymentRequestData.destinations.length > 0) {
|
|
443
468
|
return this.handleMultiDestinationPayment(paymentRequestData, paymentRequestUrl, paymentRequestId);
|
|
444
469
|
}
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
if (!requestedNetwork) {
|
|
448
|
-
throw new Error(`Payment network not provided`);
|
|
449
|
-
}
|
|
450
|
-
const destination = paymentRequestData.destination;
|
|
451
|
-
if (!destination) {
|
|
452
|
-
throw new Error(`destination not provided`);
|
|
453
|
-
}
|
|
454
|
-
let amount = new BigNumber(0);
|
|
455
|
-
if (!paymentRequestData.amount) {
|
|
456
|
-
throw new Error(`amount not provided`);
|
|
457
|
-
}
|
|
458
|
-
try {
|
|
459
|
-
amount = new BigNumber(paymentRequestData.amount);
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
throw new Error(`Invalid amount ${paymentRequestData.amount}`);
|
|
463
|
-
}
|
|
464
|
-
if (amount.lte(0)) {
|
|
465
|
-
throw new Error(`Invalid amount ${paymentRequestData.amount}`);
|
|
466
|
-
}
|
|
467
|
-
const currency = paymentRequestData.currency;
|
|
468
|
-
if (!currency) {
|
|
469
|
-
throw new Error(`Currency not provided`);
|
|
470
|
-
}
|
|
471
|
-
// Resolve atxp_base destinations to real base network destinations
|
|
472
|
-
let destinationAddress = destination;
|
|
473
|
-
let destinationNetwork = requestedNetwork;
|
|
474
|
-
const resolved = await this.resolveAtxpBaseDestination(requestedNetwork, destination, paymentRequestId, amount, currency, destination, paymentRequestData.iss);
|
|
475
|
-
if (resolved) {
|
|
476
|
-
destinationAddress = resolved.destinationAddress;
|
|
477
|
-
destinationNetwork = resolved.network;
|
|
478
|
-
}
|
|
479
|
-
const paymentMaker = this.paymentMakers.get(destinationNetwork);
|
|
480
|
-
if (!paymentMaker) {
|
|
481
|
-
this.logger.info(`ATXP: payment network '${destinationNetwork}' not set up for this client (available networks: ${Array.from(this.paymentMakers.keys()).join(', ')})`);
|
|
482
|
-
return false;
|
|
483
|
-
}
|
|
484
|
-
const prospectivePayment = {
|
|
485
|
-
accountId: this.accountId,
|
|
486
|
-
resourceUrl: paymentRequestData.resource?.toString() ?? '',
|
|
487
|
-
resourceName: paymentRequestData.resourceName ?? '',
|
|
488
|
-
network: destinationNetwork,
|
|
489
|
-
currency,
|
|
490
|
-
amount,
|
|
491
|
-
iss: paymentRequestData.iss ?? '',
|
|
492
|
-
};
|
|
493
|
-
if (!await this.approvePayment(prospectivePayment)) {
|
|
494
|
-
this.logger.info(`ATXP: payment request denied by callback function`);
|
|
495
|
-
return false;
|
|
496
|
-
}
|
|
497
|
-
let paymentId;
|
|
498
|
-
try {
|
|
499
|
-
paymentId = await paymentMaker.makePayment(amount, currency, destinationAddress, paymentRequestData.iss);
|
|
500
|
-
this.logger.info(`ATXP: made payment of ${amount} ${currency} on ${destinationNetwork}: ${paymentId}`);
|
|
501
|
-
// Call onPayment callback after successful payment
|
|
502
|
-
await this.onPayment({ payment: prospectivePayment });
|
|
503
|
-
}
|
|
504
|
-
catch (paymentError) {
|
|
505
|
-
// Call onPaymentFailure callback if payment fails
|
|
506
|
-
await this.onPaymentFailure({
|
|
507
|
-
payment: prospectivePayment,
|
|
508
|
-
error: paymentError
|
|
509
|
-
});
|
|
510
|
-
throw paymentError;
|
|
511
|
-
}
|
|
512
|
-
const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '' });
|
|
513
|
-
// Make a fetch call to the authorization URL with the payment ID
|
|
514
|
-
// redirect=false is a hack
|
|
515
|
-
// The OAuth spec calls for the authorization url to return with a redirect, but fetch
|
|
516
|
-
// on mobile will automatically follow the redirect (it doesn't support the redirect=manual option)
|
|
517
|
-
// We want the redirect URL so we can extract the code from it, not the contents of the
|
|
518
|
-
// redirect URL (which might not even exist for agentic ATXP clients)
|
|
519
|
-
// So ATXP servers are set up to instead return a 200 with the redirect URL in the body
|
|
520
|
-
// if we pass redirect=false.
|
|
521
|
-
// TODO: Remove the redirect=false hack once we have a way to handle the redirect on mobile
|
|
522
|
-
const response = await this.sideChannelFetch(paymentRequestUrl.toString(), {
|
|
523
|
-
method: 'PUT',
|
|
524
|
-
headers: {
|
|
525
|
-
'Authorization': `Bearer ${jwt}`,
|
|
526
|
-
'Content-Type': 'application/json'
|
|
527
|
-
},
|
|
528
|
-
body: JSON.stringify({
|
|
529
|
-
transactionId: paymentId,
|
|
530
|
-
network: destinationNetwork,
|
|
531
|
-
currency: currency
|
|
532
|
-
})
|
|
533
|
-
});
|
|
534
|
-
this.logger.debug(`ATXP: payment was ${response.ok ? 'successfully' : 'not successfully'} PUT to ${paymentRequestUrl} : status ${response.status} ${response.statusText}`);
|
|
535
|
-
if (!response.ok) {
|
|
536
|
-
const msg = `ATXP: payment to ${paymentRequestUrl} failed: HTTP ${response.status} ${await response.text()}`;
|
|
537
|
-
this.logger.info(msg);
|
|
538
|
-
throw new Error(msg);
|
|
539
|
-
}
|
|
540
|
-
return true;
|
|
470
|
+
// Payment request doesn't have destinations - this shouldn't happen with new SDK
|
|
471
|
+
throw new Error(`ATXP: payment request does not contain destinations array`);
|
|
541
472
|
};
|
|
542
473
|
this.getPaymentRequestData = async (paymentRequestUrl) => {
|
|
543
474
|
const prRequest = await this.sideChannelFetch(paymentRequestUrl);
|
|
@@ -545,6 +476,14 @@ class ATXPFetcher {
|
|
|
545
476
|
throw new Error(`ATXP: GET ${paymentRequestUrl} failed: ${prRequest.status} ${prRequest.statusText}`);
|
|
546
477
|
}
|
|
547
478
|
const paymentRequest = await prRequest.json();
|
|
479
|
+
// Parse amount strings to BigNumber objects
|
|
480
|
+
if (paymentRequest.destinations) {
|
|
481
|
+
for (const dest of paymentRequest.destinations) {
|
|
482
|
+
if (typeof dest.amount === 'string' || typeof dest.amount === 'number') {
|
|
483
|
+
dest.amount = new BigNumber(dest.amount);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
548
487
|
return paymentRequest;
|
|
549
488
|
};
|
|
550
489
|
this.isAllowedAuthServer = (url) => {
|
|
@@ -558,15 +497,15 @@ class ATXPFetcher {
|
|
|
558
497
|
throw new Error(`Code challenge not provided`);
|
|
559
498
|
}
|
|
560
499
|
if (!paymentMaker) {
|
|
561
|
-
const
|
|
562
|
-
throw new Error(`Payment maker is null/undefined. Available payment
|
|
500
|
+
const paymentMakerCount = this.account.paymentMakers.length;
|
|
501
|
+
throw new Error(`Payment maker is null/undefined. Available payment maker count: ${paymentMakerCount}. This usually indicates a payment maker object was not properly instantiated.`);
|
|
563
502
|
}
|
|
564
503
|
// TypeScript should prevent this, but add runtime check for edge cases (untyped JS, version mismatches, etc.)
|
|
565
504
|
if (!paymentMaker.generateJWT) {
|
|
566
|
-
const
|
|
567
|
-
throw new Error(`Payment maker is missing generateJWT method. Available payment
|
|
505
|
+
const paymentMakerCount = this.account.paymentMakers.length;
|
|
506
|
+
throw new Error(`Payment maker is missing generateJWT method. Available payment maker count: ${paymentMakerCount}. This indicates the payment maker object does not implement the PaymentMaker interface. If using TypeScript, ensure your payment maker properly implements the PaymentMaker interface.`);
|
|
568
507
|
}
|
|
569
|
-
const authToken = await paymentMaker.generateJWT({ paymentRequestId: '', codeChallenge: codeChallenge });
|
|
508
|
+
const authToken = await paymentMaker.generateJWT({ paymentRequestId: '', codeChallenge: codeChallenge, accountId: this.account.accountId });
|
|
570
509
|
// Make a fetch call to the authorization URL with the payment ID
|
|
571
510
|
// redirect=false is a hack
|
|
572
511
|
// The OAuth spec calls for the authorization url to return with a redirect, but fetch
|
|
@@ -612,11 +551,11 @@ class ATXPFetcher {
|
|
|
612
551
|
throw new Error(`Expected redirect response from authorization URL, got ${response.status}`);
|
|
613
552
|
};
|
|
614
553
|
this.authToService = async (error) => {
|
|
615
|
-
// TODO: We need to generalize this - we can't assume that there's a single paymentMaker for the auth flow.
|
|
616
|
-
if (this.paymentMakers.
|
|
554
|
+
// TODO: We need to generalize this - we can't assume that there's a single paymentMaker for the auth flow.
|
|
555
|
+
if (this.account.paymentMakers.length > 1) {
|
|
617
556
|
throw new Error(`ATXP: multiple payment makers found - cannot determine which one to use for auth`);
|
|
618
557
|
}
|
|
619
|
-
const paymentMaker =
|
|
558
|
+
const paymentMaker = this.account.paymentMakers[0];
|
|
620
559
|
if (paymentMaker) {
|
|
621
560
|
// We can do the full OAuth flow - we'll generate a signed JWT and call /authorize on the
|
|
622
561
|
// AS to get a code, then exchange the code for an access token
|
|
@@ -631,14 +570,14 @@ class ATXPFetcher {
|
|
|
631
570
|
// Call onAuthorize callback after successful authorization
|
|
632
571
|
await this.onAuthorize({
|
|
633
572
|
authorizationServer: authorizationUrl.origin,
|
|
634
|
-
userId: this.accountId
|
|
573
|
+
userId: this.account.accountId
|
|
635
574
|
});
|
|
636
575
|
}
|
|
637
576
|
catch (authError) {
|
|
638
577
|
// Call onAuthorizeFailure callback if authorization fails
|
|
639
578
|
await this.onAuthorizeFailure({
|
|
640
579
|
authorizationServer: authorizationUrl.origin,
|
|
641
|
-
userId: this.accountId,
|
|
580
|
+
userId: this.account.accountId,
|
|
642
581
|
error: authError
|
|
643
582
|
});
|
|
644
583
|
throw authError;
|
|
@@ -649,13 +588,13 @@ class ATXPFetcher {
|
|
|
649
588
|
// If we do, we'll use it to auth to the downstream resource
|
|
650
589
|
// (In pass-through scenarios, the atxpServer() middleware stores the incoming
|
|
651
590
|
// token in the DB under the '' resource URL).
|
|
652
|
-
const existingToken = await this.db.getAccessToken(this.accountId, '');
|
|
591
|
+
const existingToken = await this.db.getAccessToken(this.account.accountId, '');
|
|
653
592
|
if (!existingToken) {
|
|
654
593
|
this.logger.info(`ATXP: no token found for the current server - we can't exchange a token if we don't have one`);
|
|
655
594
|
throw error;
|
|
656
595
|
}
|
|
657
596
|
const newToken = await this.exchangeToken(existingToken, error.resourceServerUrl);
|
|
658
|
-
this.db.saveAccessToken(this.accountId, error.resourceServerUrl, newToken);
|
|
597
|
+
this.db.saveAccessToken(this.account.accountId, error.resourceServerUrl, newToken);
|
|
659
598
|
}
|
|
660
599
|
};
|
|
661
600
|
this.exchangeToken = async (myToken, newResourceUrl) => {
|
|
@@ -746,15 +685,15 @@ class ATXPFetcher {
|
|
|
746
685
|
throw error;
|
|
747
686
|
}
|
|
748
687
|
};
|
|
749
|
-
const {
|
|
688
|
+
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 = async () => { } } = config;
|
|
750
689
|
// Use React Native safe fetch if in React Native environment
|
|
751
690
|
const safeFetchFn = getIsReactNative() ? createReactNativeSafeFetch(fetchFn) : fetchFn;
|
|
752
691
|
const safeSideChannelFetch = getIsReactNative() ? createReactNativeSafeFetch(sideChannelFetch) : sideChannelFetch;
|
|
753
|
-
// ATXPClient should never actually use the callback url - instead of redirecting the user to
|
|
692
|
+
// ATXPClient should never actually use the callback url - instead of redirecting the user to
|
|
754
693
|
// an authorization url which redirects back to the callback url, ATXPClient posts the payment
|
|
755
694
|
// directly to the authorization server, then does the token exchange itself
|
|
756
695
|
this.oauthClient = new OAuthClient({
|
|
757
|
-
userId: accountId,
|
|
696
|
+
userId: account.accountId,
|
|
758
697
|
db,
|
|
759
698
|
callbackUrl: 'http://localhost:3000/unused-dummy-atxp-callback',
|
|
760
699
|
isPublic: false,
|
|
@@ -764,10 +703,10 @@ class ATXPFetcher {
|
|
|
764
703
|
allowInsecureRequests,
|
|
765
704
|
logger: logger
|
|
766
705
|
});
|
|
767
|
-
this.
|
|
706
|
+
this.account = account;
|
|
707
|
+
this.destinationMakers = destinationMakers;
|
|
768
708
|
this.sideChannelFetch = safeSideChannelFetch;
|
|
769
709
|
this.db = db;
|
|
770
|
-
this.accountId = accountId;
|
|
771
710
|
this.allowedAuthorizationServers = allowedAuthorizationServers;
|
|
772
711
|
this.approvePayment = approvePayment;
|
|
773
712
|
this.logger = logger;
|
|
@@ -776,79 +715,6 @@ class ATXPFetcher {
|
|
|
776
715
|
this.onPayment = onPayment;
|
|
777
716
|
this.onPaymentFailure = onPaymentFailure || this.defaultPaymentFailureHandler;
|
|
778
717
|
}
|
|
779
|
-
/**
|
|
780
|
-
* Resolves atxp_base or atxp_base_sepolia destinations to real base network destinations
|
|
781
|
-
* by calling the payment_info endpoint to get the destination address and network
|
|
782
|
-
*/
|
|
783
|
-
async resolveAtxpBaseDestination(network, paymentInfoUrl, paymentRequestId, amount, currency, receiver, memo) {
|
|
784
|
-
// Check if this is an atxp_base network that needs resolution
|
|
785
|
-
if (network !== 'atxp_base' && network !== 'atxp_base_sepolia') {
|
|
786
|
-
return null;
|
|
787
|
-
}
|
|
788
|
-
// Map atxp_base networks to their real counterparts
|
|
789
|
-
const realNetwork = network === 'atxp_base' ? 'base' : 'base_sepolia';
|
|
790
|
-
// Get the payment maker for the real network
|
|
791
|
-
const paymentMaker = this.paymentMakers.get(realNetwork);
|
|
792
|
-
if (!paymentMaker) {
|
|
793
|
-
this.logger.debug(`ATXP: payment network '${realNetwork}' not available for atxp_base resolution`);
|
|
794
|
-
return null;
|
|
795
|
-
}
|
|
796
|
-
// Get the buyer address (source address) from the payment maker
|
|
797
|
-
let buyerAddress;
|
|
798
|
-
try {
|
|
799
|
-
buyerAddress = await paymentMaker.getSourceAddress({
|
|
800
|
-
amount,
|
|
801
|
-
currency,
|
|
802
|
-
receiver,
|
|
803
|
-
memo
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
catch (error) {
|
|
807
|
-
this.logger.warn(`ATXP: failed to get source address from payment maker for ${realNetwork}: ${error.message}`);
|
|
808
|
-
return null;
|
|
809
|
-
}
|
|
810
|
-
// Call the payment_info endpoint
|
|
811
|
-
this.logger.debug(`ATXP: resolving ${network} destination via ${paymentInfoUrl}`);
|
|
812
|
-
try {
|
|
813
|
-
const response = await this.sideChannelFetch(paymentInfoUrl, {
|
|
814
|
-
method: 'POST',
|
|
815
|
-
headers: {
|
|
816
|
-
'Content-Type': 'application/json',
|
|
817
|
-
},
|
|
818
|
-
body: JSON.stringify({
|
|
819
|
-
paymentRequestId,
|
|
820
|
-
buyerAddress,
|
|
821
|
-
}),
|
|
822
|
-
});
|
|
823
|
-
if (!response.ok) {
|
|
824
|
-
const text = await response.text();
|
|
825
|
-
this.logger.warn(`ATXP: payment_info endpoint failed: ${response.status} ${response.statusText} ${text}`);
|
|
826
|
-
return null;
|
|
827
|
-
}
|
|
828
|
-
const data = await response.json();
|
|
829
|
-
if (data.status !== 'success') {
|
|
830
|
-
this.logger.warn(`ATXP: payment_info endpoint returned non-success status: ${JSON.stringify(data)}`);
|
|
831
|
-
return null;
|
|
832
|
-
}
|
|
833
|
-
if (!data.destinationAddress) {
|
|
834
|
-
this.logger.warn(`ATXP: payment_info endpoint did not return destinationAddress`);
|
|
835
|
-
return null;
|
|
836
|
-
}
|
|
837
|
-
if (!data.network) {
|
|
838
|
-
this.logger.warn(`ATXP: payment_info endpoint did not return network`);
|
|
839
|
-
return null;
|
|
840
|
-
}
|
|
841
|
-
this.logger.info(`ATXP: resolved ${network} destination to ${data.destinationAddress} on ${data.network}`);
|
|
842
|
-
return {
|
|
843
|
-
destinationAddress: data.destinationAddress,
|
|
844
|
-
network: data.network,
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
catch (error) {
|
|
848
|
-
this.logger.warn(`ATXP: failed to resolve ${network} destination: ${error.message}`);
|
|
849
|
-
return null;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
718
|
}
|
|
853
719
|
|
|
854
720
|
var util$1;
|
|
@@ -15933,146 +15799,618 @@ class StreamableHTTPClientTransport {
|
|
|
15933
15799
|
}
|
|
15934
15800
|
}
|
|
15935
15801
|
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
|
|
15942
|
-
|
|
15943
|
-
|
|
15944
|
-
|
|
15945
|
-
|
|
15946
|
-
|
|
15947
|
-
approvePayment: async (_p) => true,
|
|
15948
|
-
fetchFn: getFetch(),
|
|
15949
|
-
oAuthChannelFetch: getFetch(),
|
|
15950
|
-
allowHttp: false, // may be overridden in buildClientConfig by process.env.NODE_ENV
|
|
15951
|
-
clientInfo: {
|
|
15952
|
-
name: 'ATXPClient',
|
|
15953
|
-
version: '0.0.1'
|
|
15954
|
-
},
|
|
15955
|
-
clientOptions: {
|
|
15956
|
-
capabilities: {}
|
|
15957
|
-
},
|
|
15958
|
-
onAuthorize: async () => { },
|
|
15959
|
-
onAuthorizeFailure: async () => { },
|
|
15960
|
-
onPayment: async () => { },
|
|
15961
|
-
onPaymentFailure: async () => { }
|
|
15962
|
-
};
|
|
15963
|
-
function buildClientConfig(args) {
|
|
15964
|
-
// Use fetchFn for oAuthChannelFetch if the latter isn't explicitly set
|
|
15965
|
-
if (args.fetchFn && !args.oAuthChannelFetch) {
|
|
15966
|
-
args.oAuthChannelFetch = args.fetchFn;
|
|
15967
|
-
}
|
|
15968
|
-
// Read environment variable at runtime, not module load time
|
|
15969
|
-
const envDefaults = {
|
|
15970
|
-
...DEFAULT_CLIENT_CONFIG,
|
|
15971
|
-
allowHttp: process.env.NODE_ENV === 'development',
|
|
15972
|
-
};
|
|
15973
|
-
const withDefaults = { ...envDefaults, ...args };
|
|
15974
|
-
const logger = withDefaults.logger ?? new ConsoleLogger();
|
|
15975
|
-
const oAuthDb = withDefaults.oAuthDb ?? new MemoryOAuthDb({ logger });
|
|
15976
|
-
const built = { oAuthDb, logger };
|
|
15977
|
-
return Object.freeze({ ...withDefaults, ...built });
|
|
15978
|
-
}
|
|
15979
|
-
function buildStreamableTransport(args) {
|
|
15980
|
-
const config = buildClientConfig(args);
|
|
15981
|
-
// Apply the ATXP wrapper to the fetch function
|
|
15982
|
-
const wrappedFetch = atxpFetch(config);
|
|
15983
|
-
const transport = new StreamableHTTPClientTransport(new URL(args.mcpServer), { fetch: wrappedFetch });
|
|
15984
|
-
return transport;
|
|
15802
|
+
function isDestinationResponse(obj) {
|
|
15803
|
+
return (typeof obj === 'object' &&
|
|
15804
|
+
obj !== null &&
|
|
15805
|
+
'chain' in obj &&
|
|
15806
|
+
'address' in obj &&
|
|
15807
|
+
'currency' in obj &&
|
|
15808
|
+
'amount' in obj &&
|
|
15809
|
+
typeof obj.chain === 'string' &&
|
|
15810
|
+
typeof obj.address === 'string' &&
|
|
15811
|
+
typeof obj.currency === 'string' &&
|
|
15812
|
+
typeof obj.amount === 'string');
|
|
15985
15813
|
}
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15814
|
+
function isDestinationsApiResponse(obj) {
|
|
15815
|
+
return (typeof obj === 'object' &&
|
|
15816
|
+
obj !== null &&
|
|
15817
|
+
'destinations' in obj &&
|
|
15818
|
+
'paymentRequestId' in obj &&
|
|
15819
|
+
Array.isArray(obj.destinations) &&
|
|
15820
|
+
typeof obj.paymentRequestId === 'string');
|
|
15992
15821
|
}
|
|
15993
|
-
|
|
15994
|
-
//
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
15822
|
+
function parseDestinationsResponse(data) {
|
|
15823
|
+
// Validate response structure
|
|
15824
|
+
if (!isDestinationsApiResponse(data)) {
|
|
15825
|
+
throw new Error('Invalid response: expected object with destinations array and paymentRequestId');
|
|
15826
|
+
}
|
|
15827
|
+
// Validate and convert each destination
|
|
15828
|
+
return data.destinations.map((dest, index) => {
|
|
15829
|
+
if (!isDestinationResponse(dest)) {
|
|
15830
|
+
throw new Error(`Invalid destination at index ${index}: missing required fields (chain, address, currency, amount)`);
|
|
15831
|
+
}
|
|
15832
|
+
// Validate chain is a valid Chain enum value
|
|
15833
|
+
if (!isEnumValue(ChainEnum, dest.chain)) {
|
|
15834
|
+
const validChains = Object.values(ChainEnum).join(', ');
|
|
15835
|
+
throw new Error(`Invalid destination at index ${index}: chain "${dest.chain}" is not a valid chain. Valid chains are: ${validChains}`);
|
|
15836
|
+
}
|
|
15837
|
+
// Validate currency is a valid Currency enum value
|
|
15838
|
+
if (!isEnumValue(CurrencyEnum, dest.currency)) {
|
|
15839
|
+
const validCurrencies = Object.values(CurrencyEnum).join(', ');
|
|
15840
|
+
throw new Error(`Invalid destination at index ${index}: currency "${dest.currency}" is not a valid currency. Valid currencies are: ${validCurrencies}`);
|
|
15841
|
+
}
|
|
15842
|
+
// Validate amount is a valid number
|
|
15843
|
+
const amount = new BigNumber(dest.amount);
|
|
15844
|
+
if (amount.isNaN()) {
|
|
15845
|
+
throw new Error(`Invalid destination at index ${index}: amount "${dest.amount}" is not a valid number`);
|
|
15846
|
+
}
|
|
15847
|
+
return {
|
|
15848
|
+
chain: dest.chain,
|
|
15849
|
+
currency: dest.currency,
|
|
15850
|
+
address: dest.address,
|
|
15851
|
+
amount: amount
|
|
16014
15852
|
};
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
|
|
16019
|
-
|
|
16020
|
-
|
|
16021
|
-
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
});
|
|
16038
|
-
const transaction = await createTransfer(this.connection, this.source.publicKey, {
|
|
16039
|
-
amount: amount,
|
|
16040
|
-
recipient: receiverKey,
|
|
16041
|
-
splToken: USDC_MINT,
|
|
16042
|
-
memo,
|
|
16043
|
-
});
|
|
16044
|
-
transaction.add(modifyComputeUnits);
|
|
16045
|
-
transaction.add(addPriorityFee);
|
|
16046
|
-
const transactionHash = await sendAndConfirmTransaction(this.connection, transaction, [this.source]);
|
|
16047
|
-
return transactionHash;
|
|
15853
|
+
});
|
|
15854
|
+
}
|
|
15855
|
+
/**
|
|
15856
|
+
* Destination mapper for ATXP network destinations.
|
|
15857
|
+
* Converts destinations with network='atxp' to actual blockchain network destinations
|
|
15858
|
+
* by resolving the account ID to its associated blockchain addresses.
|
|
15859
|
+
*/
|
|
15860
|
+
class ATXPDestinationMaker {
|
|
15861
|
+
constructor(accountsServiceUrl, fetchFn = fetch) {
|
|
15862
|
+
this.accountsServiceUrl = accountsServiceUrl;
|
|
15863
|
+
this.fetchFn = fetchFn;
|
|
15864
|
+
}
|
|
15865
|
+
async makeDestinations(option, logger, paymentRequestId, sources) {
|
|
15866
|
+
if (option.network !== 'atxp') {
|
|
15867
|
+
return [];
|
|
15868
|
+
}
|
|
15869
|
+
try {
|
|
15870
|
+
// The address field contains the account ID (e.g., atxp_acct_xxx) for atxp options
|
|
15871
|
+
const accountId = option.address;
|
|
15872
|
+
// Always use the destinations endpoint
|
|
15873
|
+
const destinations = await this.getDestinations(accountId, paymentRequestId, option, sources, logger);
|
|
15874
|
+
if (destinations.length === 0) {
|
|
15875
|
+
logger.warn(`ATXPDestinationMaker: No destinations found for account ${accountId}`);
|
|
16048
15876
|
}
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
throw error;
|
|
16052
|
-
}
|
|
16053
|
-
// Wrap other errors in PaymentNetworkError
|
|
16054
|
-
throw new PaymentNetworkError(`Payment failed on Solana network: ${error.message}`, error);
|
|
15877
|
+
else {
|
|
15878
|
+
logger.debug(`ATXPDestinationMaker: Got ${destinations.length} destinations for account ${accountId}`);
|
|
16055
15879
|
}
|
|
16056
|
-
|
|
16057
|
-
if (!solanaEndpoint) {
|
|
16058
|
-
throw new Error('Solana endpoint is required');
|
|
15880
|
+
return destinations;
|
|
16059
15881
|
}
|
|
16060
|
-
|
|
16061
|
-
|
|
15882
|
+
catch (error) {
|
|
15883
|
+
logger.error(`ATXPDestinationMaker: Failed to make ATXP destinations: ${error}`);
|
|
15884
|
+
throw error;
|
|
16062
15885
|
}
|
|
16063
|
-
this.connection = new Connection(solanaEndpoint, { commitment: 'confirmed' });
|
|
16064
|
-
this.source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
|
|
16065
|
-
this.logger = logger ?? new ConsoleLogger();
|
|
16066
15886
|
}
|
|
16067
|
-
|
|
16068
|
-
|
|
15887
|
+
async getDestinations(accountId, paymentRequestId, option, sources, logger) {
|
|
15888
|
+
// Strip any network prefix if present
|
|
15889
|
+
const unqualifiedId = accountId.includes(':') ? accountId.split(':')[1] : accountId;
|
|
15890
|
+
const url = `${this.accountsServiceUrl}/account/${unqualifiedId}/destinations`;
|
|
15891
|
+
logger?.debug(`ATXPDestinationMaker: Fetching destinations from ${url}`);
|
|
15892
|
+
try {
|
|
15893
|
+
const requestBody = {
|
|
15894
|
+
paymentRequestId,
|
|
15895
|
+
options: [{
|
|
15896
|
+
network: option.network,
|
|
15897
|
+
currency: option.currency.toString(),
|
|
15898
|
+
address: option.address,
|
|
15899
|
+
amount: option.amount.toString()
|
|
15900
|
+
}],
|
|
15901
|
+
sources: sources
|
|
15902
|
+
};
|
|
15903
|
+
const response = await this.fetchFn(url, {
|
|
15904
|
+
method: 'POST',
|
|
15905
|
+
headers: {
|
|
15906
|
+
'Accept': 'application/json',
|
|
15907
|
+
'Content-Type': 'application/json',
|
|
15908
|
+
},
|
|
15909
|
+
body: JSON.stringify(requestBody),
|
|
15910
|
+
});
|
|
15911
|
+
if (!response.ok) {
|
|
15912
|
+
const text = await response.text();
|
|
15913
|
+
throw new Error(`Failed to fetch destinations: ${response.status} ${response.statusText} - ${text}`);
|
|
15914
|
+
}
|
|
15915
|
+
const data = await response.json();
|
|
15916
|
+
return parseDestinationsResponse(data);
|
|
15917
|
+
}
|
|
15918
|
+
catch (error) {
|
|
15919
|
+
logger?.error(`ATXPDestinationMaker: Error fetching destinations: ${error}`);
|
|
15920
|
+
throw error;
|
|
15921
|
+
}
|
|
16069
15922
|
}
|
|
16070
15923
|
}
|
|
16071
15924
|
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
15925
|
+
class PassthroughDestinationMaker {
|
|
15926
|
+
constructor(network) {
|
|
15927
|
+
this.network = network;
|
|
15928
|
+
}
|
|
15929
|
+
async makeDestinations(option, _logger, _paymentRequestId, _sources) {
|
|
15930
|
+
if (option.network !== this.network) {
|
|
15931
|
+
return [];
|
|
15932
|
+
}
|
|
15933
|
+
// Check if option.network is also a Chain by inspecting the ChainEnum values
|
|
15934
|
+
if (Object.values(ChainEnum).includes(option.network)) {
|
|
15935
|
+
// It's a chain, so return a single passthrough destination
|
|
15936
|
+
const destination = {
|
|
15937
|
+
chain: option.network,
|
|
15938
|
+
currency: option.currency,
|
|
15939
|
+
address: option.address,
|
|
15940
|
+
amount: option.amount
|
|
15941
|
+
};
|
|
15942
|
+
return [destination];
|
|
15943
|
+
}
|
|
15944
|
+
return [];
|
|
15945
|
+
}
|
|
15946
|
+
}
|
|
15947
|
+
|
|
15948
|
+
function createDestinationMakers(config) {
|
|
15949
|
+
const { atxpAccountsServer, fetchFn = fetch } = config;
|
|
15950
|
+
// Build the map by exhaustively checking all Network values
|
|
15951
|
+
const makers = new Map();
|
|
15952
|
+
for (const network of Object.values(NetworkEnum)) {
|
|
15953
|
+
// Exhaustiveness check using switch with assertNever
|
|
15954
|
+
switch (network) {
|
|
15955
|
+
case NetworkEnum.Solana:
|
|
15956
|
+
makers.set(network, new PassthroughDestinationMaker(network));
|
|
15957
|
+
break;
|
|
15958
|
+
case NetworkEnum.Base:
|
|
15959
|
+
makers.set(network, new PassthroughDestinationMaker(network));
|
|
15960
|
+
break;
|
|
15961
|
+
case NetworkEnum.World:
|
|
15962
|
+
makers.set(network, new PassthroughDestinationMaker(network));
|
|
15963
|
+
break;
|
|
15964
|
+
case NetworkEnum.Polygon:
|
|
15965
|
+
makers.set(network, new PassthroughDestinationMaker(network));
|
|
15966
|
+
break;
|
|
15967
|
+
case NetworkEnum.BaseSepolia:
|
|
15968
|
+
makers.set(network, new PassthroughDestinationMaker(network));
|
|
15969
|
+
break;
|
|
15970
|
+
case NetworkEnum.WorldSepolia:
|
|
15971
|
+
makers.set(network, new PassthroughDestinationMaker(network));
|
|
15972
|
+
break;
|
|
15973
|
+
case NetworkEnum.ATXP:
|
|
15974
|
+
makers.set(network, new ATXPDestinationMaker(atxpAccountsServer, fetchFn));
|
|
15975
|
+
break;
|
|
15976
|
+
default:
|
|
15977
|
+
// This will cause a compilation error if a new Network is added but not handled above
|
|
15978
|
+
assertNever(network);
|
|
15979
|
+
}
|
|
15980
|
+
}
|
|
15981
|
+
return makers;
|
|
15982
|
+
}
|
|
15983
|
+
|
|
15984
|
+
function toBasicAuth$1(token) {
|
|
15985
|
+
// Basic auth is base64("username:password"), password is blank
|
|
15986
|
+
const b64 = Buffer.from(`${token}:`).toString('base64');
|
|
15987
|
+
return `Basic ${b64}`;
|
|
15988
|
+
}
|
|
15989
|
+
/**
|
|
15990
|
+
* ATXP implementation of viem's LocalAccount interface.
|
|
15991
|
+
* Delegates signing operations to the accounts-x402 API.
|
|
15992
|
+
* Includes properties needed by x402 library for wallet client compatibility.
|
|
15993
|
+
*/
|
|
15994
|
+
class ATXPLocalAccount {
|
|
15995
|
+
constructor(address, origin, token, fetchFn = fetch) {
|
|
15996
|
+
this.address = address;
|
|
15997
|
+
this.origin = origin;
|
|
15998
|
+
this.token = token;
|
|
15999
|
+
this.fetchFn = fetchFn;
|
|
16000
|
+
this.type = 'local';
|
|
16001
|
+
/**
|
|
16002
|
+
* Get public key - required by LocalAccount interface
|
|
16003
|
+
*/
|
|
16004
|
+
this.publicKey = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
16005
|
+
/**
|
|
16006
|
+
* Source - required by LocalAccount interface (set to 'custom')
|
|
16007
|
+
*/
|
|
16008
|
+
this.source = 'custom';
|
|
16009
|
+
// x402 library expects these properties for wallet client compatibility
|
|
16010
|
+
this.account = this; // Self-reference for x402's isSignerWallet check
|
|
16011
|
+
this.chain = { id: 8453 }; // Base mainnet - could make this configurable
|
|
16012
|
+
this.transport = {}; // Empty transport object for x402 compatibility
|
|
16013
|
+
}
|
|
16014
|
+
/**
|
|
16015
|
+
* Fetch the wallet address from the /address endpoint
|
|
16016
|
+
*/
|
|
16017
|
+
static async create(origin, token, fetchFn = fetch) {
|
|
16018
|
+
// The /address endpoint uses Basic auth like other authenticated endpoints
|
|
16019
|
+
// For X402, we need the Ethereum/Base address with USDC currency
|
|
16020
|
+
const url = new URL(`${origin}/address`);
|
|
16021
|
+
url.searchParams.set('network', 'base'); // X402 operates on Base
|
|
16022
|
+
url.searchParams.set('currency', 'USDC'); // Always USDC for X402
|
|
16023
|
+
const response = await fetchFn(url.toString(), {
|
|
16024
|
+
method: 'GET',
|
|
16025
|
+
headers: {
|
|
16026
|
+
'Authorization': toBasicAuth$1(token)
|
|
16027
|
+
}
|
|
16028
|
+
});
|
|
16029
|
+
if (!response.ok) {
|
|
16030
|
+
const errorText = await response.text();
|
|
16031
|
+
throw new Error(`Failed to fetch destination address: ${response.status} ${response.statusText} ${errorText}`);
|
|
16032
|
+
}
|
|
16033
|
+
const data = await response.json();
|
|
16034
|
+
const address = data.address;
|
|
16035
|
+
if (!address) {
|
|
16036
|
+
throw new Error('Address endpoint did not return an address');
|
|
16037
|
+
}
|
|
16038
|
+
// Check that the account is an Ethereum/Base account (required for X402/EVM operations)
|
|
16039
|
+
const network = data.network;
|
|
16040
|
+
if (!network) {
|
|
16041
|
+
throw new Error('Address endpoint did not return a network');
|
|
16042
|
+
}
|
|
16043
|
+
if (network !== 'ethereum' && network !== 'base') {
|
|
16044
|
+
throw new Error(`ATXPLocalAccount requires an Ethereum/Base account, but got ${network} account`);
|
|
16045
|
+
}
|
|
16046
|
+
return new ATXPLocalAccount(address, origin, token, fetchFn);
|
|
16047
|
+
}
|
|
16048
|
+
/**
|
|
16049
|
+
* Sign a typed data structure using EIP-712
|
|
16050
|
+
* This is what x402 library will call for EIP-3009 authorization
|
|
16051
|
+
*/
|
|
16052
|
+
async signTypedData(typedData) {
|
|
16053
|
+
const response = await this.fetchFn(`${this.origin}/sign-typed-data`, {
|
|
16054
|
+
method: 'POST',
|
|
16055
|
+
headers: {
|
|
16056
|
+
'Authorization': toBasicAuth$1(this.token),
|
|
16057
|
+
'Content-Type': 'application/json',
|
|
16058
|
+
},
|
|
16059
|
+
body: JSON.stringify({
|
|
16060
|
+
typedData
|
|
16061
|
+
})
|
|
16062
|
+
});
|
|
16063
|
+
if (!response.ok) {
|
|
16064
|
+
const errorText = await response.text();
|
|
16065
|
+
throw new Error(`Failed to sign typed data: ${response.status} ${response.statusText} ${errorText}`);
|
|
16066
|
+
}
|
|
16067
|
+
const result = await response.json();
|
|
16068
|
+
return result.signature;
|
|
16069
|
+
}
|
|
16070
|
+
/**
|
|
16071
|
+
* Sign a message - required by LocalAccount interface but not used for X402
|
|
16072
|
+
*/
|
|
16073
|
+
async signMessage(_) {
|
|
16074
|
+
throw new Error('Message signing not implemented for ATXP local account');
|
|
16075
|
+
}
|
|
16076
|
+
/**
|
|
16077
|
+
* Sign a transaction - required by LocalAccount interface but not used for X402
|
|
16078
|
+
*/
|
|
16079
|
+
async signTransaction(_transaction, _args) {
|
|
16080
|
+
throw new Error('Transaction signing not implemented for ATXP local account');
|
|
16081
|
+
}
|
|
16082
|
+
}
|
|
16083
|
+
|
|
16084
|
+
function toBasicAuth(token) {
|
|
16085
|
+
// Basic auth is base64("username:password"), password is blank
|
|
16086
|
+
const b64 = Buffer.from(`${token}:`).toString('base64');
|
|
16087
|
+
return `Basic ${b64}`;
|
|
16088
|
+
}
|
|
16089
|
+
function parseConnectionString(connectionString) {
|
|
16090
|
+
const url = new URL(connectionString);
|
|
16091
|
+
const origin = url.origin;
|
|
16092
|
+
const token = url.searchParams.get('connection_token') || '';
|
|
16093
|
+
const accountId = url.searchParams.get('account_id');
|
|
16094
|
+
if (!token) {
|
|
16095
|
+
throw new Error('ATXPAccount: connection string missing connection token');
|
|
16096
|
+
}
|
|
16097
|
+
if (!accountId) {
|
|
16098
|
+
throw new Error('ATXPAccount: connection string missing account id');
|
|
16099
|
+
}
|
|
16100
|
+
return { origin, token, accountId };
|
|
16101
|
+
}
|
|
16102
|
+
class ATXPHttpPaymentMaker {
|
|
16103
|
+
constructor(origin, token, fetchFn = fetch) {
|
|
16104
|
+
this.origin = origin;
|
|
16105
|
+
this.token = token;
|
|
16106
|
+
this.fetchFn = fetchFn;
|
|
16107
|
+
}
|
|
16108
|
+
async getSourceAddress(params) {
|
|
16109
|
+
// Call the /address_for_payment endpoint to get the source address for this account
|
|
16110
|
+
const response = await this.fetchFn(`${this.origin}/address_for_payment`, {
|
|
16111
|
+
method: 'POST',
|
|
16112
|
+
headers: {
|
|
16113
|
+
'Authorization': toBasicAuth(this.token),
|
|
16114
|
+
'Content-Type': 'application/json',
|
|
16115
|
+
},
|
|
16116
|
+
body: JSON.stringify({
|
|
16117
|
+
amount: params.amount.toString(),
|
|
16118
|
+
currency: params.currency,
|
|
16119
|
+
receiver: params.receiver,
|
|
16120
|
+
memo: params.memo,
|
|
16121
|
+
}),
|
|
16122
|
+
});
|
|
16123
|
+
if (!response.ok) {
|
|
16124
|
+
const text = await response.text();
|
|
16125
|
+
throw new Error(`ATXPAccount: /address_for_payment failed: ${response.status} ${response.statusText} ${text}`);
|
|
16126
|
+
}
|
|
16127
|
+
const json = await response.json();
|
|
16128
|
+
if (!json?.sourceAddress) {
|
|
16129
|
+
throw new Error('ATXPAccount: /address_for_payment did not return sourceAddress');
|
|
16130
|
+
}
|
|
16131
|
+
return json.sourceAddress;
|
|
16132
|
+
}
|
|
16133
|
+
async makePayment(destinations, memo, paymentRequestId) {
|
|
16134
|
+
// Make a payment via the /pay endpoint with multiple destinations
|
|
16135
|
+
const response = await this.fetchFn(`${this.origin}/pay`, {
|
|
16136
|
+
method: 'POST',
|
|
16137
|
+
headers: {
|
|
16138
|
+
'Authorization': toBasicAuth(this.token),
|
|
16139
|
+
'Content-Type': 'application/json',
|
|
16140
|
+
},
|
|
16141
|
+
body: JSON.stringify({
|
|
16142
|
+
destinations: destinations.map(d => ({
|
|
16143
|
+
chain: d.chain,
|
|
16144
|
+
address: d.address,
|
|
16145
|
+
amount: d.amount.toString(),
|
|
16146
|
+
currency: d.currency
|
|
16147
|
+
})),
|
|
16148
|
+
memo,
|
|
16149
|
+
...(paymentRequestId && { paymentRequestId })
|
|
16150
|
+
}),
|
|
16151
|
+
});
|
|
16152
|
+
if (!response.ok) {
|
|
16153
|
+
const text = await response.text();
|
|
16154
|
+
throw new Error(`ATXPAccount: /pay failed: ${response.status} ${response.statusText} ${text}`);
|
|
16155
|
+
}
|
|
16156
|
+
const json = await response.json();
|
|
16157
|
+
const transactionId = json.transactionId;
|
|
16158
|
+
if (!transactionId) {
|
|
16159
|
+
throw new Error('ATXPAccount: /pay did not return transactionId or txHash');
|
|
16160
|
+
}
|
|
16161
|
+
if (!json?.chain) {
|
|
16162
|
+
throw new Error('ATXPAccount: /pay did not return chain');
|
|
16163
|
+
}
|
|
16164
|
+
if (!json?.currency) {
|
|
16165
|
+
throw new Error('ATXPAccount: /pay did not return currency');
|
|
16166
|
+
}
|
|
16167
|
+
return {
|
|
16168
|
+
transactionId,
|
|
16169
|
+
...(json.transactionSubId ? { transactionSubId: json.transactionSubId } : {}),
|
|
16170
|
+
chain: json.chain,
|
|
16171
|
+
currency: json.currency
|
|
16172
|
+
};
|
|
16173
|
+
}
|
|
16174
|
+
async generateJWT(params) {
|
|
16175
|
+
const response = await this.fetchFn(`${this.origin}/sign`, {
|
|
16176
|
+
method: 'POST',
|
|
16177
|
+
headers: {
|
|
16178
|
+
'Authorization': toBasicAuth(this.token),
|
|
16179
|
+
'Content-Type': 'application/json',
|
|
16180
|
+
},
|
|
16181
|
+
body: JSON.stringify({
|
|
16182
|
+
paymentRequestId: params.paymentRequestId,
|
|
16183
|
+
codeChallenge: params.codeChallenge,
|
|
16184
|
+
...(params.accountId ? { accountId: params.accountId } : {}),
|
|
16185
|
+
}),
|
|
16186
|
+
});
|
|
16187
|
+
if (!response.ok) {
|
|
16188
|
+
const text = await response.text();
|
|
16189
|
+
throw new Error(`ATXPAccount: /sign failed: ${response.status} ${response.statusText} ${text}`);
|
|
16190
|
+
}
|
|
16191
|
+
const json = await response.json();
|
|
16192
|
+
if (!json?.jwt) {
|
|
16193
|
+
throw new Error('ATXPAccount: /sign did not return jwt');
|
|
16194
|
+
}
|
|
16195
|
+
return json.jwt;
|
|
16196
|
+
}
|
|
16197
|
+
}
|
|
16198
|
+
class ATXPAccount {
|
|
16199
|
+
constructor(connectionString, opts) {
|
|
16200
|
+
const { origin, token, accountId } = parseConnectionString(connectionString);
|
|
16201
|
+
const fetchFn = opts?.fetchFn ?? fetch;
|
|
16202
|
+
// Store for use in X402 payment creation
|
|
16203
|
+
this.origin = origin;
|
|
16204
|
+
this.token = token;
|
|
16205
|
+
this.fetchFn = fetchFn;
|
|
16206
|
+
// Format accountId as network:address
|
|
16207
|
+
// Connection string provides just the atxp_acct_xxx part (no prefix for UI)
|
|
16208
|
+
this.unqualifiedAccountId = accountId;
|
|
16209
|
+
this.accountId = `atxp:${accountId}`;
|
|
16210
|
+
this.paymentMakers = [
|
|
16211
|
+
new ATXPHttpPaymentMaker(origin, token, fetchFn)
|
|
16212
|
+
];
|
|
16213
|
+
}
|
|
16214
|
+
async getSigner() {
|
|
16215
|
+
return ATXPLocalAccount.create(this.origin, this.token, this.fetchFn);
|
|
16216
|
+
}
|
|
16217
|
+
/**
|
|
16218
|
+
* Get sources for this account by calling the accounts service
|
|
16219
|
+
*/
|
|
16220
|
+
async getSources() {
|
|
16221
|
+
// Use the unqualified account ID (without atxp: prefix) for the API call
|
|
16222
|
+
const response = await this.fetchFn(`${this.origin}/account/${this.unqualifiedAccountId}/sources`, {
|
|
16223
|
+
method: 'GET',
|
|
16224
|
+
headers: {
|
|
16225
|
+
'Accept': 'application/json',
|
|
16226
|
+
}
|
|
16227
|
+
});
|
|
16228
|
+
if (!response.ok) {
|
|
16229
|
+
const text = await response.text();
|
|
16230
|
+
throw new Error(`ATXPAccount: /account/${this.unqualifiedAccountId}/sources failed: ${response.status} ${response.statusText} ${text}`);
|
|
16231
|
+
}
|
|
16232
|
+
const json = await response.json();
|
|
16233
|
+
// The accounts service returns the sources array directly, not wrapped in an object
|
|
16234
|
+
if (!Array.isArray(json)) {
|
|
16235
|
+
throw new Error(`ATXPAccount: /account/${this.unqualifiedAccountId}/sources did not return sources array`);
|
|
16236
|
+
}
|
|
16237
|
+
return json;
|
|
16238
|
+
}
|
|
16239
|
+
}
|
|
16240
|
+
|
|
16241
|
+
// Detect if we're in a browser environment and bind fetch appropriately
|
|
16242
|
+
const getFetch = () => {
|
|
16243
|
+
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
16244
|
+
// In browser, bind fetch to window to avoid "Illegal invocation" errors
|
|
16245
|
+
return fetch.bind(window);
|
|
16246
|
+
}
|
|
16247
|
+
// In Node.js or other environments, use fetch as-is
|
|
16248
|
+
return fetch;
|
|
16249
|
+
};
|
|
16250
|
+
const DEFAULT_CLIENT_CONFIG = {
|
|
16251
|
+
allowedAuthorizationServers: [DEFAULT_AUTHORIZATION_SERVER],
|
|
16252
|
+
atxpAccountsServer: DEFAULT_ATXP_ACCOUNTS_SERVER,
|
|
16253
|
+
approvePayment: async (_p) => true,
|
|
16254
|
+
fetchFn: getFetch(),
|
|
16255
|
+
oAuthChannelFetch: getFetch(),
|
|
16256
|
+
allowHttp: false, // may be overridden in buildClientConfig by process.env.NODE_ENV
|
|
16257
|
+
clientInfo: {
|
|
16258
|
+
name: 'ATXPClient',
|
|
16259
|
+
version: '0.0.1'
|
|
16260
|
+
},
|
|
16261
|
+
clientOptions: {
|
|
16262
|
+
capabilities: {}
|
|
16263
|
+
},
|
|
16264
|
+
onAuthorize: async () => { },
|
|
16265
|
+
onAuthorizeFailure: async () => { },
|
|
16266
|
+
onPayment: async () => { },
|
|
16267
|
+
onPaymentFailure: async () => { }
|
|
16268
|
+
};
|
|
16269
|
+
function buildClientConfig(args) {
|
|
16270
|
+
// Use fetchFn for oAuthChannelFetch if the latter isn't explicitly set
|
|
16271
|
+
if (args.fetchFn && !args.oAuthChannelFetch) {
|
|
16272
|
+
args.oAuthChannelFetch = args.fetchFn;
|
|
16273
|
+
}
|
|
16274
|
+
// Read environment variable at runtime, not module load time
|
|
16275
|
+
const envDefaults = {
|
|
16276
|
+
...DEFAULT_CLIENT_CONFIG,
|
|
16277
|
+
allowHttp: process.env.NODE_ENV === 'development',
|
|
16278
|
+
};
|
|
16279
|
+
const withDefaults = { ...envDefaults, ...args };
|
|
16280
|
+
const logger = withDefaults.logger ?? new ConsoleLogger();
|
|
16281
|
+
const oAuthDb = withDefaults.oAuthDb ?? new MemoryOAuthDb({ logger });
|
|
16282
|
+
const fetchFn = withDefaults.fetchFn;
|
|
16283
|
+
// Build destination makers if not provided
|
|
16284
|
+
let accountsServer = withDefaults.atxpAccountsServer;
|
|
16285
|
+
// QoL hack for unspecified accounts server - if the caller is passing an atxpAccount, then assume the origin for that
|
|
16286
|
+
// is what we should use for the accounts server. In practice, the only option is accounts.atxp.ai,
|
|
16287
|
+
// but this supports staging environment
|
|
16288
|
+
if (args.atxpAccountsServer === undefined && withDefaults.account && withDefaults.account instanceof ATXPAccount) {
|
|
16289
|
+
accountsServer = withDefaults.account.origin;
|
|
16290
|
+
}
|
|
16291
|
+
const destinationMakers = withDefaults.destinationMakers ?? createDestinationMakers({
|
|
16292
|
+
atxpAccountsServer: accountsServer,
|
|
16293
|
+
fetchFn
|
|
16294
|
+
});
|
|
16295
|
+
const built = { oAuthDb, logger, destinationMakers };
|
|
16296
|
+
return Object.freeze({ ...withDefaults, ...built });
|
|
16297
|
+
}
|
|
16298
|
+
function buildStreamableTransport(args) {
|
|
16299
|
+
const config = buildClientConfig(args);
|
|
16300
|
+
// Apply the ATXP wrapper to the fetch function
|
|
16301
|
+
const wrappedFetch = atxpFetch(config);
|
|
16302
|
+
const transport = new StreamableHTTPClientTransport(new URL(args.mcpServer), { fetch: wrappedFetch });
|
|
16303
|
+
return transport;
|
|
16304
|
+
}
|
|
16305
|
+
async function atxpClient(args) {
|
|
16306
|
+
const config = buildClientConfig(args);
|
|
16307
|
+
const transport = buildStreamableTransport(config);
|
|
16308
|
+
const client = new Client(config.clientInfo, config.clientOptions);
|
|
16309
|
+
await client.connect(transport);
|
|
16310
|
+
return client;
|
|
16311
|
+
}
|
|
16312
|
+
|
|
16313
|
+
// this is a global public key for USDC on the solana mainnet
|
|
16314
|
+
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
16315
|
+
const ValidateTransferError = ValidateTransferError$1;
|
|
16316
|
+
class SolanaPaymentMaker {
|
|
16317
|
+
constructor(solanaEndpoint, sourceSecretKey, logger) {
|
|
16318
|
+
this.generateJWT = async ({ paymentRequestId, codeChallenge, accountId }) => {
|
|
16319
|
+
// Solana/Web3.js secretKey is 64 bytes:
|
|
16320
|
+
// first 32 bytes are the private scalar, last 32 are the public key.
|
|
16321
|
+
// JWK expects only the 32-byte private scalar for 'd'
|
|
16322
|
+
const jwk = {
|
|
16323
|
+
kty: 'OKP',
|
|
16324
|
+
crv: 'Ed25519',
|
|
16325
|
+
d: Buffer.from(this.source.secretKey.slice(0, 32)).toString('base64url'),
|
|
16326
|
+
x: Buffer.from(this.source.publicKey.toBytes()).toString('base64url'),
|
|
16327
|
+
};
|
|
16328
|
+
const privateKey = await importJWK(jwk, 'EdDSA');
|
|
16329
|
+
if (!(privateKey instanceof CryptoKey)) {
|
|
16330
|
+
throw new Error('Expected CryptoKey from importJWK');
|
|
16331
|
+
}
|
|
16332
|
+
return generateJWT(this.source.publicKey.toBase58(), privateKey, paymentRequestId || '', codeChallenge || '', accountId);
|
|
16333
|
+
};
|
|
16334
|
+
this.makePayment = async (destinations, memo, _paymentRequestId) => {
|
|
16335
|
+
// Filter to solana chain destinations
|
|
16336
|
+
const solanaDestinations = destinations.filter(d => d.chain === 'solana');
|
|
16337
|
+
if (solanaDestinations.length === 0) {
|
|
16338
|
+
this.logger.debug('SolanaPaymentMaker: No solana destinations found, cannot handle payment');
|
|
16339
|
+
return null; // Cannot handle these destinations
|
|
16340
|
+
}
|
|
16341
|
+
// Pick first solana destination
|
|
16342
|
+
const dest = solanaDestinations[0];
|
|
16343
|
+
const amount = dest.amount;
|
|
16344
|
+
const currency = dest.currency;
|
|
16345
|
+
const receiver = dest.address;
|
|
16346
|
+
if (currency.toUpperCase() !== 'USDC') {
|
|
16347
|
+
throw new PaymentNetworkError('Only USDC currency is supported; received ' + currency);
|
|
16348
|
+
}
|
|
16349
|
+
const receiverKey = new PublicKey(receiver);
|
|
16350
|
+
this.logger.info(`Making payment of ${amount} ${currency} to ${receiver} on Solana from ${this.source.publicKey.toBase58()}`);
|
|
16351
|
+
try {
|
|
16352
|
+
// Check balance before attempting payment
|
|
16353
|
+
const tokenAccountAddress = await getAssociatedTokenAddress(USDC_MINT, this.source.publicKey);
|
|
16354
|
+
const tokenAccount = await getAccount(this.connection, tokenAccountAddress);
|
|
16355
|
+
const balance = new BigNumber$1(tokenAccount.amount.toString()).dividedBy(10 ** 6); // USDC has 6 decimals
|
|
16356
|
+
if (balance.lt(amount)) {
|
|
16357
|
+
this.logger.warn(`Insufficient ${currency} balance for payment. Required: ${amount}, Available: ${balance}`);
|
|
16358
|
+
throw new InsufficientFundsError(currency, amount, balance, 'solana');
|
|
16359
|
+
}
|
|
16360
|
+
// Get the destination token account address (this will be the transactionId)
|
|
16361
|
+
const destinationTokenAccount = await getAssociatedTokenAddress(USDC_MINT, receiverKey);
|
|
16362
|
+
// Increase compute units to handle both memo and token transfer
|
|
16363
|
+
// Memo uses ~6000 CUs, token transfer needs ~6500 CUs
|
|
16364
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
16365
|
+
units: 50000,
|
|
16366
|
+
});
|
|
16367
|
+
const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
|
|
16368
|
+
microLamports: 20000,
|
|
16369
|
+
});
|
|
16370
|
+
const transaction = await createTransfer(this.connection, this.source.publicKey, {
|
|
16371
|
+
amount: amount,
|
|
16372
|
+
recipient: receiverKey,
|
|
16373
|
+
splToken: USDC_MINT,
|
|
16374
|
+
memo,
|
|
16375
|
+
});
|
|
16376
|
+
transaction.add(modifyComputeUnits);
|
|
16377
|
+
transaction.add(addPriorityFee);
|
|
16378
|
+
const transactionSignature = await sendAndConfirmTransaction(this.connection, transaction, [this.source]);
|
|
16379
|
+
// Return transaction signature as transactionId and token account address as transactionSubId
|
|
16380
|
+
return {
|
|
16381
|
+
transactionId: transactionSignature,
|
|
16382
|
+
transactionSubId: destinationTokenAccount.toBase58(),
|
|
16383
|
+
chain: 'solana',
|
|
16384
|
+
currency: 'USDC'
|
|
16385
|
+
};
|
|
16386
|
+
}
|
|
16387
|
+
catch (error) {
|
|
16388
|
+
if (error instanceof InsufficientFundsError || error instanceof PaymentNetworkError) {
|
|
16389
|
+
throw error;
|
|
16390
|
+
}
|
|
16391
|
+
// Wrap other errors in PaymentNetworkError
|
|
16392
|
+
throw new PaymentNetworkError(`Payment failed on Solana network: ${error.message}`, error);
|
|
16393
|
+
}
|
|
16394
|
+
};
|
|
16395
|
+
if (!solanaEndpoint) {
|
|
16396
|
+
throw new Error('Solana endpoint is required');
|
|
16397
|
+
}
|
|
16398
|
+
if (!sourceSecretKey) {
|
|
16399
|
+
throw new Error('Source secret key is required');
|
|
16400
|
+
}
|
|
16401
|
+
this.connection = new Connection(solanaEndpoint, { commitment: 'confirmed' });
|
|
16402
|
+
this.source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
|
|
16403
|
+
this.logger = logger ?? new ConsoleLogger();
|
|
16404
|
+
}
|
|
16405
|
+
getSourceAddress(_params) {
|
|
16406
|
+
return this.source.publicKey.toBase58();
|
|
16407
|
+
}
|
|
16408
|
+
}
|
|
16409
|
+
|
|
16410
|
+
const USDC_CONTRACT_ADDRESS_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // USDC on Base mainnet
|
|
16411
|
+
const USDC_CONTRACT_ADDRESS_BASE_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // USDC on Base Sepolia testnet
|
|
16412
|
+
/**
|
|
16413
|
+
* Get USDC contract address for Base chain by chain ID
|
|
16076
16414
|
* @param chainId - Chain ID (8453 for mainnet, 84532 for sepolia)
|
|
16077
16415
|
* @returns USDC contract address
|
|
16078
16416
|
* @throws Error if chain ID is not supported
|
|
@@ -16146,7 +16484,7 @@ class BasePaymentMaker {
|
|
|
16146
16484
|
getSourceAddress(_params) {
|
|
16147
16485
|
return this.signingClient.account.address;
|
|
16148
16486
|
}
|
|
16149
|
-
async generateJWT({ paymentRequestId, codeChallenge }) {
|
|
16487
|
+
async generateJWT({ paymentRequestId, codeChallenge, accountId }) {
|
|
16150
16488
|
const headerObj = { alg: 'ES256K' };
|
|
16151
16489
|
const payloadObj = {
|
|
16152
16490
|
sub: this.signingClient.account.address,
|
|
@@ -16156,6 +16494,7 @@ class BasePaymentMaker {
|
|
|
16156
16494
|
exp: Math.floor(Date.now() / 1000) + 60 * 60,
|
|
16157
16495
|
...(codeChallenge ? { code_challenge: codeChallenge } : {}),
|
|
16158
16496
|
...(paymentRequestId ? { payment_request_id: paymentRequestId } : {}),
|
|
16497
|
+
...(accountId ? { account_id: accountId } : {}),
|
|
16159
16498
|
};
|
|
16160
16499
|
const header = toBase64Url(JSON.stringify(headerObj));
|
|
16161
16500
|
const payload = toBase64Url(JSON.stringify(payloadObj));
|
|
@@ -16176,7 +16515,18 @@ class BasePaymentMaker {
|
|
|
16176
16515
|
this.logger.info(`Generated ES256K JWT: ${jwt}`);
|
|
16177
16516
|
return jwt;
|
|
16178
16517
|
}
|
|
16179
|
-
async makePayment(
|
|
16518
|
+
async makePayment(destinations, _memo, _paymentRequestId) {
|
|
16519
|
+
// Filter to base chain destinations
|
|
16520
|
+
const baseDestinations = destinations.filter(d => d.chain === 'base');
|
|
16521
|
+
if (baseDestinations.length === 0) {
|
|
16522
|
+
this.logger.debug('BasePaymentMaker: No base destinations found, cannot handle payment');
|
|
16523
|
+
return null; // Cannot handle these destinations
|
|
16524
|
+
}
|
|
16525
|
+
// Pick first base destination
|
|
16526
|
+
const dest = baseDestinations[0];
|
|
16527
|
+
const amount = dest.amount;
|
|
16528
|
+
const currency = dest.currency;
|
|
16529
|
+
const receiver = dest.address;
|
|
16180
16530
|
if (currency.toUpperCase() !== 'USDC') {
|
|
16181
16531
|
throw new PaymentNetworkError('Only USDC currency is supported; received ' + currency);
|
|
16182
16532
|
}
|
|
@@ -16219,7 +16569,12 @@ class BasePaymentMaker {
|
|
|
16219
16569
|
throw new PaymentNetworkError(`Transaction reverted: ${hash}`, new Error('Transaction reverted on chain'));
|
|
16220
16570
|
}
|
|
16221
16571
|
this.logger.info(`Transaction confirmed: ${hash} in block ${receipt.blockNumber}`);
|
|
16222
|
-
|
|
16572
|
+
// Return payment result with chain and currency
|
|
16573
|
+
return {
|
|
16574
|
+
transactionId: hash,
|
|
16575
|
+
chain: 'base',
|
|
16576
|
+
currency: 'USDC'
|
|
16577
|
+
};
|
|
16223
16578
|
}
|
|
16224
16579
|
catch (error) {
|
|
16225
16580
|
if (error instanceof InsufficientFundsError || error instanceof PaymentNetworkError) {
|
|
@@ -16240,228 +16595,22 @@ class SolanaAccount {
|
|
|
16240
16595
|
throw new Error('Source secret key is required');
|
|
16241
16596
|
}
|
|
16242
16597
|
const source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
|
|
16243
|
-
this.
|
|
16244
|
-
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
function toBasicAuth$1(token) {
|
|
16251
|
-
// Basic auth is base64("username:password"), password is blank
|
|
16252
|
-
const b64 = Buffer.from(`${token}:`).toString('base64');
|
|
16253
|
-
return `Basic ${b64}`;
|
|
16254
|
-
}
|
|
16255
|
-
/**
|
|
16256
|
-
* ATXP implementation of viem's LocalAccount interface.
|
|
16257
|
-
* Delegates signing operations to the accounts-x402 API.
|
|
16258
|
-
* Includes properties needed by x402 library for wallet client compatibility.
|
|
16259
|
-
*/
|
|
16260
|
-
class ATXPLocalAccount {
|
|
16261
|
-
constructor(address, origin, token, fetchFn = fetch) {
|
|
16262
|
-
this.address = address;
|
|
16263
|
-
this.origin = origin;
|
|
16264
|
-
this.token = token;
|
|
16265
|
-
this.fetchFn = fetchFn;
|
|
16266
|
-
this.type = 'local';
|
|
16267
|
-
/**
|
|
16268
|
-
* Get public key - required by LocalAccount interface
|
|
16269
|
-
*/
|
|
16270
|
-
this.publicKey = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
16271
|
-
/**
|
|
16272
|
-
* Source - required by LocalAccount interface (set to 'custom')
|
|
16273
|
-
*/
|
|
16274
|
-
this.source = 'custom';
|
|
16275
|
-
// x402 library expects these properties for wallet client compatibility
|
|
16276
|
-
this.account = this; // Self-reference for x402's isSignerWallet check
|
|
16277
|
-
this.chain = { id: 8453 }; // Base mainnet - could make this configurable
|
|
16278
|
-
this.transport = {}; // Empty transport object for x402 compatibility
|
|
16279
|
-
}
|
|
16280
|
-
/**
|
|
16281
|
-
* Fetch the wallet address from the /address endpoint
|
|
16282
|
-
*/
|
|
16283
|
-
static async create(origin, token, fetchFn = fetch) {
|
|
16284
|
-
// The /address endpoint uses Basic auth like other authenticated endpoints
|
|
16285
|
-
// For X402, we need the Ethereum/Base address with USDC currency
|
|
16286
|
-
const url = new URL(`${origin}/address`);
|
|
16287
|
-
url.searchParams.set('network', 'base'); // X402 operates on Base
|
|
16288
|
-
url.searchParams.set('currency', 'USDC'); // Always USDC for X402
|
|
16289
|
-
const response = await fetchFn(url.toString(), {
|
|
16290
|
-
method: 'GET',
|
|
16291
|
-
headers: {
|
|
16292
|
-
'Authorization': toBasicAuth$1(token)
|
|
16293
|
-
}
|
|
16294
|
-
});
|
|
16295
|
-
if (!response.ok) {
|
|
16296
|
-
const errorText = await response.text();
|
|
16297
|
-
throw new Error(`Failed to fetch destination address: ${response.status} ${response.statusText} ${errorText}`);
|
|
16298
|
-
}
|
|
16299
|
-
const data = await response.json();
|
|
16300
|
-
const address = data.address;
|
|
16301
|
-
if (!address) {
|
|
16302
|
-
throw new Error('Address endpoint did not return an address');
|
|
16303
|
-
}
|
|
16304
|
-
// Check that the account is an Ethereum/Base account (required for X402/EVM operations)
|
|
16305
|
-
const network = data.network;
|
|
16306
|
-
if (!network) {
|
|
16307
|
-
throw new Error('Address endpoint did not return a network');
|
|
16308
|
-
}
|
|
16309
|
-
if (network !== 'ethereum' && network !== 'base') {
|
|
16310
|
-
throw new Error(`ATXPLocalAccount requires an Ethereum/Base account, but got ${network} account`);
|
|
16311
|
-
}
|
|
16312
|
-
return new ATXPLocalAccount(address, origin, token, fetchFn);
|
|
16313
|
-
}
|
|
16314
|
-
/**
|
|
16315
|
-
* Sign a typed data structure using EIP-712
|
|
16316
|
-
* This is what x402 library will call for EIP-3009 authorization
|
|
16317
|
-
*/
|
|
16318
|
-
async signTypedData(typedData) {
|
|
16319
|
-
const response = await this.fetchFn(`${this.origin}/sign-typed-data`, {
|
|
16320
|
-
method: 'POST',
|
|
16321
|
-
headers: {
|
|
16322
|
-
'Authorization': toBasicAuth$1(this.token),
|
|
16323
|
-
'Content-Type': 'application/json',
|
|
16324
|
-
},
|
|
16325
|
-
body: JSON.stringify({
|
|
16326
|
-
typedData
|
|
16327
|
-
})
|
|
16328
|
-
});
|
|
16329
|
-
if (!response.ok) {
|
|
16330
|
-
const errorText = await response.text();
|
|
16331
|
-
throw new Error(`Failed to sign typed data: ${response.status} ${response.statusText} ${errorText}`);
|
|
16332
|
-
}
|
|
16333
|
-
const result = await response.json();
|
|
16334
|
-
return result.signature;
|
|
16598
|
+
this.sourcePublicKey = source.publicKey.toBase58();
|
|
16599
|
+
// Format accountId as network:address
|
|
16600
|
+
this.accountId = `solana:${this.sourcePublicKey}`;
|
|
16601
|
+
this.paymentMakers = [
|
|
16602
|
+
new SolanaPaymentMaker(solanaEndpoint, sourceSecretKey)
|
|
16603
|
+
];
|
|
16335
16604
|
}
|
|
16336
16605
|
/**
|
|
16337
|
-
*
|
|
16338
|
-
*/
|
|
16339
|
-
async signMessage(_) {
|
|
16340
|
-
throw new Error('Message signing not implemented for ATXP local account');
|
|
16341
|
-
}
|
|
16342
|
-
/**
|
|
16343
|
-
* Sign a transaction - required by LocalAccount interface but not used for X402
|
|
16606
|
+
* Get sources for this account
|
|
16344
16607
|
*/
|
|
16345
|
-
async
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
// Basic auth is base64("username:password"), password is blank
|
|
16352
|
-
const b64 = Buffer.from(`${token}:`).toString('base64');
|
|
16353
|
-
return `Basic ${b64}`;
|
|
16354
|
-
}
|
|
16355
|
-
function parseConnectionString(connectionString) {
|
|
16356
|
-
const url = new URL(connectionString);
|
|
16357
|
-
const origin = url.origin;
|
|
16358
|
-
const token = url.searchParams.get('connection_token') || '';
|
|
16359
|
-
const accountId = url.searchParams.get('account_id');
|
|
16360
|
-
if (!token) {
|
|
16361
|
-
throw new Error('ATXPAccount: connection string missing connection token');
|
|
16362
|
-
}
|
|
16363
|
-
return { origin, token, accountId };
|
|
16364
|
-
}
|
|
16365
|
-
class ATXPHttpPaymentMaker {
|
|
16366
|
-
constructor(origin, token, fetchFn = fetch) {
|
|
16367
|
-
this.origin = origin;
|
|
16368
|
-
this.token = token;
|
|
16369
|
-
this.fetchFn = fetchFn;
|
|
16370
|
-
}
|
|
16371
|
-
async getSourceAddress(params) {
|
|
16372
|
-
// Call the /address_for_payment endpoint to get the source address for this account
|
|
16373
|
-
const response = await this.fetchFn(`${this.origin}/address_for_payment`, {
|
|
16374
|
-
method: 'POST',
|
|
16375
|
-
headers: {
|
|
16376
|
-
'Authorization': toBasicAuth(this.token),
|
|
16377
|
-
'Content-Type': 'application/json',
|
|
16378
|
-
},
|
|
16379
|
-
body: JSON.stringify({
|
|
16380
|
-
amount: params.amount.toString(),
|
|
16381
|
-
currency: params.currency,
|
|
16382
|
-
receiver: params.receiver,
|
|
16383
|
-
memo: params.memo,
|
|
16384
|
-
}),
|
|
16385
|
-
});
|
|
16386
|
-
if (!response.ok) {
|
|
16387
|
-
const text = await response.text();
|
|
16388
|
-
throw new Error(`ATXPAccount: /address_for_payment failed: ${response.status} ${response.statusText} ${text}`);
|
|
16389
|
-
}
|
|
16390
|
-
const json = await response.json();
|
|
16391
|
-
if (!json?.sourceAddress) {
|
|
16392
|
-
throw new Error('ATXPAccount: /address_for_payment did not return sourceAddress');
|
|
16393
|
-
}
|
|
16394
|
-
return json.sourceAddress;
|
|
16395
|
-
}
|
|
16396
|
-
async makePayment(amount, currency, receiver, memo) {
|
|
16397
|
-
// Make a regular payment via the /pay endpoint
|
|
16398
|
-
const response = await this.fetchFn(`${this.origin}/pay`, {
|
|
16399
|
-
method: 'POST',
|
|
16400
|
-
headers: {
|
|
16401
|
-
'Authorization': toBasicAuth(this.token),
|
|
16402
|
-
'Content-Type': 'application/json',
|
|
16403
|
-
},
|
|
16404
|
-
body: JSON.stringify({
|
|
16405
|
-
amount: amount.toString(),
|
|
16406
|
-
currency,
|
|
16407
|
-
receiver,
|
|
16408
|
-
memo,
|
|
16409
|
-
}),
|
|
16410
|
-
});
|
|
16411
|
-
if (!response.ok) {
|
|
16412
|
-
const text = await response.text();
|
|
16413
|
-
throw new Error(`ATXPAccount: /pay failed: ${response.status} ${response.statusText} ${text}`);
|
|
16414
|
-
}
|
|
16415
|
-
const json = await response.json();
|
|
16416
|
-
if (!json?.txHash) {
|
|
16417
|
-
throw new Error('ATXPAccount: /pay did not return txHash');
|
|
16418
|
-
}
|
|
16419
|
-
return json.txHash;
|
|
16420
|
-
}
|
|
16421
|
-
async generateJWT(params) {
|
|
16422
|
-
const response = await this.fetchFn(`${this.origin}/sign`, {
|
|
16423
|
-
method: 'POST',
|
|
16424
|
-
headers: {
|
|
16425
|
-
'Authorization': toBasicAuth(this.token),
|
|
16426
|
-
'Content-Type': 'application/json',
|
|
16427
|
-
},
|
|
16428
|
-
body: JSON.stringify({
|
|
16429
|
-
paymentRequestId: params.paymentRequestId,
|
|
16430
|
-
codeChallenge: params.codeChallenge,
|
|
16431
|
-
}),
|
|
16432
|
-
});
|
|
16433
|
-
if (!response.ok) {
|
|
16434
|
-
const text = await response.text();
|
|
16435
|
-
throw new Error(`ATXPAccount: /sign failed: ${response.status} ${response.statusText} ${text}`);
|
|
16436
|
-
}
|
|
16437
|
-
const json = await response.json();
|
|
16438
|
-
if (!json?.jwt) {
|
|
16439
|
-
throw new Error('ATXPAccount: /sign did not return jwt');
|
|
16440
|
-
}
|
|
16441
|
-
return json.jwt;
|
|
16442
|
-
}
|
|
16443
|
-
}
|
|
16444
|
-
class ATXPAccount {
|
|
16445
|
-
constructor(connectionString, opts) {
|
|
16446
|
-
const { origin, token, accountId } = parseConnectionString(connectionString);
|
|
16447
|
-
const fetchFn = opts?.fetchFn ?? fetch;
|
|
16448
|
-
const network = opts?.network ?? 'base';
|
|
16449
|
-
// Store for use in X402 payment creation
|
|
16450
|
-
this.origin = origin;
|
|
16451
|
-
this.token = token;
|
|
16452
|
-
this.fetchFn = fetchFn;
|
|
16453
|
-
if (accountId) {
|
|
16454
|
-
this.accountId = `atxp:${accountId}`;
|
|
16455
|
-
}
|
|
16456
|
-
else {
|
|
16457
|
-
this.accountId = `atxp:${crypto$1.randomUUID()}`;
|
|
16458
|
-
}
|
|
16459
|
-
this.paymentMakers = {
|
|
16460
|
-
[network]: new ATXPHttpPaymentMaker(origin, token, fetchFn),
|
|
16461
|
-
};
|
|
16462
|
-
}
|
|
16463
|
-
async getSigner() {
|
|
16464
|
-
return ATXPLocalAccount.create(this.origin, this.token, this.fetchFn);
|
|
16608
|
+
async getSources() {
|
|
16609
|
+
return [{
|
|
16610
|
+
address: this.sourcePublicKey,
|
|
16611
|
+
chain: 'solana',
|
|
16612
|
+
walletType: 'eoa'
|
|
16613
|
+
}];
|
|
16465
16614
|
}
|
|
16466
16615
|
}
|
|
16467
16616
|
|
|
@@ -16545,6 +16694,58 @@ const getWorldChainUSDCAddress = (chainId) => {
|
|
|
16545
16694
|
}
|
|
16546
16695
|
};
|
|
16547
16696
|
|
|
16697
|
+
const USDC_CONTRACT_ADDRESS_POLYGON_MAINNET = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"; // Native USDC on Polygon mainnet
|
|
16698
|
+
// Polygon Mainnet (Chain ID: 137)
|
|
16699
|
+
const POLYGON_MAINNET = {
|
|
16700
|
+
id: 137,
|
|
16701
|
+
name: 'Polygon',
|
|
16702
|
+
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
|
|
16703
|
+
rpcUrls: {
|
|
16704
|
+
default: { http: ['https://polygon-rpc.com'] }
|
|
16705
|
+
},
|
|
16706
|
+
blockExplorers: {
|
|
16707
|
+
default: { name: 'PolygonScan', url: 'https://polygonscan.com' }
|
|
16708
|
+
}
|
|
16709
|
+
};
|
|
16710
|
+
/**
|
|
16711
|
+
* Get Polygon Mainnet configuration with custom RPC URL (e.g., with API key)
|
|
16712
|
+
* @param rpcUrl - Custom RPC URL, e.g., 'https://polygon-mainnet.g.alchemy.com/v2/YOUR_API_KEY'
|
|
16713
|
+
*/
|
|
16714
|
+
const getPolygonMainnetWithRPC = (rpcUrl) => ({
|
|
16715
|
+
...POLYGON_MAINNET,
|
|
16716
|
+
rpcUrls: {
|
|
16717
|
+
default: { http: [rpcUrl] }
|
|
16718
|
+
}
|
|
16719
|
+
});
|
|
16720
|
+
/**
|
|
16721
|
+
* Get Polygon Chain configuration by chain ID
|
|
16722
|
+
* @param chainId - Chain ID (137 for mainnet)
|
|
16723
|
+
* @returns Polygon Chain configuration
|
|
16724
|
+
* @throws Error if chain ID is not supported
|
|
16725
|
+
*/
|
|
16726
|
+
const getPolygonByChainId = (chainId) => {
|
|
16727
|
+
switch (chainId) {
|
|
16728
|
+
case 137:
|
|
16729
|
+
return POLYGON_MAINNET;
|
|
16730
|
+
default:
|
|
16731
|
+
throw new Error(`Unsupported Polygon Chain ID: ${chainId}. Supported chains: 137 (mainnet)`);
|
|
16732
|
+
}
|
|
16733
|
+
};
|
|
16734
|
+
/**
|
|
16735
|
+
* Get USDC contract address for Polygon by chain ID
|
|
16736
|
+
* @param chainId - Chain ID (137 for mainnet)
|
|
16737
|
+
* @returns USDC contract address
|
|
16738
|
+
* @throws Error if chain ID is not supported
|
|
16739
|
+
*/
|
|
16740
|
+
const getPolygonUSDCAddress = (chainId) => {
|
|
16741
|
+
switch (chainId) {
|
|
16742
|
+
case 137:
|
|
16743
|
+
return USDC_CONTRACT_ADDRESS_POLYGON_MAINNET;
|
|
16744
|
+
default:
|
|
16745
|
+
throw new Error(`Unsupported Polygon Chain ID: ${chainId}. Supported chains: 137 (mainnet)`);
|
|
16746
|
+
}
|
|
16747
|
+
};
|
|
16748
|
+
|
|
16548
16749
|
class BaseAccount {
|
|
16549
16750
|
constructor(baseRPCUrl, sourceSecretKey) {
|
|
16550
16751
|
if (!baseRPCUrl) {
|
|
@@ -16554,15 +16755,16 @@ class BaseAccount {
|
|
|
16554
16755
|
throw new Error('Source secret key is required');
|
|
16555
16756
|
}
|
|
16556
16757
|
this.account = privateKeyToAccount(sourceSecretKey);
|
|
16557
|
-
|
|
16758
|
+
// Format accountId as network:address
|
|
16759
|
+
this.accountId = `base:${this.account.address}`;
|
|
16558
16760
|
this.walletClient = createWalletClient({
|
|
16559
16761
|
account: this.account,
|
|
16560
16762
|
chain: base,
|
|
16561
16763
|
transport: http(baseRPCUrl),
|
|
16562
16764
|
});
|
|
16563
|
-
this.paymentMakers =
|
|
16564
|
-
|
|
16565
|
-
|
|
16765
|
+
this.paymentMakers = [
|
|
16766
|
+
new BasePaymentMaker(baseRPCUrl, this.walletClient)
|
|
16767
|
+
];
|
|
16566
16768
|
}
|
|
16567
16769
|
/**
|
|
16568
16770
|
* Get a signer that can be used with the x402 library
|
|
@@ -16572,7 +16774,17 @@ class BaseAccount {
|
|
|
16572
16774
|
// Return the viem account directly - it implements LocalAccount interface
|
|
16573
16775
|
return this.account;
|
|
16574
16776
|
}
|
|
16777
|
+
/**
|
|
16778
|
+
* Get sources for this account
|
|
16779
|
+
*/
|
|
16780
|
+
async getSources() {
|
|
16781
|
+
return [{
|
|
16782
|
+
address: this.account.address,
|
|
16783
|
+
chain: 'base',
|
|
16784
|
+
walletType: 'eoa'
|
|
16785
|
+
}];
|
|
16786
|
+
}
|
|
16575
16787
|
}
|
|
16576
16788
|
|
|
16577
|
-
export { ATXPAccount, ATXPLocalAccount, BaseAccount, BasePaymentMaker, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, PaymentNetworkError, SolanaAccount, SolanaPaymentMaker, USDC_CONTRACT_ADDRESS_BASE, USDC_CONTRACT_ADDRESS_BASE_SEPOLIA, USDC_CONTRACT_ADDRESS_WORLD_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_SEPOLIA, ValidateTransferError, WORLD_CHAIN_MAINNET, WORLD_CHAIN_SEPOLIA, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport, getBaseUSDCAddress, getWorldChainByChainId, getWorldChainMainnetWithRPC, getWorldChainSepoliaWithRPC, getWorldChainUSDCAddress };
|
|
16789
|
+
export { ATXPAccount, ATXPDestinationMaker, ATXPLocalAccount, BaseAccount, BasePaymentMaker, DEFAULT_CLIENT_CONFIG, InsufficientFundsError, OAuthAuthenticationRequiredError, OAuthClient, POLYGON_MAINNET, PassthroughDestinationMaker, PaymentNetworkError, SolanaAccount, SolanaPaymentMaker, USDC_CONTRACT_ADDRESS_BASE, USDC_CONTRACT_ADDRESS_BASE_SEPOLIA, USDC_CONTRACT_ADDRESS_POLYGON_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_SEPOLIA, ValidateTransferError, WORLD_CHAIN_MAINNET, WORLD_CHAIN_SEPOLIA, atxpClient, atxpFetch, buildClientConfig, buildStreamableTransport, getBaseUSDCAddress, getPolygonByChainId, getPolygonMainnetWithRPC, getPolygonUSDCAddress, getWorldChainByChainId, getWorldChainMainnetWithRPC, getWorldChainSepoliaWithRPC, getWorldChainUSDCAddress };
|
|
16578
16790
|
//# sourceMappingURL=index.js.map
|