uvd-x402-sdk 2.11.1 → 2.13.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/adapters/index.d.mts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/backend/index.d.mts +1 -1
- package/dist/backend/index.d.ts +1 -1
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/index.mjs.map +1 -1
- package/dist/{index-C60c_e5z.d.mts → index-C6Vxnneo.d.mts} +1 -1
- package/dist/{index-VIOUicmO.d.ts → index-DmJGKD9r.d.ts} +1 -1
- package/dist/{index-D-dO_FoP.d.mts → index-fIhvHqCQ.d.mts} +18 -22
- package/dist/{index-D-dO_FoP.d.ts → index-fIhvHqCQ.d.ts} +18 -22
- package/dist/index.d.mts +56 -2
- package/dist/index.d.ts +56 -2
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -1
- package/dist/index.mjs.map +1 -1
- package/dist/providers/algorand/index.d.mts +11 -5
- package/dist/providers/algorand/index.d.ts +11 -5
- package/dist/providers/algorand/index.js +142 -27
- package/dist/providers/algorand/index.js.map +1 -1
- package/dist/providers/algorand/index.mjs +142 -27
- package/dist/providers/algorand/index.mjs.map +1 -1
- package/dist/providers/evm/index.d.mts +1 -1
- package/dist/providers/evm/index.d.ts +1 -1
- package/dist/providers/evm/index.js.map +1 -1
- package/dist/providers/evm/index.mjs.map +1 -1
- package/dist/providers/near/index.d.mts +1 -1
- package/dist/providers/near/index.d.ts +1 -1
- package/dist/providers/near/index.js.map +1 -1
- package/dist/providers/near/index.mjs.map +1 -1
- package/dist/providers/solana/index.d.mts +1 -1
- package/dist/providers/solana/index.d.ts +1 -1
- package/dist/providers/solana/index.js.map +1 -1
- package/dist/providers/solana/index.mjs.map +1 -1
- package/dist/providers/stellar/index.d.mts +1 -1
- package/dist/providers/stellar/index.d.ts +1 -1
- package/dist/providers/stellar/index.js.map +1 -1
- package/dist/providers/stellar/index.mjs.map +1 -1
- package/dist/react/index.d.mts +3 -3
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs.map +1 -1
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/facilitator.ts +106 -0
- package/src/index.ts +4 -0
- package/src/providers/algorand/index.ts +122 -32
- package/src/types/index.ts +18 -22
|
@@ -40,6 +40,7 @@ import type {
|
|
|
40
40
|
import { X402Error } from '../../types';
|
|
41
41
|
import { getChainByName } from '../../chains';
|
|
42
42
|
import { chainToCAIP2 } from '../../utils';
|
|
43
|
+
import { getFacilitatorAddress } from '../../facilitator';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Browser-compatible base64 encoding for Uint8Array
|
|
@@ -318,11 +319,13 @@ export class AlgorandProvider implements WalletAdapter {
|
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
/**
|
|
321
|
-
* Create Algorand
|
|
322
|
+
* Create Algorand atomic group payment (GoPlausible x402-avm spec)
|
|
322
323
|
*
|
|
323
|
-
* Transaction structure:
|
|
324
|
-
*
|
|
325
|
-
*
|
|
324
|
+
* Transaction structure (atomic group):
|
|
325
|
+
* - Transaction 0: Fee payment (UNSIGNED) - facilitator -> facilitator, covers all fees
|
|
326
|
+
* - Transaction 1: ASA transfer (SIGNED) - client -> merchant
|
|
327
|
+
*
|
|
328
|
+
* The facilitator signs transaction 0 and submits the complete atomic group.
|
|
326
329
|
*/
|
|
327
330
|
async signPayment(paymentInfo: PaymentInfo, chainConfig: ChainConfig): Promise<string> {
|
|
328
331
|
await loadAlgorandDeps();
|
|
@@ -341,6 +344,19 @@ export class AlgorandProvider implements WalletAdapter {
|
|
|
341
344
|
const recipient = paymentInfo.recipients?.algorand || paymentInfo.recipient;
|
|
342
345
|
const assetId = parseInt(chainConfig.usdc.address, 10);
|
|
343
346
|
|
|
347
|
+
// Get facilitator address (fee payer) - automatically from SDK config
|
|
348
|
+
// Priority: paymentInfo.facilitator > SDK default for chain
|
|
349
|
+
const chainName = chainConfig?.name || 'algorand';
|
|
350
|
+
const facilitatorAddress =
|
|
351
|
+
paymentInfo.facilitator || getFacilitatorAddress(chainName, 'algorand');
|
|
352
|
+
|
|
353
|
+
if (!facilitatorAddress) {
|
|
354
|
+
throw new X402Error(
|
|
355
|
+
'Facilitator address not configured for Algorand',
|
|
356
|
+
'PAYMENT_FAILED'
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
344
360
|
// Parse amount (6 decimals for USDC)
|
|
345
361
|
const amount = Math.floor(parseFloat(paymentInfo.amount) * 1_000_000);
|
|
346
362
|
|
|
@@ -348,46 +364,92 @@ export class AlgorandProvider implements WalletAdapter {
|
|
|
348
364
|
// Get suggested transaction parameters
|
|
349
365
|
const suggestedParams = await algodClient.getTransactionParams().do();
|
|
350
366
|
|
|
351
|
-
//
|
|
367
|
+
// Transaction 0: Fee payment (facilitator -> facilitator, 0 amount)
|
|
368
|
+
// This transaction pays fees for both txns in the group (fee pooling)
|
|
352
369
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
353
|
-
const
|
|
370
|
+
const feeTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
|
|
371
|
+
sender: facilitatorAddress,
|
|
372
|
+
receiver: facilitatorAddress, // self-transfer
|
|
373
|
+
amount: 0,
|
|
374
|
+
suggestedParams: {
|
|
375
|
+
...suggestedParams,
|
|
376
|
+
fee: 2000, // Covers both transactions (1000 each)
|
|
377
|
+
flatFee: true,
|
|
378
|
+
},
|
|
379
|
+
} as any);
|
|
380
|
+
|
|
381
|
+
// Transaction 1: ASA transfer (client -> merchant)
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
383
|
+
const paymentTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
|
|
354
384
|
sender: this.address,
|
|
355
385
|
receiver: recipient,
|
|
356
386
|
amount: BigInt(amount),
|
|
357
387
|
assetIndex: assetId,
|
|
358
|
-
suggestedParams:
|
|
388
|
+
suggestedParams: {
|
|
389
|
+
...suggestedParams,
|
|
390
|
+
fee: 0, // Fee paid by transaction 0
|
|
391
|
+
flatFee: true,
|
|
392
|
+
},
|
|
359
393
|
note: new TextEncoder().encode('x402 payment via uvd-x402-sdk'),
|
|
360
394
|
} as any);
|
|
361
395
|
|
|
362
|
-
//
|
|
363
|
-
|
|
396
|
+
// Assign group ID to both transactions (creates atomic group)
|
|
397
|
+
const txnGroup = algosdk.assignGroupID([feeTxn, paymentTxn]);
|
|
398
|
+
|
|
399
|
+
// Encode fee transaction (UNSIGNED - facilitator will sign)
|
|
400
|
+
const unsignedFeeTxnBytes = algosdk.encodeUnsignedTransaction(txnGroup[0]);
|
|
401
|
+
const unsignedFeeTxnBase64 = uint8ArrayToBase64(unsignedFeeTxnBytes);
|
|
402
|
+
|
|
403
|
+
// Sign the payment transaction (index 1) with the active wallet
|
|
404
|
+
let signedPaymentTxnBytes: Uint8Array;
|
|
364
405
|
|
|
365
406
|
if (this.walletType === 'lute' && this.luteWallet) {
|
|
366
407
|
// Lute uses signTxns with base64 encoded transactions
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
|
|
408
|
+
// For atomic groups, pass both txns but only sign the one we control
|
|
409
|
+
const feeTxnBase64 = uint8ArrayToBase64(txnGroup[0].toByte());
|
|
410
|
+
const paymentTxnBase64 = uint8ArrayToBase64(txnGroup[1].toByte());
|
|
411
|
+
|
|
412
|
+
// Sign only the payment transaction (index 1), leave fee txn unsigned
|
|
413
|
+
const signedTxns = await this.luteWallet.signTxns([
|
|
414
|
+
{ txn: feeTxnBase64, signers: [] }, // Don't sign - facilitator will
|
|
415
|
+
{ txn: paymentTxnBase64 }, // Sign this one
|
|
416
|
+
]);
|
|
417
|
+
|
|
418
|
+
if (!signedTxns || signedTxns.length < 2 || !signedTxns[1]) {
|
|
370
419
|
throw new X402Error('No signed transaction returned', 'SIGNATURE_REJECTED');
|
|
371
420
|
}
|
|
372
|
-
|
|
373
|
-
|
|
421
|
+
|
|
422
|
+
// Get the signed payment transaction (index 1)
|
|
423
|
+
const signedResult = signedTxns[1];
|
|
424
|
+
signedPaymentTxnBytes = this.decodeSignedTxn(signedResult);
|
|
374
425
|
} else if (this.walletType === 'pera' && this.peraWallet) {
|
|
375
426
|
// Pera uses signTransaction with transaction objects
|
|
376
|
-
|
|
377
|
-
|
|
427
|
+
// For atomic groups, pass both txns but only sign the one we control
|
|
428
|
+
const signedTxns = await this.peraWallet.signTransaction([
|
|
429
|
+
[
|
|
430
|
+
{ txn: txnGroup[0], signers: [] }, // Don't sign - facilitator will
|
|
431
|
+
{ txn: txnGroup[1] }, // Sign this one
|
|
432
|
+
],
|
|
433
|
+
]);
|
|
434
|
+
|
|
435
|
+
if (!signedTxns || signedTxns.length < 2 || !signedTxns[1]) {
|
|
378
436
|
throw new X402Error('No signed transaction returned', 'SIGNATURE_REJECTED');
|
|
379
437
|
}
|
|
380
|
-
|
|
438
|
+
|
|
439
|
+
signedPaymentTxnBytes = signedTxns[1];
|
|
381
440
|
} else {
|
|
382
441
|
throw new X402Error('No wallet available for signing', 'WALLET_NOT_CONNECTED');
|
|
383
442
|
}
|
|
384
443
|
|
|
444
|
+
const signedPaymentTxnBase64 = uint8ArrayToBase64(signedPaymentTxnBytes);
|
|
445
|
+
|
|
446
|
+
// Build payload following GoPlausible x402-avm spec
|
|
385
447
|
const payload: AlgorandPaymentPayload = {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
448
|
+
paymentIndex: 1, // Index of the payment transaction in the group
|
|
449
|
+
paymentGroup: [
|
|
450
|
+
unsignedFeeTxnBase64, // Transaction 0: unsigned fee tx
|
|
451
|
+
signedPaymentTxnBase64, // Transaction 1: signed payment tx
|
|
452
|
+
],
|
|
391
453
|
};
|
|
392
454
|
|
|
393
455
|
return JSON.stringify(payload);
|
|
@@ -408,6 +470,32 @@ export class AlgorandProvider implements WalletAdapter {
|
|
|
408
470
|
}
|
|
409
471
|
}
|
|
410
472
|
|
|
473
|
+
/**
|
|
474
|
+
* Decode signed transaction from wallet response (handles various formats)
|
|
475
|
+
*/
|
|
476
|
+
private decodeSignedTxn(signedResult: unknown): Uint8Array {
|
|
477
|
+
if (signedResult instanceof Uint8Array) {
|
|
478
|
+
return signedResult;
|
|
479
|
+
} else if (typeof signedResult === 'string') {
|
|
480
|
+
// Try to decode as base64
|
|
481
|
+
try {
|
|
482
|
+
return Uint8Array.from(atob(signedResult), c => c.charCodeAt(0));
|
|
483
|
+
} catch {
|
|
484
|
+
// If standard base64 fails, try URL-safe base64
|
|
485
|
+
const standardBase64 = signedResult.replace(/-/g, '+').replace(/_/g, '/');
|
|
486
|
+
return Uint8Array.from(atob(standardBase64), c => c.charCodeAt(0));
|
|
487
|
+
}
|
|
488
|
+
} else if (ArrayBuffer.isView(signedResult)) {
|
|
489
|
+
return new Uint8Array(
|
|
490
|
+
(signedResult as ArrayBufferView).buffer,
|
|
491
|
+
(signedResult as ArrayBufferView).byteOffset,
|
|
492
|
+
(signedResult as ArrayBufferView).byteLength
|
|
493
|
+
);
|
|
494
|
+
} else {
|
|
495
|
+
throw new X402Error('Unexpected signed transaction format', 'PAYMENT_FAILED');
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
411
499
|
/**
|
|
412
500
|
* Encode Algorand payment as X-PAYMENT header
|
|
413
501
|
*
|
|
@@ -423,17 +511,19 @@ export class AlgorandProvider implements WalletAdapter {
|
|
|
423
511
|
): string {
|
|
424
512
|
const payload = JSON.parse(paymentPayload) as AlgorandPaymentPayload;
|
|
425
513
|
|
|
426
|
-
//
|
|
427
|
-
|
|
514
|
+
// Determine network name for x402
|
|
515
|
+
// Use "algorand-mainnet" or "algorand-testnet" format for facilitator
|
|
516
|
+
let networkName: string;
|
|
517
|
+
if (chainConfig?.name === 'algorand-testnet') {
|
|
518
|
+
networkName = 'algorand-testnet';
|
|
519
|
+
} else {
|
|
520
|
+
networkName = 'algorand-mainnet'; // Default to mainnet
|
|
521
|
+
}
|
|
428
522
|
|
|
429
|
-
// Build the payload data
|
|
523
|
+
// Build the payload data (GoPlausible x402-avm spec)
|
|
430
524
|
const payloadData = {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
amount: payload.amount,
|
|
434
|
-
assetId: payload.assetId,
|
|
435
|
-
signedTxn: payload.signedTxn,
|
|
436
|
-
...(payload.note && { note: payload.note }),
|
|
525
|
+
paymentIndex: payload.paymentIndex,
|
|
526
|
+
paymentGroup: payload.paymentGroup,
|
|
437
527
|
};
|
|
438
528
|
|
|
439
529
|
// Format in x402 standard format (v1 or v2)
|
|
@@ -447,7 +537,7 @@ export class AlgorandProvider implements WalletAdapter {
|
|
|
447
537
|
: {
|
|
448
538
|
x402Version: 1 as const,
|
|
449
539
|
scheme: 'exact' as const,
|
|
450
|
-
network: networkName,
|
|
540
|
+
network: networkName,
|
|
451
541
|
payload: payloadData,
|
|
452
542
|
};
|
|
453
543
|
|
package/src/types/index.ts
CHANGED
|
@@ -345,23 +345,21 @@ export interface NEARPaymentPayload {
|
|
|
345
345
|
/**
|
|
346
346
|
* Algorand payment payload (atomic transaction group)
|
|
347
347
|
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
348
|
+
* Follows the GoPlausible x402-avm spec for atomic groups:
|
|
349
|
+
* - Transaction 0: Fee payment (UNSIGNED) - facilitator -> facilitator, covers all fees
|
|
350
|
+
* - Transaction 1: ASA transfer (SIGNED) - client -> merchant
|
|
351
|
+
*
|
|
352
|
+
* The facilitator signs transaction 0 and submits the complete atomic group.
|
|
351
353
|
*/
|
|
352
354
|
export interface AlgorandPaymentPayload {
|
|
353
|
-
/**
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
/** Base64-encoded signed transaction bytes */
|
|
362
|
-
signedTxn: string;
|
|
363
|
-
/** Optional note field */
|
|
364
|
-
note?: string;
|
|
355
|
+
/** Index of the payment transaction in the group (always 1) */
|
|
356
|
+
paymentIndex: number;
|
|
357
|
+
/**
|
|
358
|
+
* Array of base64-encoded msgpack transactions forming the atomic group:
|
|
359
|
+
* - [0]: Unsigned fee transaction (facilitator signs)
|
|
360
|
+
* - [1]: Signed ASA transfer (client signed)
|
|
361
|
+
*/
|
|
362
|
+
paymentGroup: string[];
|
|
365
363
|
}
|
|
366
364
|
|
|
367
365
|
/**
|
|
@@ -497,15 +495,13 @@ export interface X402NEARPayload {
|
|
|
497
495
|
}
|
|
498
496
|
|
|
499
497
|
/**
|
|
500
|
-
* Algorand-specific payload in x402 header
|
|
498
|
+
* Algorand-specific payload in x402 header (atomic group format)
|
|
501
499
|
*/
|
|
502
500
|
export interface X402AlgorandPayload {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
signedTxn: string;
|
|
508
|
-
note?: string;
|
|
501
|
+
/** Index of the payment transaction in the group (always 1) */
|
|
502
|
+
paymentIndex: number;
|
|
503
|
+
/** Array of base64-encoded msgpack transactions */
|
|
504
|
+
paymentGroup: string[];
|
|
509
505
|
}
|
|
510
506
|
|
|
511
507
|
/**
|