uvd-x402-sdk 2.10.1 → 2.12.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.
Files changed (49) hide show
  1. package/dist/adapters/index.d.mts +1 -1
  2. package/dist/adapters/index.d.ts +1 -1
  3. package/dist/adapters/index.js.map +1 -1
  4. package/dist/adapters/index.mjs.map +1 -1
  5. package/dist/backend/index.d.mts +1 -1
  6. package/dist/backend/index.d.ts +1 -1
  7. package/dist/backend/index.js.map +1 -1
  8. package/dist/backend/index.mjs.map +1 -1
  9. package/dist/{index-C60c_e5z.d.mts → index-C6Vxnneo.d.mts} +1 -1
  10. package/dist/{index-VIOUicmO.d.ts → index-DmJGKD9r.d.ts} +1 -1
  11. package/dist/{index-D-dO_FoP.d.mts → index-fIhvHqCQ.d.mts} +18 -22
  12. package/dist/{index-D-dO_FoP.d.ts → index-fIhvHqCQ.d.ts} +18 -22
  13. package/dist/index.d.mts +2 -2
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/providers/algorand/index.d.mts +42 -15
  18. package/dist/providers/algorand/index.d.ts +42 -15
  19. package/dist/providers/algorand/index.js +198 -32
  20. package/dist/providers/algorand/index.js.map +1 -1
  21. package/dist/providers/algorand/index.mjs +198 -32
  22. package/dist/providers/algorand/index.mjs.map +1 -1
  23. package/dist/providers/evm/index.d.mts +1 -1
  24. package/dist/providers/evm/index.d.ts +1 -1
  25. package/dist/providers/evm/index.js.map +1 -1
  26. package/dist/providers/evm/index.mjs.map +1 -1
  27. package/dist/providers/near/index.d.mts +1 -1
  28. package/dist/providers/near/index.d.ts +1 -1
  29. package/dist/providers/near/index.js.map +1 -1
  30. package/dist/providers/near/index.mjs.map +1 -1
  31. package/dist/providers/solana/index.d.mts +1 -1
  32. package/dist/providers/solana/index.d.ts +1 -1
  33. package/dist/providers/solana/index.js.map +1 -1
  34. package/dist/providers/solana/index.mjs.map +1 -1
  35. package/dist/providers/stellar/index.d.mts +1 -1
  36. package/dist/providers/stellar/index.d.ts +1 -1
  37. package/dist/providers/stellar/index.js.map +1 -1
  38. package/dist/providers/stellar/index.mjs.map +1 -1
  39. package/dist/react/index.d.mts +3 -3
  40. package/dist/react/index.d.ts +3 -3
  41. package/dist/react/index.js.map +1 -1
  42. package/dist/react/index.mjs.map +1 -1
  43. package/dist/utils/index.d.mts +1 -1
  44. package/dist/utils/index.d.ts +1 -1
  45. package/dist/utils/index.js.map +1 -1
  46. package/dist/utils/index.mjs.map +1 -1
  47. package/package.json +6 -1
  48. package/src/providers/algorand/index.ts +261 -40
  49. package/src/types/index.ts +18 -22
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * uvd-x402-sdk - Algorand Provider
3
3
  *
4
- * Provides wallet connection and payment creation for Algorand via Pera Wallet.
4
+ * Provides wallet connection and payment creation for Algorand.
5
+ * Supports both Lute Wallet (desktop browser extension) and Pera Wallet (mobile).
5
6
  * Uses ASA (Algorand Standard Assets) transfers for USDC payments.
6
7
  *
8
+ * Wallet Priority:
9
+ * 1. Lute Wallet - Desktop browser extension (preferred for desktop)
10
+ * 2. Pera Wallet - Mobile via WalletConnect (fallback/mobile)
11
+ *
7
12
  * USDC ASA IDs:
8
13
  * - Mainnet: 31566704
9
14
  * - Testnet: 10458941
@@ -15,7 +20,7 @@
15
20
  *
16
21
  * const algorand = new AlgorandProvider();
17
22
  *
18
- * // Connect to Pera Wallet
23
+ * // Connect to Lute (desktop) or Pera (mobile) automatically
19
24
  * const address = await algorand.connect();
20
25
  *
21
26
  * // Create Algorand payment
@@ -50,27 +55,60 @@ function uint8ArrayToBase64(bytes: Uint8Array): string {
50
55
  // Lazy import Algorand dependencies
51
56
  let algosdk: typeof import('algosdk') | null = null;
52
57
  let PeraWalletConnect: typeof import('@perawallet/connect').PeraWalletConnect | null = null;
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ let LuteConnect: any = null;
53
60
 
54
61
  async function loadAlgorandDeps() {
55
62
  if (!algosdk) {
56
63
  algosdk = await import('algosdk');
57
64
  }
65
+ }
66
+
67
+ async function loadPeraWallet() {
58
68
  if (!PeraWalletConnect) {
59
69
  const peraModule = await import('@perawallet/connect');
60
70
  PeraWalletConnect = peraModule.PeraWalletConnect;
61
71
  }
62
72
  }
63
73
 
74
+ async function loadLuteWallet() {
75
+ if (!LuteConnect) {
76
+ try {
77
+ const luteModule = await import('lute-connect');
78
+ LuteConnect = luteModule.default;
79
+ } catch {
80
+ // Lute not installed, will fall back to Pera
81
+ LuteConnect = null;
82
+ }
83
+ }
84
+ }
85
+
64
86
  /**
65
- * AlgorandProvider - Wallet adapter for Algorand via Pera Wallet
87
+ * Check if Lute wallet extension is installed
88
+ */
89
+ function isLuteAvailable(): boolean {
90
+ if (typeof window === 'undefined') return false;
91
+ // Lute injects itself into window.algorand or window.lute
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ const win = window as any;
94
+ return !!(win.algorand || win.lute);
95
+ }
96
+
97
+ /**
98
+ * AlgorandProvider - Wallet adapter for Algorand
66
99
  *
67
- * Supports both mainnet and testnet through chain configuration.
100
+ * Supports Lute Wallet (desktop) and Pera Wallet (mobile).
101
+ * Automatically detects and uses the best available wallet.
68
102
  */
69
103
  export class AlgorandProvider implements WalletAdapter {
70
- readonly id = 'pera';
71
- readonly name = 'Pera Wallet';
104
+ readonly id = 'algorand';
105
+ readonly name = 'Algorand Wallet';
72
106
  readonly networkType = 'algorand' as const;
73
107
 
108
+ // Active wallet type
109
+ private walletType: 'lute' | 'pera' | null = null;
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ private luteWallet: any = null;
74
112
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
113
  private peraWallet: any = null;
76
114
  private address: string | null = null;
@@ -78,19 +116,91 @@ export class AlgorandProvider implements WalletAdapter {
78
116
  private algodClients: Map<string, any> = new Map();
79
117
 
80
118
  /**
81
- * Check if Pera Wallet is available
82
- * Note: Pera works as a WalletConnect modal, so it's always "available"
119
+ * Check if any Algorand wallet is available
120
+ * Returns true if Lute extension is installed OR we can use Pera (always available via WalletConnect)
83
121
  */
84
122
  isAvailable(): boolean {
85
123
  return typeof window !== 'undefined';
86
124
  }
87
125
 
88
126
  /**
89
- * Connect to Pera Wallet
127
+ * Get the name of the currently connected wallet
128
+ */
129
+ getWalletName(): string {
130
+ if (this.walletType === 'lute') return 'Lute Wallet';
131
+ if (this.walletType === 'pera') return 'Pera Wallet';
132
+ return 'Algorand Wallet';
133
+ }
134
+
135
+ /**
136
+ * Connect to Algorand wallet
137
+ * Priority: Lute (desktop extension) > Pera (mobile via WalletConnect)
90
138
  */
91
139
  async connect(_chainName?: string): Promise<string> {
92
140
  await loadAlgorandDeps();
93
141
 
142
+ // Try Lute first (better desktop UX)
143
+ if (isLuteAvailable()) {
144
+ try {
145
+ return await this.connectLute();
146
+ } catch (error) {
147
+ // Lute failed, try Pera
148
+ console.warn('Lute connection failed, falling back to Pera:', error);
149
+ }
150
+ }
151
+
152
+ // Fall back to Pera (mobile/WalletConnect)
153
+ return await this.connectPera();
154
+ }
155
+
156
+ /**
157
+ * Connect to Lute Wallet (desktop browser extension)
158
+ */
159
+ private async connectLute(): Promise<string> {
160
+ await loadLuteWallet();
161
+
162
+ if (!LuteConnect) {
163
+ throw new X402Error('Lute Wallet SDK not available', 'WALLET_NOT_FOUND');
164
+ }
165
+
166
+ try {
167
+ this.luteWallet = new LuteConnect('402milly');
168
+
169
+ // Get Algorand genesis ID for mainnet
170
+ const genesisId = 'mainnet-v1.0';
171
+
172
+ // Connect and get accounts
173
+ const accounts = await this.luteWallet.connect(genesisId);
174
+
175
+ if (!accounts || accounts.length === 0) {
176
+ throw new X402Error('No accounts returned from Lute Wallet', 'WALLET_CONNECTION_REJECTED');
177
+ }
178
+
179
+ this.address = accounts[0];
180
+ this.walletType = 'lute';
181
+
182
+ return accounts[0];
183
+ } catch (error: unknown) {
184
+ if (error instanceof X402Error) throw error;
185
+ if (error instanceof Error) {
186
+ if (error.message.includes('rejected') || error.message.includes('cancelled')) {
187
+ throw new X402Error('Connection rejected by user', 'WALLET_CONNECTION_REJECTED');
188
+ }
189
+ }
190
+ throw new X402Error(
191
+ `Failed to connect Lute Wallet: ${error instanceof Error ? error.message : 'Unknown error'}`,
192
+ 'UNKNOWN_ERROR',
193
+ error
194
+ );
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Connect to Pera Wallet (mobile via WalletConnect)
200
+ */
201
+ private async connectPera(): Promise<string> {
202
+ await loadPeraWallet();
203
+
94
204
  if (!PeraWalletConnect) {
95
205
  throw new X402Error('Failed to load Pera Wallet SDK', 'WALLET_NOT_FOUND');
96
206
  }
@@ -104,6 +214,7 @@ export class AlgorandProvider implements WalletAdapter {
104
214
 
105
215
  if (accounts.length > 0) {
106
216
  this.address = accounts[0];
217
+ this.walletType = 'pera';
107
218
  return accounts[0];
108
219
  }
109
220
 
@@ -115,10 +226,12 @@ export class AlgorandProvider implements WalletAdapter {
115
226
  }
116
227
 
117
228
  this.address = newAccounts[0];
229
+ this.walletType = 'pera';
118
230
 
119
231
  // Set up disconnect handler
120
232
  this.peraWallet.connector?.on('disconnect', () => {
121
233
  this.address = null;
234
+ this.walletType = null;
122
235
  });
123
236
 
124
237
  return newAccounts[0];
@@ -137,18 +250,27 @@ export class AlgorandProvider implements WalletAdapter {
137
250
  }
138
251
 
139
252
  /**
140
- * Disconnect from Pera Wallet
253
+ * Disconnect from wallet
141
254
  */
142
255
  async disconnect(): Promise<void> {
143
- if (this.peraWallet) {
256
+ if (this.walletType === 'lute' && this.luteWallet) {
257
+ try {
258
+ // Lute doesn't have a disconnect method, just clear state
259
+ } catch {
260
+ // Ignore disconnect errors
261
+ }
262
+ this.luteWallet = null;
263
+ }
264
+ if (this.walletType === 'pera' && this.peraWallet) {
144
265
  try {
145
266
  await this.peraWallet.disconnect();
146
267
  } catch {
147
268
  // Ignore disconnect errors
148
269
  }
270
+ this.peraWallet = null;
149
271
  }
150
- this.peraWallet = null;
151
272
  this.address = null;
273
+ this.walletType = null;
152
274
  this.algodClients.clear();
153
275
  }
154
276
 
@@ -196,16 +318,18 @@ export class AlgorandProvider implements WalletAdapter {
196
318
  }
197
319
 
198
320
  /**
199
- * Create Algorand ASA transfer payment
321
+ * Create Algorand atomic group payment (GoPlausible x402-avm spec)
200
322
  *
201
- * Transaction structure:
202
- * 1. ASA Transfer from user to recipient
203
- * 2. Facilitator pays transaction fees
323
+ * Transaction structure (atomic group):
324
+ * - Transaction 0: Fee payment (UNSIGNED) - facilitator -> facilitator, covers all fees
325
+ * - Transaction 1: ASA transfer (SIGNED) - client -> merchant
326
+ *
327
+ * The facilitator signs transaction 0 and submits the complete atomic group.
204
328
  */
205
329
  async signPayment(paymentInfo: PaymentInfo, chainConfig: ChainConfig): Promise<string> {
206
330
  await loadAlgorandDeps();
207
331
 
208
- if (!this.peraWallet || !this.address) {
332
+ if (!this.address || !this.walletType) {
209
333
  throw new X402Error('Wallet not connected', 'WALLET_NOT_CONNECTED');
210
334
  }
211
335
 
@@ -219,6 +343,15 @@ export class AlgorandProvider implements WalletAdapter {
219
343
  const recipient = paymentInfo.recipients?.algorand || paymentInfo.recipient;
220
344
  const assetId = parseInt(chainConfig.usdc.address, 10);
221
345
 
346
+ // Get facilitator address (fee payer)
347
+ const facilitatorAddress = paymentInfo.facilitator;
348
+ if (!facilitatorAddress) {
349
+ throw new X402Error(
350
+ 'Facilitator address required for Algorand payments. Set paymentInfo.facilitator',
351
+ 'PAYMENT_FAILED'
352
+ );
353
+ }
354
+
222
355
  // Parse amount (6 decimals for USDC)
223
356
  const amount = Math.floor(parseFloat(paymentInfo.amount) * 1_000_000);
224
357
 
@@ -226,32 +359,92 @@ export class AlgorandProvider implements WalletAdapter {
226
359
  // Get suggested transaction parameters
227
360
  const suggestedParams = await algodClient.getTransactionParams().do();
228
361
 
229
- // Create ASA transfer transaction
362
+ // Transaction 0: Fee payment (facilitator -> facilitator, 0 amount)
363
+ // This transaction pays fees for both txns in the group (fee pooling)
230
364
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
231
- const txn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
365
+ const feeTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
366
+ sender: facilitatorAddress,
367
+ receiver: facilitatorAddress, // self-transfer
368
+ amount: 0,
369
+ suggestedParams: {
370
+ ...suggestedParams,
371
+ fee: 2000, // Covers both transactions (1000 each)
372
+ flatFee: true,
373
+ },
374
+ } as any);
375
+
376
+ // Transaction 1: ASA transfer (client -> merchant)
377
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
378
+ const paymentTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
232
379
  sender: this.address,
233
380
  receiver: recipient,
234
381
  amount: BigInt(amount),
235
382
  assetIndex: assetId,
236
- suggestedParams: suggestedParams,
383
+ suggestedParams: {
384
+ ...suggestedParams,
385
+ fee: 0, // Fee paid by transaction 0
386
+ flatFee: true,
387
+ },
237
388
  note: new TextEncoder().encode('x402 payment via uvd-x402-sdk'),
238
389
  } as any);
239
390
 
240
- // Sign with Pera Wallet
241
- const signedTxns = await this.peraWallet.signTransaction([[{ txn }]]);
391
+ // Assign group ID to both transactions (creates atomic group)
392
+ const txnGroup = algosdk.assignGroupID([feeTxn, paymentTxn]);
393
+
394
+ // Encode fee transaction (UNSIGNED - facilitator will sign)
395
+ const unsignedFeeTxnBytes = algosdk.encodeUnsignedTransaction(txnGroup[0]);
396
+ const unsignedFeeTxnBase64 = uint8ArrayToBase64(unsignedFeeTxnBytes);
397
+
398
+ // Sign the payment transaction (index 1) with the active wallet
399
+ let signedPaymentTxnBytes: Uint8Array;
400
+
401
+ if (this.walletType === 'lute' && this.luteWallet) {
402
+ // Lute uses signTxns with base64 encoded transactions
403
+ // For atomic groups, pass both txns but only sign the one we control
404
+ const feeTxnBase64 = uint8ArrayToBase64(txnGroup[0].toByte());
405
+ const paymentTxnBase64 = uint8ArrayToBase64(txnGroup[1].toByte());
406
+
407
+ // Sign only the payment transaction (index 1), leave fee txn unsigned
408
+ const signedTxns = await this.luteWallet.signTxns([
409
+ { txn: feeTxnBase64, signers: [] }, // Don't sign - facilitator will
410
+ { txn: paymentTxnBase64 }, // Sign this one
411
+ ]);
412
+
413
+ if (!signedTxns || signedTxns.length < 2 || !signedTxns[1]) {
414
+ throw new X402Error('No signed transaction returned', 'SIGNATURE_REJECTED');
415
+ }
416
+
417
+ // Get the signed payment transaction (index 1)
418
+ const signedResult = signedTxns[1];
419
+ signedPaymentTxnBytes = this.decodeSignedTxn(signedResult);
420
+ } else if (this.walletType === 'pera' && this.peraWallet) {
421
+ // Pera uses signTransaction with transaction objects
422
+ // For atomic groups, pass both txns but only sign the one we control
423
+ const signedTxns = await this.peraWallet.signTransaction([
424
+ [
425
+ { txn: txnGroup[0], signers: [] }, // Don't sign - facilitator will
426
+ { txn: txnGroup[1] }, // Sign this one
427
+ ],
428
+ ]);
429
+
430
+ if (!signedTxns || signedTxns.length < 2 || !signedTxns[1]) {
431
+ throw new X402Error('No signed transaction returned', 'SIGNATURE_REJECTED');
432
+ }
242
433
 
243
- if (!signedTxns || signedTxns.length === 0) {
244
- throw new X402Error('No signed transaction returned', 'SIGNATURE_REJECTED');
434
+ signedPaymentTxnBytes = signedTxns[1];
435
+ } else {
436
+ throw new X402Error('No wallet available for signing', 'WALLET_NOT_CONNECTED');
245
437
  }
246
438
 
247
- const signedTxn = signedTxns[0];
439
+ const signedPaymentTxnBase64 = uint8ArrayToBase64(signedPaymentTxnBytes);
248
440
 
441
+ // Build payload following GoPlausible x402-avm spec
249
442
  const payload: AlgorandPaymentPayload = {
250
- from: this.address,
251
- to: recipient,
252
- amount: amount.toString(),
253
- assetId: assetId,
254
- signedTxn: uint8ArrayToBase64(signedTxn),
443
+ paymentIndex: 1, // Index of the payment transaction in the group
444
+ paymentGroup: [
445
+ unsignedFeeTxnBase64, // Transaction 0: unsigned fee tx
446
+ signedPaymentTxnBase64, // Transaction 1: signed payment tx
447
+ ],
255
448
  };
256
449
 
257
450
  return JSON.stringify(payload);
@@ -272,6 +465,32 @@ export class AlgorandProvider implements WalletAdapter {
272
465
  }
273
466
  }
274
467
 
468
+ /**
469
+ * Decode signed transaction from wallet response (handles various formats)
470
+ */
471
+ private decodeSignedTxn(signedResult: unknown): Uint8Array {
472
+ if (signedResult instanceof Uint8Array) {
473
+ return signedResult;
474
+ } else if (typeof signedResult === 'string') {
475
+ // Try to decode as base64
476
+ try {
477
+ return Uint8Array.from(atob(signedResult), c => c.charCodeAt(0));
478
+ } catch {
479
+ // If standard base64 fails, try URL-safe base64
480
+ const standardBase64 = signedResult.replace(/-/g, '+').replace(/_/g, '/');
481
+ return Uint8Array.from(atob(standardBase64), c => c.charCodeAt(0));
482
+ }
483
+ } else if (ArrayBuffer.isView(signedResult)) {
484
+ return new Uint8Array(
485
+ (signedResult as ArrayBufferView).buffer,
486
+ (signedResult as ArrayBufferView).byteOffset,
487
+ (signedResult as ArrayBufferView).byteLength
488
+ );
489
+ } else {
490
+ throw new X402Error('Unexpected signed transaction format', 'PAYMENT_FAILED');
491
+ }
492
+ }
493
+
275
494
  /**
276
495
  * Encode Algorand payment as X-PAYMENT header
277
496
  *
@@ -287,17 +506,19 @@ export class AlgorandProvider implements WalletAdapter {
287
506
  ): string {
288
507
  const payload = JSON.parse(paymentPayload) as AlgorandPaymentPayload;
289
508
 
290
- // Use chain name from config, or default to 'algorand'
291
- const networkName = chainConfig?.name || 'algorand';
509
+ // Determine network name for x402
510
+ // Use "algorand-mainnet" or "algorand-testnet" format for facilitator
511
+ let networkName: string;
512
+ if (chainConfig?.name === 'algorand-testnet') {
513
+ networkName = 'algorand-testnet';
514
+ } else {
515
+ networkName = 'algorand-mainnet'; // Default to mainnet
516
+ }
292
517
 
293
- // Build the payload data
518
+ // Build the payload data (GoPlausible x402-avm spec)
294
519
  const payloadData = {
295
- from: payload.from,
296
- to: payload.to,
297
- amount: payload.amount,
298
- assetId: payload.assetId,
299
- signedTxn: payload.signedTxn,
300
- ...(payload.note && { note: payload.note }),
520
+ paymentIndex: payload.paymentIndex,
521
+ paymentGroup: payload.paymentGroup,
301
522
  };
302
523
 
303
524
  // Format in x402 standard format (v1 or v2)
@@ -311,7 +532,7 @@ export class AlgorandProvider implements WalletAdapter {
311
532
  : {
312
533
  x402Version: 1 as const,
313
534
  scheme: 'exact' as const,
314
- network: networkName, // Plain chain name for v1
535
+ network: networkName,
315
536
  payload: payloadData,
316
537
  };
317
538
 
@@ -345,23 +345,21 @@ export interface NEARPaymentPayload {
345
345
  /**
346
346
  * Algorand payment payload (atomic transaction group)
347
347
  *
348
- * Algorand uses a unique payment model where the facilitator creates and signs
349
- * an atomic transaction group. The user signs their portion (the ASA transfer)
350
- * and the facilitator submits the complete group.
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
- /** Sender's Algorand address (58-character base32) */
354
- from: string;
355
- /** Recipient's Algorand address */
356
- to: string;
357
- /** Amount in base units (microAlgos for ALGO, or base units for ASA) */
358
- amount: string;
359
- /** USDC ASA ID (31566704 for mainnet, 10458941 for testnet) */
360
- assetId: number;
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
- from: string;
504
- to: string;
505
- amount: string;
506
- assetId: number;
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
  /**