uvd-x402-sdk 2.15.2 → 2.16.2

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 (77) hide show
  1. package/README.md +247 -6
  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 +1 -1
  9. package/dist/backend/index.d.ts +1 -1
  10. package/dist/backend/index.js +82 -1
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/index.mjs +82 -1
  13. package/dist/backend/index.mjs.map +1 -1
  14. package/dist/{index-Cwi_VM05.d.ts → index-B2cQzyKa.d.ts} +10 -2
  15. package/dist/{index-BYIugZlE.d.mts → index-BE5cH7oS.d.mts} +48 -5
  16. package/dist/{index-BYIugZlE.d.ts → index-BE5cH7oS.d.ts} +48 -5
  17. package/dist/{index-D3PO3jLW.d.mts → index-oE4dj05k.d.mts} +10 -2
  18. package/dist/index.d.mts +11 -2
  19. package/dist/index.d.ts +11 -2
  20. package/dist/index.js +111 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +110 -3
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/providers/algorand/index.d.mts +1 -1
  25. package/dist/providers/algorand/index.d.ts +1 -1
  26. package/dist/providers/algorand/index.js +95 -2
  27. package/dist/providers/algorand/index.js.map +1 -1
  28. package/dist/providers/algorand/index.mjs +95 -2
  29. package/dist/providers/algorand/index.mjs.map +1 -1
  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 +82 -1
  33. package/dist/providers/evm/index.js.map +1 -1
  34. package/dist/providers/evm/index.mjs +82 -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 +82 -1
  39. package/dist/providers/near/index.js.map +1 -1
  40. package/dist/providers/near/index.mjs +82 -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 +82 -1
  45. package/dist/providers/solana/index.js.map +1 -1
  46. package/dist/providers/solana/index.mjs +82 -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 +82 -1
  51. package/dist/providers/stellar/index.js.map +1 -1
  52. package/dist/providers/stellar/index.mjs +82 -1
  53. package/dist/providers/stellar/index.mjs.map +1 -1
  54. package/dist/providers/sui/index.d.mts +105 -0
  55. package/dist/providers/sui/index.d.ts +105 -0
  56. package/dist/providers/sui/index.js +1027 -0
  57. package/dist/providers/sui/index.js.map +1 -0
  58. package/dist/providers/sui/index.mjs +1022 -0
  59. package/dist/providers/sui/index.mjs.map +1 -0
  60. package/dist/react/index.d.mts +3 -3
  61. package/dist/react/index.d.ts +3 -3
  62. package/dist/react/index.js +82 -1
  63. package/dist/react/index.js.map +1 -1
  64. package/dist/react/index.mjs +82 -1
  65. package/dist/react/index.mjs.map +1 -1
  66. package/dist/utils/index.d.mts +1 -1
  67. package/dist/utils/index.d.ts +1 -1
  68. package/dist/utils/index.js +82 -1
  69. package/dist/utils/index.js.map +1 -1
  70. package/dist/utils/index.mjs +82 -1
  71. package/dist/utils/index.mjs.map +1 -1
  72. package/package.json +15 -4
  73. package/src/chains/index.ts +98 -0
  74. package/src/facilitator.ts +18 -0
  75. package/src/index.ts +20 -1
  76. package/src/providers/sui/index.ts +431 -0
  77. package/src/types/index.ts +55 -3
@@ -11,6 +11,7 @@
11
11
  * - Algorand: Signing fee transactions in atomic groups
12
12
  * - Stellar: Signing authorization entries
13
13
  * - NEAR: Acting as relayer for meta-transactions
14
+ * - Sui: Sponsoring transactions (paying gas via sponsored txs)
14
15
  */
15
16
 
16
17
  /**
@@ -109,6 +110,20 @@ export const FACILITATOR_ADDRESSES = {
109
110
  * Algorand facilitator address (testnet)
110
111
  */
111
112
  'algorand-testnet': '5DPPDQNYUPCTXRZWRYSF3WPYU6RKAUR25F3YG4EKXQRHV5AUAI62H5GXL4',
113
+
114
+ // ============================================
115
+ // Sui
116
+ // ============================================
117
+ /**
118
+ * Sui facilitator address (mainnet)
119
+ * Used for: Sponsoring transactions (paying gas)
120
+ */
121
+ sui: '0xe7bbf2b13f7d72714760aa16e024fa1b35a978793f9893d0568a4fbf356a764a',
122
+
123
+ /**
124
+ * Sui facilitator address (testnet)
125
+ */
126
+ 'sui-testnet': '0xabbd16a2fab2a502c9cfe835195a6fc7d70bfc27cffb40b8b286b52a97006e67',
112
127
  } as const;
113
128
 
114
129
  /**
@@ -144,6 +159,9 @@ export function getFacilitatorAddress(
144
159
  if (networkType === 'near') {
145
160
  return FACILITATOR_ADDRESSES.near;
146
161
  }
162
+ if (networkType === 'sui') {
163
+ return FACILITATOR_ADDRESSES.sui;
164
+ }
147
165
 
148
166
  return undefined;
149
167
  }
package/src/index.ts CHANGED
@@ -3,12 +3,13 @@
3
3
  *
4
4
  * x402 Payment SDK - Gasless crypto payments using the Ultravioleta facilitator.
5
5
  *
6
- * Supports 16 blockchain networks:
6
+ * Supports 18 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
11
  * - Algorand (2): Algorand mainnet and testnet
12
+ * - Sui (2): Sui mainnet and testnet
12
13
  *
13
14
  * Supports both x402 v1 and v2 protocols.
14
15
  *
@@ -73,6 +74,19 @@
73
74
  * const header = algorand.encodePaymentHeader(payload, algorandConfig);
74
75
  * ```
75
76
  *
77
+ * @example With Sui (Sponsored Transactions)
78
+ * ```ts
79
+ * import { SuiProvider } from 'uvd-x402-sdk/sui';
80
+ * import { getChainByName } from 'uvd-x402-sdk';
81
+ *
82
+ * const sui = new SuiProvider();
83
+ * const address = await sui.connect();
84
+ * const suiConfig = getChainByName('sui')!;
85
+ * const payload = await sui.signPayment(paymentInfo, suiConfig);
86
+ * const header = sui.encodePaymentHeader(payload, suiConfig);
87
+ * // User pays ZERO gas - facilitator sponsors the transaction
88
+ * ```
89
+ *
76
90
  * @example With React
77
91
  * ```tsx
78
92
  * import { X402Provider, useX402, usePayment } from 'uvd-x402-sdk/react';
@@ -116,6 +130,9 @@ export {
116
130
  // Algorand helper functions
117
131
  getAlgorandChains,
118
132
  isAlgorandChain,
133
+ // Sui helper functions
134
+ getSuiChains,
135
+ isSuiChain,
119
136
  } from './chains';
120
137
 
121
138
  // x402 utilities
@@ -171,6 +188,7 @@ export type {
171
188
  StellarPaymentPayload,
172
189
  NEARPaymentPayload,
173
190
  AlgorandPaymentPayload,
191
+ SuiPaymentPayload,
174
192
  X402HeaderName,
175
193
 
176
194
  // x402 header types (v1 and v2)
@@ -185,6 +203,7 @@ export type {
185
203
  X402StellarPayload,
186
204
  X402NEARPayload,
187
205
  X402AlgorandPayload,
206
+ X402SuiPayload,
188
207
 
189
208
  // Config types
190
209
  X402ClientConfig,
@@ -0,0 +1,431 @@
1
+ /**
2
+ * uvd-x402-sdk - Sui Provider
3
+ *
4
+ * Provides wallet connection and payment creation for Sui blockchain.
5
+ * Uses sponsored transactions where the facilitator pays gas fees.
6
+ *
7
+ * @example Sui Mainnet
8
+ * ```ts
9
+ * import { SuiProvider } from 'uvd-x402-sdk/sui';
10
+ * import { getChainByName } from 'uvd-x402-sdk';
11
+ *
12
+ * const sui = new SuiProvider();
13
+ *
14
+ * // Connect
15
+ * const address = await sui.connect();
16
+ *
17
+ * // Create payment
18
+ * const chainConfig = getChainByName('sui')!;
19
+ * const paymentPayload = await sui.signPayment(paymentInfo, chainConfig);
20
+ * const header = sui.encodePaymentHeader(paymentPayload, chainConfig);
21
+ * ```
22
+ *
23
+ * @example Sui Testnet
24
+ * ```ts
25
+ * import { SuiProvider } from 'uvd-x402-sdk/sui';
26
+ * import { getChainByName } from 'uvd-x402-sdk';
27
+ *
28
+ * const sui = new SuiProvider();
29
+ *
30
+ * // Connect
31
+ * const address = await sui.connect();
32
+ *
33
+ * // Create testnet payment
34
+ * const chainConfig = getChainByName('sui-testnet')!;
35
+ * const paymentPayload = await sui.signPayment(paymentInfo, chainConfig);
36
+ * const header = sui.encodePaymentHeader(paymentPayload, chainConfig);
37
+ * ```
38
+ */
39
+
40
+ import type {
41
+ ChainConfig,
42
+ PaymentInfo,
43
+ SuiPaymentPayload,
44
+ WalletAdapter,
45
+ X402Version,
46
+ } from '../../types';
47
+ import { X402Error } from '../../types';
48
+ import { getChainByName } from '../../chains';
49
+ import { chainToCAIP2 } from '../../utils';
50
+
51
+ // Lazy import Sui dependencies to avoid bundling when not used
52
+ let SuiClient: typeof import('@mysten/sui/client').SuiClient;
53
+ let Transaction: typeof import('@mysten/sui/transactions').Transaction;
54
+
55
+ async function loadSuiDeps() {
56
+ if (!SuiClient) {
57
+ const clientModule = await import('@mysten/sui/client');
58
+ const txModule = await import('@mysten/sui/transactions');
59
+ SuiClient = clientModule.SuiClient;
60
+ Transaction = txModule.Transaction;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Sui wallet provider interface (for Sui Wallet, Suiet, etc.)
66
+ */
67
+ interface SuiWalletProvider {
68
+ hasPermissions?: () => Promise<boolean>;
69
+ requestPermissions?: () => Promise<boolean>;
70
+ getAccounts: () => Promise<string[]>;
71
+ signTransactionBlock?: (input: {
72
+ transactionBlock: Uint8Array;
73
+ account: string;
74
+ chain: string;
75
+ }) => Promise<{
76
+ signature: string;
77
+ transactionBlockBytes: string;
78
+ }>;
79
+ signTransaction?: (input: {
80
+ transaction: Uint8Array;
81
+ account: string;
82
+ chain: string;
83
+ }) => Promise<{
84
+ signature: string;
85
+ bytes: string;
86
+ }>;
87
+ }
88
+
89
+ /**
90
+ * SuiProvider - Wallet adapter for Sui blockchain
91
+ *
92
+ * Uses sponsored transactions where:
93
+ * 1. User creates a programmable transaction for token transfer
94
+ * 2. User signs the transaction
95
+ * 3. Facilitator sponsors (pays gas) and submits
96
+ */
97
+ export class SuiProvider implements WalletAdapter {
98
+ readonly id = 'sui-wallet';
99
+ readonly name = 'Sui Wallet';
100
+ readonly networkType = 'sui' as const;
101
+
102
+ private provider: SuiWalletProvider | null = null;
103
+ private clients: Map<string, InstanceType<typeof SuiClient>> = new Map();
104
+ private address: string | null = null;
105
+
106
+ /**
107
+ * Check if Sui wallet is available
108
+ */
109
+ isAvailable(): boolean {
110
+ if (typeof window === 'undefined') return false;
111
+ const win = window as Window & { suiWallet?: SuiWalletProvider };
112
+ return !!win.suiWallet;
113
+ }
114
+
115
+ /**
116
+ * Connect to Sui wallet
117
+ */
118
+ async connect(): Promise<string> {
119
+ await loadSuiDeps();
120
+
121
+ // Get Sui wallet provider
122
+ this.provider = await this.getSuiWalletProvider();
123
+ if (!this.provider) {
124
+ throw new X402Error(
125
+ 'Sui wallet not installed. Please install Sui Wallet or Suiet from Chrome Web Store',
126
+ 'WALLET_NOT_FOUND'
127
+ );
128
+ }
129
+
130
+ try {
131
+ // Request permissions if needed
132
+ if (this.provider.requestPermissions) {
133
+ await this.provider.requestPermissions();
134
+ }
135
+
136
+ // Get accounts
137
+ const accounts = await this.provider.getAccounts();
138
+ if (!accounts || accounts.length === 0) {
139
+ throw new X402Error('No Sui accounts found', 'WALLET_CONNECTION_FAILED');
140
+ }
141
+
142
+ this.address = accounts[0];
143
+ return this.address;
144
+ } catch (error: unknown) {
145
+ if (error instanceof X402Error) throw error;
146
+ if (error instanceof Error) {
147
+ if (error.message.includes('User rejected') || error.message.includes('rejected')) {
148
+ throw new X402Error('Connection rejected by user', 'WALLET_CONNECTION_REJECTED');
149
+ }
150
+ }
151
+ throw new X402Error(
152
+ `Failed to connect Sui wallet: ${error instanceof Error ? error.message : 'Unknown error'}`,
153
+ 'UNKNOWN_ERROR',
154
+ error
155
+ );
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Disconnect from Sui wallet
161
+ */
162
+ async disconnect(): Promise<void> {
163
+ this.provider = null;
164
+ this.clients.clear();
165
+ this.address = null;
166
+ }
167
+
168
+ /**
169
+ * Get current address
170
+ */
171
+ getAddress(): string | null {
172
+ return this.address;
173
+ }
174
+
175
+ /**
176
+ * Get USDC balance
177
+ */
178
+ async getBalance(chainConfig: ChainConfig): Promise<string> {
179
+ await loadSuiDeps();
180
+
181
+ if (!this.address) {
182
+ throw new X402Error('Wallet not connected', 'WALLET_NOT_CONNECTED');
183
+ }
184
+
185
+ const client = await this.getClient(chainConfig);
186
+
187
+ try {
188
+ // Get all USDC coins for this address
189
+ const coins = await client.getCoins({
190
+ owner: this.address,
191
+ coinType: chainConfig.usdc.address,
192
+ });
193
+
194
+ if (!coins.data || coins.data.length === 0) {
195
+ return '0.00';
196
+ }
197
+
198
+ // Sum all USDC balances
199
+ const totalBalance = coins.data.reduce((sum, coin) => {
200
+ return sum + BigInt(coin.balance);
201
+ }, BigInt(0));
202
+
203
+ const balance = Number(totalBalance) / Math.pow(10, chainConfig.usdc.decimals);
204
+ return balance.toFixed(2);
205
+ } catch {
206
+ return '0.00';
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Create Sui payment (sponsored transaction)
212
+ *
213
+ * Transaction structure:
214
+ * 1. User creates a programmable transaction for USDC transfer
215
+ * 2. Transaction is signed by the user
216
+ * 3. Facilitator sponsors the transaction (pays gas in SUI)
217
+ * 4. Facilitator adds sponsor signature and submits
218
+ *
219
+ * User pays: ZERO SUI
220
+ */
221
+ async signPayment(paymentInfo: PaymentInfo, chainConfig: ChainConfig): Promise<string> {
222
+ await loadSuiDeps();
223
+
224
+ if (!this.provider || !this.address) {
225
+ throw new X402Error('Wallet not connected', 'WALLET_NOT_CONNECTED');
226
+ }
227
+
228
+ const client = await this.getClient(chainConfig);
229
+
230
+ // Get recipient address
231
+ const recipient = paymentInfo.recipients?.sui || paymentInfo.recipient;
232
+ const facilitatorAddress = paymentInfo.facilitator;
233
+
234
+ if (!facilitatorAddress) {
235
+ throw new X402Error('Facilitator address not provided', 'INVALID_CONFIG');
236
+ }
237
+
238
+ // Parse amount (6 decimals for USDC)
239
+ const amount = BigInt(Math.floor(parseFloat(paymentInfo.amount) * 1_000_000));
240
+
241
+ // Find USDC coins for transfer
242
+ const usdcCoins = await client.getCoins({
243
+ owner: this.address,
244
+ coinType: chainConfig.usdc.address,
245
+ });
246
+
247
+ if (!usdcCoins.data || usdcCoins.data.length === 0) {
248
+ throw new X402Error('No USDC coins found in wallet', 'INSUFFICIENT_BALANCE');
249
+ }
250
+
251
+ // Check total balance
252
+ const totalBalance = usdcCoins.data.reduce((sum, coin) => sum + BigInt(coin.balance), BigInt(0));
253
+ if (totalBalance < amount) {
254
+ throw new X402Error(
255
+ `Insufficient USDC balance. Have: ${Number(totalBalance) / 1_000_000}, Need: ${Number(amount) / 1_000_000}`,
256
+ 'INSUFFICIENT_BALANCE'
257
+ );
258
+ }
259
+
260
+ // Build the programmable transaction
261
+ const tx = new Transaction();
262
+
263
+ // Set the gas sponsor (facilitator pays)
264
+ tx.setSender(this.address);
265
+ tx.setGasOwner(facilitatorAddress);
266
+
267
+ // Find a coin with enough balance or merge coins
268
+ let coinToUse: string;
269
+ const sufficientCoin = usdcCoins.data.find(c => BigInt(c.balance) >= amount);
270
+
271
+ if (sufficientCoin) {
272
+ coinToUse = sufficientCoin.coinObjectId;
273
+ } else {
274
+ // Need to merge coins - use the first coin as base
275
+ const baseCoin = usdcCoins.data[0];
276
+ coinToUse = baseCoin.coinObjectId;
277
+
278
+ // Merge other coins into the first one
279
+ const otherCoins = usdcCoins.data.slice(1).map(c => c.coinObjectId);
280
+ if (otherCoins.length > 0) {
281
+ tx.mergeCoins(tx.object(coinToUse), otherCoins.map(id => tx.object(id)));
282
+ }
283
+ }
284
+
285
+ // Split the exact amount and transfer to recipient
286
+ const [paymentCoin] = tx.splitCoins(tx.object(coinToUse), [amount]);
287
+ tx.transferObjects([paymentCoin], recipient);
288
+
289
+ // Build transaction bytes
290
+ const txBytes = await tx.build({ client });
291
+
292
+ // Sign the transaction
293
+ let signedTx: { signature: string; bytes: string };
294
+
295
+ try {
296
+ // Try new API first (signTransaction)
297
+ if (this.provider.signTransaction) {
298
+ signedTx = await this.provider.signTransaction({
299
+ transaction: txBytes,
300
+ account: this.address,
301
+ chain: chainConfig.name === 'sui-testnet' ? 'sui:testnet' : 'sui:mainnet',
302
+ });
303
+ }
304
+ // Fall back to old API (signTransactionBlock)
305
+ else if (this.provider.signTransactionBlock) {
306
+ const result = await this.provider.signTransactionBlock({
307
+ transactionBlock: txBytes,
308
+ account: this.address,
309
+ chain: chainConfig.name === 'sui-testnet' ? 'sui:testnet' : 'sui:mainnet',
310
+ });
311
+ signedTx = {
312
+ signature: result.signature,
313
+ bytes: result.transactionBlockBytes,
314
+ };
315
+ } else {
316
+ throw new X402Error('Wallet does not support transaction signing', 'WALLET_NOT_SUPPORTED');
317
+ }
318
+ } catch (error: unknown) {
319
+ if (error instanceof X402Error) throw error;
320
+ if (error instanceof Error) {
321
+ if (error.message.includes('User rejected') || error.message.includes('rejected')) {
322
+ throw new X402Error('Signature rejected by user', 'SIGNATURE_REJECTED');
323
+ }
324
+ }
325
+ throw new X402Error(
326
+ `Failed to sign transaction: ${error instanceof Error ? error.message : 'Unknown error'}`,
327
+ 'PAYMENT_FAILED',
328
+ error
329
+ );
330
+ }
331
+
332
+ // Build the payload for the facilitator
333
+ // CRITICAL: coinObjectId is REQUIRED by the facilitator for deserialization
334
+ const payload: SuiPaymentPayload = {
335
+ transactionBytes: signedTx.bytes,
336
+ senderSignature: signedTx.signature,
337
+ from: this.address,
338
+ to: recipient,
339
+ amount: amount.toString(),
340
+ coinObjectId: coinToUse,
341
+ };
342
+
343
+ return JSON.stringify(payload);
344
+ }
345
+
346
+ /**
347
+ * Encode Sui payment as X-PAYMENT header
348
+ *
349
+ * @param paymentPayload - JSON-encoded payment payload from signPayment()
350
+ * @param chainConfig - Chain configuration (optional, defaults to 'sui')
351
+ * @param version - x402 protocol version (1 or 2, defaults to 1)
352
+ * @returns Base64-encoded X-PAYMENT header value
353
+ */
354
+ encodePaymentHeader(
355
+ paymentPayload: string,
356
+ chainConfig?: ChainConfig,
357
+ version: X402Version = 1
358
+ ): string {
359
+ const payload = JSON.parse(paymentPayload) as SuiPaymentPayload;
360
+
361
+ // Use chain name from config, or default to 'sui' for backward compatibility
362
+ const networkName = chainConfig?.name || 'sui';
363
+
364
+ // Format in x402 standard format (v1 or v2)
365
+ const x402Payload = version === 2
366
+ ? {
367
+ x402Version: 2 as const,
368
+ scheme: 'exact' as const,
369
+ network: chainToCAIP2(networkName), // CAIP-2 format for v2
370
+ payload: payload,
371
+ }
372
+ : {
373
+ x402Version: 1 as const,
374
+ scheme: 'exact' as const,
375
+ network: networkName, // Plain chain name for v1
376
+ payload: payload,
377
+ };
378
+
379
+ return btoa(JSON.stringify(x402Payload));
380
+ }
381
+
382
+ // Private helpers
383
+
384
+ private async getSuiWalletProvider(): Promise<SuiWalletProvider | null> {
385
+ if (typeof window === 'undefined') return null;
386
+
387
+ const win = window as Window & { suiWallet?: SuiWalletProvider };
388
+
389
+ // Check for Sui Wallet
390
+ if (win.suiWallet) {
391
+ return win.suiWallet;
392
+ }
393
+
394
+ // Wait a bit for wallet to inject itself
395
+ for (let i = 0; i < 5; i++) {
396
+ await new Promise(resolve => setTimeout(resolve, 100));
397
+ if (win.suiWallet) {
398
+ return win.suiWallet;
399
+ }
400
+ }
401
+
402
+ return null;
403
+ }
404
+
405
+ /**
406
+ * Get or create a Sui client for a specific chain
407
+ */
408
+ private async getClient(chainConfig?: ChainConfig): Promise<InstanceType<typeof SuiClient>> {
409
+ await loadSuiDeps();
410
+
411
+ const config = chainConfig || getChainByName('sui');
412
+ if (!config) {
413
+ throw new X402Error('Chain config not found', 'CHAIN_NOT_SUPPORTED');
414
+ }
415
+
416
+ // Cache by RPC URL
417
+ const cacheKey = config.rpcUrl;
418
+
419
+ if (this.clients.has(cacheKey)) {
420
+ return this.clients.get(cacheKey)!;
421
+ }
422
+
423
+ const client = new SuiClient({ url: config.rpcUrl });
424
+ this.clients.set(cacheKey, client);
425
+
426
+ return client;
427
+ }
428
+ }
429
+
430
+ // Default export
431
+ export default SuiProvider;
@@ -16,10 +16,11 @@
16
16
  * - 'stellar': Stellar network (use Soroban)
17
17
  * - 'near': NEAR Protocol (use NEP-366)
18
18
  * - 'algorand': Algorand network (use ASA transfers with atomic transactions)
19
+ * - 'sui': Sui blockchain (use sponsored transactions)
19
20
  *
20
21
  * @deprecated 'solana' type is deprecated, use 'svm' instead
21
22
  */
22
- export type NetworkType = 'evm' | 'svm' | 'solana' | 'stellar' | 'near' | 'algorand';
23
+ export type NetworkType = 'evm' | 'svm' | 'solana' | 'stellar' | 'near' | 'algorand' | 'sui';
23
24
 
24
25
  /**
25
26
  * Supported stablecoin token types
@@ -202,6 +203,7 @@ export interface PaymentInfo {
202
203
  near?: string;
203
204
  stellar?: string;
204
205
  algorand?: string;
206
+ sui?: string;
205
207
  };
206
208
  /** Facilitator address (for Solana fee payer) */
207
209
  facilitator?: string;
@@ -380,6 +382,31 @@ export interface AlgorandPaymentPayload {
380
382
  paymentGroup: string[];
381
383
  }
382
384
 
385
+ /**
386
+ * Sui payment payload (sponsored transaction)
387
+ *
388
+ * Uses Sui sponsored transactions where:
389
+ * - User creates a programmable transaction for USDC transfer
390
+ * - User signs the transaction
391
+ * - Facilitator sponsors (pays gas in SUI) and submits
392
+ *
393
+ * User pays: ZERO SUI
394
+ */
395
+ export interface SuiPaymentPayload {
396
+ /** Base64-encoded BCS serialized TransactionData */
397
+ transactionBytes: string;
398
+ /** Base64-encoded user signature */
399
+ senderSignature: string;
400
+ /** Sender address (0x + 64 hex chars) */
401
+ from: string;
402
+ /** Recipient address (0x + 64 hex chars) */
403
+ to: string;
404
+ /** Amount in base units (string to handle large numbers) */
405
+ amount: string;
406
+ /** Coin object ID used for the transfer (REQUIRED by facilitator) */
407
+ coinObjectId: string;
408
+ }
409
+
383
410
  /**
384
411
  * Union type for all payment payloads
385
412
  */
@@ -388,7 +415,8 @@ export type PaymentPayload =
388
415
  | SolanaPaymentPayload
389
416
  | StellarPaymentPayload
390
417
  | NEARPaymentPayload
391
- | AlgorandPaymentPayload;
418
+ | AlgorandPaymentPayload
419
+ | SuiPaymentPayload;
392
420
 
393
421
  // ============================================================================
394
422
  // X402 HEADER TYPES (v1 and v2)
@@ -425,6 +453,9 @@ export const CAIP2_IDENTIFIERS: Record<string, string> = {
425
453
  // Algorand
426
454
  algorand: 'algorand:mainnet',
427
455
  'algorand-testnet': 'algorand:testnet',
456
+ // Sui
457
+ sui: 'sui:mainnet',
458
+ 'sui-testnet': 'sui:testnet',
428
459
  };
429
460
 
430
461
  /**
@@ -522,6 +553,24 @@ export interface X402AlgorandPayload {
522
553
  paymentGroup: string[];
523
554
  }
524
555
 
556
+ /**
557
+ * Sui-specific payload in x402 header (sponsored transaction)
558
+ */
559
+ export interface X402SuiPayload {
560
+ /** BCS-encoded transaction bytes (base64) */
561
+ transactionBytes: string;
562
+ /** User's signature on the transaction (base64) */
563
+ senderSignature: string;
564
+ /** Sender's Sui address (0x...) */
565
+ from: string;
566
+ /** Recipient's Sui address (0x...) */
567
+ to: string;
568
+ /** Amount in smallest unit (string to avoid precision issues) */
569
+ amount: string;
570
+ /** Coin object ID used for the transfer (REQUIRED by facilitator) */
571
+ coinObjectId: string;
572
+ }
573
+
525
574
  /**
526
575
  * Union of all x402 payload types
527
576
  */
@@ -530,7 +579,8 @@ export type X402PayloadData =
530
579
  | X402SolanaPayload
531
580
  | X402StellarPayload
532
581
  | X402NEARPayload
533
- | X402AlgorandPayload;
582
+ | X402AlgorandPayload
583
+ | X402SuiPayload;
534
584
 
535
585
  // ============================================================================
536
586
  // CLIENT CONFIGURATION
@@ -656,6 +706,8 @@ export type X402EventHandler<E extends X402Event> = (data: X402EventData[E]) =>
656
706
  export type X402ErrorCode =
657
707
  | 'WALLET_NOT_FOUND'
658
708
  | 'WALLET_NOT_CONNECTED'
709
+ | 'WALLET_NOT_SUPPORTED'
710
+ | 'WALLET_CONNECTION_FAILED'
659
711
  | 'WALLET_CONNECTION_REJECTED'
660
712
  | 'WALLET_CONNECTION_TIMEOUT'
661
713
  | 'CHAIN_NOT_SUPPORTED'