uvd-x402-sdk 2.5.0 → 2.10.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 (75) hide show
  1. package/README.md +380 -3
  2. package/dist/adapters/index.d.mts +1 -1
  3. package/dist/adapters/index.d.ts +1 -1
  4. package/dist/adapters/index.js +82 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/adapters/index.mjs +82 -1
  7. package/dist/adapters/index.mjs.map +1 -1
  8. package/dist/backend/index.d.mts +1036 -0
  9. package/dist/backend/index.d.ts +1036 -0
  10. package/dist/backend/index.js +1722 -0
  11. package/dist/backend/index.js.map +1 -0
  12. package/dist/backend/index.mjs +1704 -0
  13. package/dist/backend/index.mjs.map +1 -0
  14. package/dist/{index-BrFeSWKm.d.mts → index-C60c_e5z.d.mts} +13 -4
  15. package/dist/{index-DR2vXt-c.d.mts → index-D-dO_FoP.d.mts} +70 -4
  16. package/dist/{index-DR2vXt-c.d.ts → index-D-dO_FoP.d.ts} +70 -4
  17. package/dist/{index-BYX9BU79.d.ts → index-VIOUicmO.d.ts} +13 -4
  18. package/dist/index.d.mts +3 -3
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +115 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +110 -2
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/providers/algorand/index.d.mts +86 -0
  25. package/dist/providers/algorand/index.d.ts +86 -0
  26. package/dist/providers/algorand/index.js +903 -0
  27. package/dist/providers/algorand/index.js.map +1 -0
  28. package/dist/providers/algorand/index.mjs +898 -0
  29. package/dist/providers/algorand/index.mjs.map +1 -0
  30. package/dist/providers/evm/index.d.mts +1 -1
  31. package/dist/providers/evm/index.d.ts +1 -1
  32. package/dist/providers/evm/index.js +78 -1
  33. package/dist/providers/evm/index.js.map +1 -1
  34. package/dist/providers/evm/index.mjs +78 -1
  35. package/dist/providers/evm/index.mjs.map +1 -1
  36. package/dist/providers/near/index.d.mts +1 -1
  37. package/dist/providers/near/index.d.ts +1 -1
  38. package/dist/providers/near/index.js +78 -1
  39. package/dist/providers/near/index.js.map +1 -1
  40. package/dist/providers/near/index.mjs +78 -1
  41. package/dist/providers/near/index.mjs.map +1 -1
  42. package/dist/providers/solana/index.d.mts +1 -1
  43. package/dist/providers/solana/index.d.ts +1 -1
  44. package/dist/providers/solana/index.js +78 -1
  45. package/dist/providers/solana/index.js.map +1 -1
  46. package/dist/providers/solana/index.mjs +78 -1
  47. package/dist/providers/solana/index.mjs.map +1 -1
  48. package/dist/providers/stellar/index.d.mts +1 -1
  49. package/dist/providers/stellar/index.d.ts +1 -1
  50. package/dist/providers/stellar/index.js +78 -1
  51. package/dist/providers/stellar/index.js.map +1 -1
  52. package/dist/providers/stellar/index.mjs +78 -1
  53. package/dist/providers/stellar/index.mjs.map +1 -1
  54. package/dist/react/index.d.mts +3 -3
  55. package/dist/react/index.d.ts +3 -3
  56. package/dist/react/index.js +82 -1
  57. package/dist/react/index.js.map +1 -1
  58. package/dist/react/index.mjs +82 -1
  59. package/dist/react/index.mjs.map +1 -1
  60. package/dist/utils/index.d.mts +57 -5
  61. package/dist/utils/index.d.ts +57 -5
  62. package/dist/utils/index.js +96 -1
  63. package/dist/utils/index.js.map +1 -1
  64. package/dist/utils/index.mjs +93 -2
  65. package/dist/utils/index.mjs.map +1 -1
  66. package/package.json +24 -3
  67. package/src/adapters/wagmi.ts +4 -0
  68. package/src/backend/index.ts +2131 -0
  69. package/src/chains/index.ts +94 -2
  70. package/src/client/X402Client.ts +4 -0
  71. package/src/index.ts +26 -1
  72. package/src/providers/algorand/index.ts +356 -0
  73. package/src/types/index.ts +78 -3
  74. package/src/utils/index.ts +4 -0
  75. package/src/utils/validation.ts +76 -3
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * uvd-x402-sdk - Chain Registry
3
3
  *
4
- * Complete configuration for all 15 supported blockchain networks.
5
- * EVM chains (11): Use ERC-3009 TransferWithAuthorization
4
+ * Complete configuration for all 16 supported blockchain networks.
5
+ * EVM chains (10): Use ERC-3009 TransferWithAuthorization
6
6
  * SVM chains (2): Solana and Fogo - Use SPL tokens with partially-signed transactions
7
7
  * Stellar (1): Uses Soroban authorization entries
8
8
  * NEAR (1): Uses NEP-366 meta-transactions
9
+ * Algorand (2): Uses ASA transfers with atomic transaction groups
9
10
  */
10
11
 
11
12
  import type { ChainConfig, NetworkType, TokenType, TokenConfig } from '../types';
@@ -541,6 +542,76 @@ export const SUPPORTED_CHAINS: Record<string, ChainConfig> = {
541
542
  enabled: true, // NEP-366 meta-transactions supported
542
543
  },
543
544
  },
545
+
546
+ // ============================================================================
547
+ // ALGORAND (2 networks) - Uses ASA transfers with atomic transaction groups
548
+ // ============================================================================
549
+
550
+ algorand: {
551
+ chainId: 0, // Non-EVM (Algorand uses genesis hash for network identification)
552
+ chainIdHex: '0x0',
553
+ name: 'algorand',
554
+ displayName: 'Algorand',
555
+ networkType: 'algorand',
556
+ rpcUrl: 'https://mainnet-api.algonode.cloud',
557
+ explorerUrl: 'https://allo.info',
558
+ nativeCurrency: {
559
+ name: 'Algo',
560
+ symbol: 'ALGO',
561
+ decimals: 6, // Algorand uses 6 decimals (microAlgos)
562
+ },
563
+ usdc: {
564
+ address: '31566704', // USDC ASA ID on Algorand mainnet
565
+ decimals: 6,
566
+ name: 'USDC',
567
+ version: '1',
568
+ },
569
+ tokens: {
570
+ usdc: {
571
+ address: '31566704', // USDC ASA ID on Algorand mainnet
572
+ decimals: 6,
573
+ name: 'USDC',
574
+ version: '1',
575
+ },
576
+ },
577
+ x402: {
578
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
579
+ enabled: true,
580
+ },
581
+ },
582
+
583
+ 'algorand-testnet': {
584
+ chainId: 0, // Non-EVM
585
+ chainIdHex: '0x0',
586
+ name: 'algorand-testnet',
587
+ displayName: 'Algorand Testnet',
588
+ networkType: 'algorand',
589
+ rpcUrl: 'https://testnet-api.algonode.cloud',
590
+ explorerUrl: 'https://testnet.allo.info',
591
+ nativeCurrency: {
592
+ name: 'Algo',
593
+ symbol: 'ALGO',
594
+ decimals: 6,
595
+ },
596
+ usdc: {
597
+ address: '10458941', // USDC ASA ID on Algorand testnet
598
+ decimals: 6,
599
+ name: 'USDC',
600
+ version: '1',
601
+ },
602
+ tokens: {
603
+ usdc: {
604
+ address: '10458941', // USDC ASA ID on Algorand testnet
605
+ decimals: 6,
606
+ name: 'USDC',
607
+ version: '1',
608
+ },
609
+ },
610
+ x402: {
611
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
612
+ enabled: true,
613
+ },
614
+ },
544
615
  };
545
616
 
546
617
  /**
@@ -637,6 +708,8 @@ export function getExplorerTxUrl(chainName: string, txHash: string): string | nu
637
708
  return `${chain.explorerUrl}/tx/${txHash}`;
638
709
  case 'near':
639
710
  return `${chain.explorerUrl}/txns/${txHash}`;
711
+ case 'algorand':
712
+ return `${chain.explorerUrl}/tx/${txHash}`;
640
713
  default:
641
714
  return null;
642
715
  }
@@ -659,11 +732,30 @@ export function getExplorerAddressUrl(chainName: string, address: string): strin
659
732
  return `${chain.explorerUrl}/account/${address}`;
660
733
  case 'near':
661
734
  return `${chain.explorerUrl}/address/${address}`;
735
+ case 'algorand':
736
+ return `${chain.explorerUrl}/account/${address}`;
662
737
  default:
663
738
  return null;
664
739
  }
665
740
  }
666
741
 
742
+ /**
743
+ * Get list of Algorand chains
744
+ */
745
+ export function getAlgorandChains(): ChainConfig[] {
746
+ return Object.values(SUPPORTED_CHAINS).filter(
747
+ chain => chain.networkType === 'algorand' && chain.x402.enabled
748
+ );
749
+ }
750
+
751
+ /**
752
+ * Check if a chain is Algorand-based
753
+ */
754
+ export function isAlgorandChain(chainName: string): boolean {
755
+ const chain = getChainByName(chainName);
756
+ return chain?.networkType === 'algorand';
757
+ }
758
+
667
759
  // ============================================================================
668
760
  // MULTI-TOKEN SUPPORT FUNCTIONS
669
761
  // ============================================================================
@@ -567,6 +567,10 @@ export class X402Client {
567
567
  const result: PaymentResult = {
568
568
  success: true,
569
569
  paymentHeader,
570
+ headers: {
571
+ 'X-PAYMENT': paymentHeader,
572
+ 'PAYMENT-SIGNATURE': paymentHeader,
573
+ },
570
574
  network: chain.name,
571
575
  payer: from,
572
576
  };
package/src/index.ts CHANGED
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * x402 Payment SDK - Gasless crypto payments using the Ultravioleta facilitator.
5
5
  *
6
- * Supports 14 blockchain networks:
6
+ * Supports 16 blockchain networks:
7
7
  * - EVM (10): Base, Ethereum, Polygon, Arbitrum, Optimism, Avalanche, Celo, HyperEVM, Unichain, Monad
8
8
  * - SVM (2): Solana, Fogo
9
9
  * - Stellar (1): Stellar
10
10
  * - NEAR (1): NEAR Protocol
11
+ * - Algorand (2): Algorand mainnet and testnet
11
12
  *
12
13
  * Supports both x402 v1 and v2 protocols.
13
14
  *
@@ -60,6 +61,18 @@
60
61
  * const header = near.encodePaymentHeader(payload);
61
62
  * ```
62
63
  *
64
+ * @example With Algorand
65
+ * ```ts
66
+ * import { AlgorandProvider } from 'uvd-x402-sdk/algorand';
67
+ * import { getChainByName } from 'uvd-x402-sdk';
68
+ *
69
+ * const algorand = new AlgorandProvider();
70
+ * const address = await algorand.connect();
71
+ * const algorandConfig = getChainByName('algorand')!;
72
+ * const payload = await algorand.signPayment(paymentInfo, algorandConfig);
73
+ * const header = algorand.encodePaymentHeader(payload, algorandConfig);
74
+ * ```
75
+ *
63
76
  * @example With React
64
77
  * ```tsx
65
78
  * import { X402Provider, useX402, usePayment } from 'uvd-x402-sdk/react';
@@ -100,6 +113,9 @@ export {
100
113
  getSupportedTokens,
101
114
  isTokenSupported,
102
115
  getChainsByToken,
116
+ // Algorand helper functions
117
+ getAlgorandChains,
118
+ isAlgorandChain,
103
119
  } from './chains';
104
120
 
105
121
  // x402 utilities
@@ -119,6 +135,11 @@ export {
119
135
  // Validation utilities
120
136
  validateRecipient,
121
137
  validateAmount,
138
+ // Payment header utilities
139
+ createPaymentHeaders,
140
+ getPaymentHeader,
141
+ DEFAULT_PAYMENT_HEADER,
142
+ PAYMENT_HEADER_NAMES,
122
143
  } from './utils';
123
144
 
124
145
  // Types
@@ -143,11 +164,14 @@ export type {
143
164
  PaymentInfo,
144
165
  PaymentRequest,
145
166
  PaymentResult,
167
+ PaymentHeaders,
146
168
  PaymentPayload,
147
169
  EVMPaymentPayload,
148
170
  SolanaPaymentPayload,
149
171
  StellarPaymentPayload,
150
172
  NEARPaymentPayload,
173
+ AlgorandPaymentPayload,
174
+ X402HeaderName,
151
175
 
152
176
  // x402 header types (v1 and v2)
153
177
  X402Version,
@@ -160,6 +184,7 @@ export type {
160
184
  X402SolanaPayload,
161
185
  X402StellarPayload,
162
186
  X402NEARPayload,
187
+ X402AlgorandPayload,
163
188
 
164
189
  // Config types
165
190
  X402ClientConfig,
@@ -0,0 +1,356 @@
1
+ /**
2
+ * uvd-x402-sdk - Algorand Provider
3
+ *
4
+ * Provides wallet connection and payment creation for Algorand via Pera Wallet.
5
+ * Uses ASA (Algorand Standard Assets) transfers for USDC payments.
6
+ *
7
+ * USDC ASA IDs:
8
+ * - Mainnet: 31566704
9
+ * - Testnet: 10458941
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { AlgorandProvider } from 'uvd-x402-sdk/algorand';
14
+ * import { getChainByName } from 'uvd-x402-sdk';
15
+ *
16
+ * const algorand = new AlgorandProvider();
17
+ *
18
+ * // Connect to Pera Wallet
19
+ * const address = await algorand.connect();
20
+ *
21
+ * // Create Algorand payment
22
+ * const chainConfig = getChainByName('algorand')!;
23
+ * const paymentPayload = await algorand.signPayment(paymentInfo, chainConfig);
24
+ * const header = algorand.encodePaymentHeader(paymentPayload, chainConfig);
25
+ * ```
26
+ */
27
+
28
+ import type {
29
+ ChainConfig,
30
+ PaymentInfo,
31
+ AlgorandPaymentPayload,
32
+ WalletAdapter,
33
+ X402Version,
34
+ } from '../../types';
35
+ import { X402Error } from '../../types';
36
+ import { getChainByName } from '../../chains';
37
+ import { chainToCAIP2 } from '../../utils';
38
+
39
+ /**
40
+ * Browser-compatible base64 encoding for Uint8Array
41
+ */
42
+ function uint8ArrayToBase64(bytes: Uint8Array): string {
43
+ let binary = '';
44
+ for (let i = 0; i < bytes.length; i++) {
45
+ binary += String.fromCharCode(bytes[i]);
46
+ }
47
+ return btoa(binary);
48
+ }
49
+
50
+ // Lazy import Algorand dependencies
51
+ let algosdk: typeof import('algosdk') | null = null;
52
+ let PeraWalletConnect: typeof import('@perawallet/connect').PeraWalletConnect | null = null;
53
+
54
+ async function loadAlgorandDeps() {
55
+ if (!algosdk) {
56
+ algosdk = await import('algosdk');
57
+ }
58
+ if (!PeraWalletConnect) {
59
+ const peraModule = await import('@perawallet/connect');
60
+ PeraWalletConnect = peraModule.PeraWalletConnect;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * AlgorandProvider - Wallet adapter for Algorand via Pera Wallet
66
+ *
67
+ * Supports both mainnet and testnet through chain configuration.
68
+ */
69
+ export class AlgorandProvider implements WalletAdapter {
70
+ readonly id = 'pera';
71
+ readonly name = 'Pera Wallet';
72
+ readonly networkType = 'algorand' as const;
73
+
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ private peraWallet: any = null;
76
+ private address: string | null = null;
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ private algodClients: Map<string, any> = new Map();
79
+
80
+ /**
81
+ * Check if Pera Wallet is available
82
+ * Note: Pera works as a WalletConnect modal, so it's always "available"
83
+ */
84
+ isAvailable(): boolean {
85
+ return typeof window !== 'undefined';
86
+ }
87
+
88
+ /**
89
+ * Connect to Pera Wallet
90
+ */
91
+ async connect(_chainName?: string): Promise<string> {
92
+ await loadAlgorandDeps();
93
+
94
+ if (!PeraWalletConnect) {
95
+ throw new X402Error('Failed to load Pera Wallet SDK', 'WALLET_NOT_FOUND');
96
+ }
97
+
98
+ try {
99
+ // Create Pera Wallet instance
100
+ this.peraWallet = new PeraWalletConnect!();
101
+
102
+ // Try to reconnect from previous session
103
+ const accounts = await this.peraWallet.reconnectSession();
104
+
105
+ if (accounts.length > 0) {
106
+ this.address = accounts[0];
107
+ return accounts[0];
108
+ }
109
+
110
+ // If no previous session, connect fresh
111
+ const newAccounts = await this.peraWallet.connect();
112
+
113
+ if (newAccounts.length === 0) {
114
+ throw new X402Error('No accounts returned from Pera Wallet', 'WALLET_CONNECTION_REJECTED');
115
+ }
116
+
117
+ this.address = newAccounts[0];
118
+
119
+ // Set up disconnect handler
120
+ this.peraWallet.connector?.on('disconnect', () => {
121
+ this.address = null;
122
+ });
123
+
124
+ return newAccounts[0];
125
+ } catch (error: unknown) {
126
+ if (error instanceof Error) {
127
+ if (error.message.includes('rejected') || error.message.includes('cancelled')) {
128
+ throw new X402Error('Connection rejected by user', 'WALLET_CONNECTION_REJECTED');
129
+ }
130
+ }
131
+ throw new X402Error(
132
+ `Failed to connect Pera Wallet: ${error instanceof Error ? error.message : 'Unknown error'}`,
133
+ 'UNKNOWN_ERROR',
134
+ error
135
+ );
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Disconnect from Pera Wallet
141
+ */
142
+ async disconnect(): Promise<void> {
143
+ if (this.peraWallet) {
144
+ try {
145
+ await this.peraWallet.disconnect();
146
+ } catch {
147
+ // Ignore disconnect errors
148
+ }
149
+ }
150
+ this.peraWallet = null;
151
+ this.address = null;
152
+ this.algodClients.clear();
153
+ }
154
+
155
+ /**
156
+ * Get current address
157
+ */
158
+ getAddress(): string | null {
159
+ return this.address;
160
+ }
161
+
162
+ /**
163
+ * Get USDC (ASA) balance
164
+ */
165
+ async getBalance(chainConfig: ChainConfig): Promise<string> {
166
+ await loadAlgorandDeps();
167
+
168
+ if (!this.address) {
169
+ throw new X402Error('Wallet not connected', 'WALLET_NOT_CONNECTED');
170
+ }
171
+
172
+ const algodClient = await this.getAlgodClient(chainConfig);
173
+ const assetId = parseInt(chainConfig.usdc.address, 10);
174
+
175
+ try {
176
+ const accountInfo = await algodClient.accountInformation(this.address).do();
177
+
178
+ // Find the USDC asset in the account's assets
179
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
+ const assets: any[] = accountInfo.assets || accountInfo['assets'] || [];
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ const usdcAsset = assets.find((asset: any) =>
183
+ (asset.assetId || asset['asset-id']) === assetId
184
+ );
185
+
186
+ if (!usdcAsset) {
187
+ return '0.00'; // Account not opted into USDC
188
+ }
189
+
190
+ const amount = Number(usdcAsset.amount || usdcAsset['amount']);
191
+ const balance = amount / Math.pow(10, chainConfig.usdc.decimals);
192
+ return balance.toFixed(2);
193
+ } catch {
194
+ return '0.00';
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Create Algorand ASA transfer payment
200
+ *
201
+ * Transaction structure:
202
+ * 1. ASA Transfer from user to recipient
203
+ * 2. Facilitator pays transaction fees
204
+ */
205
+ async signPayment(paymentInfo: PaymentInfo, chainConfig: ChainConfig): Promise<string> {
206
+ await loadAlgorandDeps();
207
+
208
+ if (!this.peraWallet || !this.address) {
209
+ throw new X402Error('Wallet not connected', 'WALLET_NOT_CONNECTED');
210
+ }
211
+
212
+ if (!algosdk) {
213
+ throw new X402Error('Algorand SDK not loaded', 'UNKNOWN_ERROR');
214
+ }
215
+
216
+ const algodClient = await this.getAlgodClient(chainConfig);
217
+
218
+ // Get recipient address (use algorand-specific or fallback to default)
219
+ const recipient = paymentInfo.recipients?.algorand || paymentInfo.recipient;
220
+ const assetId = parseInt(chainConfig.usdc.address, 10);
221
+
222
+ // Parse amount (6 decimals for USDC)
223
+ const amount = Math.floor(parseFloat(paymentInfo.amount) * 1_000_000);
224
+
225
+ try {
226
+ // Get suggested transaction parameters
227
+ const suggestedParams = await algodClient.getTransactionParams().do();
228
+
229
+ // Create ASA transfer transaction
230
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
231
+ const txn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
232
+ sender: this.address,
233
+ receiver: recipient,
234
+ amount: BigInt(amount),
235
+ assetIndex: assetId,
236
+ suggestedParams: suggestedParams,
237
+ note: new TextEncoder().encode('x402 payment via uvd-x402-sdk'),
238
+ } as any);
239
+
240
+ // Sign with Pera Wallet
241
+ const signedTxns = await this.peraWallet.signTransaction([[{ txn }]]);
242
+
243
+ if (!signedTxns || signedTxns.length === 0) {
244
+ throw new X402Error('No signed transaction returned', 'SIGNATURE_REJECTED');
245
+ }
246
+
247
+ const signedTxn = signedTxns[0];
248
+
249
+ const payload: AlgorandPaymentPayload = {
250
+ from: this.address,
251
+ to: recipient,
252
+ amount: amount.toString(),
253
+ assetId: assetId,
254
+ signedTxn: uint8ArrayToBase64(signedTxn),
255
+ };
256
+
257
+ return JSON.stringify(payload);
258
+ } catch (error: unknown) {
259
+ if (error instanceof X402Error) {
260
+ throw error;
261
+ }
262
+ if (error instanceof Error) {
263
+ if (error.message.includes('rejected') || error.message.includes('cancelled')) {
264
+ throw new X402Error('Signature rejected by user', 'SIGNATURE_REJECTED');
265
+ }
266
+ }
267
+ throw new X402Error(
268
+ `Failed to sign transaction: ${error instanceof Error ? error.message : 'Unknown error'}`,
269
+ 'PAYMENT_FAILED',
270
+ error
271
+ );
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Encode Algorand payment as X-PAYMENT header
277
+ *
278
+ * @param paymentPayload - JSON-encoded payment payload from signPayment()
279
+ * @param chainConfig - Chain configuration
280
+ * @param version - x402 protocol version (1 or 2, defaults to 1)
281
+ * @returns Base64-encoded X-PAYMENT header value
282
+ */
283
+ encodePaymentHeader(
284
+ paymentPayload: string,
285
+ chainConfig?: ChainConfig,
286
+ version: X402Version = 1
287
+ ): string {
288
+ const payload = JSON.parse(paymentPayload) as AlgorandPaymentPayload;
289
+
290
+ // Use chain name from config, or default to 'algorand'
291
+ const networkName = chainConfig?.name || 'algorand';
292
+
293
+ // Build the payload data
294
+ 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 }),
301
+ };
302
+
303
+ // Format in x402 standard format (v1 or v2)
304
+ const x402Payload = version === 2
305
+ ? {
306
+ x402Version: 2 as const,
307
+ scheme: 'exact' as const,
308
+ network: chainToCAIP2(networkName), // CAIP-2 format for v2
309
+ payload: payloadData,
310
+ }
311
+ : {
312
+ x402Version: 1 as const,
313
+ scheme: 'exact' as const,
314
+ network: networkName, // Plain chain name for v1
315
+ payload: payloadData,
316
+ };
317
+
318
+ return btoa(JSON.stringify(x402Payload));
319
+ }
320
+
321
+ // Private helpers
322
+
323
+ /**
324
+ * Get or create an Algod client for a specific chain
325
+ */
326
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
+ private async getAlgodClient(chainConfig?: ChainConfig): Promise<any> {
328
+ await loadAlgorandDeps();
329
+
330
+ if (!algosdk) {
331
+ throw new X402Error('Algorand SDK not loaded', 'UNKNOWN_ERROR');
332
+ }
333
+
334
+ const config = chainConfig || getChainByName('algorand');
335
+ if (!config) {
336
+ throw new X402Error('Chain config not found', 'CHAIN_NOT_SUPPORTED');
337
+ }
338
+
339
+ // Cache by RPC URL
340
+ const cacheKey = config.rpcUrl;
341
+
342
+ if (this.algodClients.has(cacheKey)) {
343
+ return this.algodClients.get(cacheKey)!;
344
+ }
345
+
346
+ // Create new Algod client
347
+ // Algonode.cloud doesn't require auth token
348
+ const client = new algosdk.Algodv2('', config.rpcUrl, '');
349
+ this.algodClients.set(cacheKey, client);
350
+
351
+ return client;
352
+ }
353
+ }
354
+
355
+ // Default export
356
+ export default AlgorandProvider;