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
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # uvd-x402-sdk
2
2
 
3
- Gasless crypto payments across 16 blockchain networks using the x402 protocol.
3
+ Gasless crypto payments across 17 blockchain networks using the x402 protocol.
4
4
 
5
5
  Users sign a message or transaction, and the Ultravioleta facilitator handles on-chain settlement. No gas fees for users.
6
6
 
7
7
  ## Features
8
8
 
9
- - **16 Networks**: EVM (10), Solana, Fogo, Stellar, NEAR, Algorand (2)
9
+ - **17 Networks**: EVM (10), Solana, Fogo, Stellar, NEAR, Algorand, Sui
10
10
  - **Multi-Stablecoin**: USDC, EURC, AUSD, PYUSD, USDT
11
11
  - **x402 v1 & v2**: Both protocol versions with auto-detection
12
12
  - **Gasless**: Facilitator pays all network fees
@@ -36,6 +36,9 @@ npm install @near-wallet-selector/core @near-wallet-selector/my-near-wallet
36
36
 
37
37
  # Algorand
38
38
  npm install algosdk lute-connect
39
+
40
+ # Sui
41
+ npm install @mysten/sui
39
42
  ```
40
43
 
41
44
  ## Quick Start
@@ -98,6 +101,29 @@ Algorand uses atomic transaction groups:
98
101
  - Transaction 0: Fee payment (unsigned, facilitator signs)
99
102
  - Transaction 1: USDC ASA transfer (signed by user)
100
103
 
104
+ ### Sui
105
+
106
+ ```typescript
107
+ import { SuiProvider } from 'uvd-x402-sdk/sui';
108
+ import { getChainByName } from 'uvd-x402-sdk';
109
+
110
+ const sui = new SuiProvider();
111
+ const address = await sui.connect(); // Sui Wallet
112
+ const chainConfig = getChainByName('sui')!;
113
+
114
+ const payload = await sui.signPayment({
115
+ recipient: '0x1234...', // 66-char Sui address
116
+ amount: '10.00',
117
+ }, chainConfig);
118
+
119
+ const header = sui.encodePaymentHeader(payload, chainConfig);
120
+ ```
121
+
122
+ Sui uses sponsored transactions:
123
+ - User creates and signs a programmable transaction block
124
+ - Facilitator sponsors gas (pays in SUI)
125
+ - User pays zero gas fees
126
+
101
127
  ### Stellar
102
128
 
103
129
  ```typescript
@@ -118,12 +144,17 @@ const header = stellar.encodePaymentHeader(payload);
118
144
 
119
145
  ### NEAR
120
146
 
147
+ > **Important:** The SDK's `NEARProvider.signPayment()` only works with **injected wallets** (browser extensions).
148
+ > For **browser-redirect wallets** like MyNearWallet via `@near-wallet-selector`, you must use the popup flow below.
149
+
150
+ #### Option 1: Injected Wallet (Browser Extension)
151
+
121
152
  ```typescript
122
153
  import { NEARProvider } from 'uvd-x402-sdk/near';
123
154
  import { getChainByName } from 'uvd-x402-sdk';
124
155
 
125
156
  const near = new NEARProvider();
126
- const accountId = await near.connect(); // MyNearWallet
157
+ const accountId = await near.connect(); // MyNearWallet browser extension
127
158
  const chainConfig = getChainByName('near')!;
128
159
 
129
160
  const payload = await near.signPayment({
@@ -134,6 +165,209 @@ const payload = await near.signPayment({
134
165
  const header = near.encodePaymentHeader(payload);
135
166
  ```
136
167
 
168
+ #### Option 2: Browser-Redirect Wallet (Popup Flow) - Recommended
169
+
170
+ MyNearWallet via `@near-wallet-selector` is a browser-redirect wallet that requires a popup flow.
171
+ This is the recommended approach and works with the custom MyNearWallet deployment that supports NEP-366.
172
+
173
+ ```typescript
174
+ import { setupWalletSelector } from '@near-wallet-selector/core';
175
+ import { setupModal } from '@near-wallet-selector/modal-ui';
176
+ import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet';
177
+ import '@near-wallet-selector/modal-ui/styles.css';
178
+
179
+ // Configuration
180
+ const NEAR_CONFIG = {
181
+ usdcContract: '17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1',
182
+ recipientAccount: 'merchant.near',
183
+ // Custom MyNearWallet with NEP-366 signDelegateAction support
184
+ walletUrl: 'https://mynearwallet.ultravioletadao.xyz',
185
+ };
186
+
187
+ // Step 1: Initialize wallet selector
188
+ const selector = await setupWalletSelector({
189
+ network: 'mainnet',
190
+ modules: [
191
+ setupMyNearWallet({ walletUrl: NEAR_CONFIG.walletUrl }),
192
+ ],
193
+ });
194
+
195
+ const modal = setupModal(selector, {
196
+ contractId: NEAR_CONFIG.usdcContract,
197
+ });
198
+
199
+ // Step 2: Connect wallet
200
+ modal.show(); // User selects wallet
201
+ // Wait for connection via selector.store.observable.subscribe()
202
+ const state = selector.store.getState();
203
+ const accountId = state.accounts[0].accountId;
204
+
205
+ // Step 3: Create payment with popup flow
206
+ async function createNearPayment(amount: string): Promise<string> {
207
+ const amountRaw = Math.floor(parseFloat(amount) * 1_000_000); // 6 decimals
208
+
209
+ // Get access key info and block height from RPC
210
+ const [accessKeyInfo, blockInfo] = await Promise.all([
211
+ getNearAccessKeyInfo(accountId),
212
+ getNearBlockHeight(),
213
+ ]);
214
+
215
+ const nonce = accessKeyInfo.nonce + 1;
216
+ const maxBlockHeight = blockInfo.blockHeight + 1000; // ~17 minutes
217
+
218
+ // Build wallet URL for signDelegateAction
219
+ const popupUrl = new URL(NEAR_CONFIG.walletUrl);
220
+ popupUrl.pathname = '/sign-delegate-action';
221
+ popupUrl.searchParams.set('receiverId', NEAR_CONFIG.usdcContract);
222
+ popupUrl.searchParams.set('actions', JSON.stringify([{
223
+ methodName: 'ft_transfer',
224
+ args: {
225
+ receiver_id: NEAR_CONFIG.recipientAccount,
226
+ amount: amountRaw.toString(),
227
+ memo: 'x402 payment',
228
+ },
229
+ gas: '30000000000000', // 30 TGas
230
+ deposit: '1', // 1 yoctoNEAR
231
+ }]));
232
+ popupUrl.searchParams.set('callbackUrl', window.location.origin + '/near-callback');
233
+ popupUrl.searchParams.set('meta', JSON.stringify({
234
+ sender: accountId,
235
+ nonce,
236
+ maxBlockHeight,
237
+ publicKey: accessKeyInfo.publicKey,
238
+ }));
239
+
240
+ // Open popup
241
+ const popup = window.open(popupUrl.toString(), 'nearWallet', 'width=500,height=700');
242
+ if (!popup) throw new Error('Popup blocked. Please allow popups.');
243
+
244
+ // Wait for redirect with signedDelegateAction
245
+ const signedDelegateAction = await new Promise<string>((resolve, reject) => {
246
+ const checkInterval = setInterval(() => {
247
+ if (popup.closed) {
248
+ clearInterval(checkInterval);
249
+ reject(new Error('Wallet popup closed'));
250
+ return;
251
+ }
252
+ try {
253
+ const url = popup.location.href;
254
+ if (url.includes('signedDelegateAction=')) {
255
+ clearInterval(checkInterval);
256
+ popup.close();
257
+ const params = new URLSearchParams(new URL(url).search);
258
+ const errorCode = params.get('errorCode');
259
+ if (errorCode) {
260
+ reject(new Error(params.get('errorMessage') || errorCode));
261
+ return;
262
+ }
263
+ resolve(params.get('signedDelegateAction')!);
264
+ }
265
+ } catch { /* cross-origin, keep waiting */ }
266
+ }, 500);
267
+
268
+ setTimeout(() => {
269
+ clearInterval(checkInterval);
270
+ if (!popup.closed) popup.close();
271
+ reject(new Error('Popup timeout'));
272
+ }, 300000); // 5 min timeout
273
+ });
274
+
275
+ // Return x402 payload
276
+ return JSON.stringify({
277
+ signedDelegateAction,
278
+ network: 'near',
279
+ });
280
+ }
281
+
282
+ // Helper: Get access key info from NEAR RPC
283
+ async function getNearAccessKeyInfo(accountId: string) {
284
+ const rpcUrls = [
285
+ 'https://near.drpc.org',
286
+ 'https://rpc.mainnet.near.org',
287
+ ];
288
+
289
+ for (const rpcUrl of rpcUrls) {
290
+ try {
291
+ const response = await fetch(rpcUrl, {
292
+ method: 'POST',
293
+ headers: { 'Content-Type': 'application/json' },
294
+ body: JSON.stringify({
295
+ jsonrpc: '2.0',
296
+ id: 'dontcare',
297
+ method: 'query',
298
+ params: {
299
+ request_type: 'view_access_key_list',
300
+ finality: 'final',
301
+ account_id: accountId,
302
+ },
303
+ }),
304
+ });
305
+ const data = await response.json();
306
+ if (data.error) continue;
307
+
308
+ const fullAccessKey = data.result.keys.find(
309
+ (k: any) => k.access_key.permission === 'FullAccess'
310
+ );
311
+ return {
312
+ nonce: fullAccessKey.access_key.nonce,
313
+ publicKey: fullAccessKey.public_key,
314
+ };
315
+ } catch { continue; }
316
+ }
317
+ throw new Error('Failed to get NEAR access key info');
318
+ }
319
+
320
+ // Helper: Get block height from NEAR RPC
321
+ async function getNearBlockHeight() {
322
+ const rpcUrls = [
323
+ 'https://near.drpc.org',
324
+ 'https://rpc.mainnet.near.org',
325
+ ];
326
+
327
+ for (const rpcUrl of rpcUrls) {
328
+ try {
329
+ const response = await fetch(rpcUrl, {
330
+ method: 'POST',
331
+ headers: { 'Content-Type': 'application/json' },
332
+ body: JSON.stringify({
333
+ jsonrpc: '2.0',
334
+ id: 'dontcare',
335
+ method: 'block',
336
+ params: { finality: 'final' },
337
+ }),
338
+ });
339
+ const data = await response.json();
340
+ if (data.error) continue;
341
+ return { blockHeight: data.result.header.height };
342
+ } catch { continue; }
343
+ }
344
+ throw new Error('Failed to get NEAR block height');
345
+ }
346
+
347
+ // Step 4: Encode payment header
348
+ function encodeNearPaymentHeader(payload: string): string {
349
+ const parsed = JSON.parse(payload);
350
+ const x402Payload = {
351
+ x402Version: 1,
352
+ scheme: 'exact',
353
+ network: 'near',
354
+ payload: {
355
+ signedDelegateAction: parsed.signedDelegateAction,
356
+ },
357
+ };
358
+ return btoa(JSON.stringify(x402Payload));
359
+ }
360
+
361
+ // Usage
362
+ const payload = await createNearPayment('10.00');
363
+ const header = encodeNearPaymentHeader(payload);
364
+ await fetch('/api/purchase', {
365
+ headers: { 'X-PAYMENT': header },
366
+ });
367
+ ```
368
+
369
+ See [402milly's full implementation](https://github.com/UltravioletaDAO/402milly/blob/main/frontend/src/services/x402-sdk.ts) for a production-ready example.
370
+
137
371
  ## Wagmi/RainbowKit
138
372
 
139
373
  ```typescript
@@ -220,14 +454,19 @@ const header = svm.encodePaymentHeader(payload, chainConfig);
220
454
  | Solana | USDC, AUSD | Phantom |
221
455
  | Fogo | USDC | Phantom |
222
456
 
223
- ### Algorand (2)
457
+ ### Algorand
224
458
 
225
459
  | Network | USDC ASA | Wallet |
226
460
  |---------|----------|--------|
227
461
  | Algorand | 31566704 | Lute, Pera |
228
- | Algorand Testnet | 10458941 | Lute, Pera |
229
462
 
230
- ### Other (2)
463
+ ### Sui
464
+
465
+ | Network | Tokens | Wallet |
466
+ |---------|--------|--------|
467
+ | Sui | USDC, AUSD | Sui Wallet |
468
+
469
+ ### Other
231
470
 
232
471
  | Network | Wallet |
233
472
  |---------|--------|
@@ -247,10 +486,12 @@ FACILITATOR_ADDRESSES.solana; // F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq
247
486
  FACILITATOR_ADDRESSES.algorand; // KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I
248
487
  FACILITATOR_ADDRESSES.stellar; // GCHPGXJT2WFFRFCA5TV4G4E3PMMXLNIDUH27PKDYA4QJ2XGYZWGFZNHB
249
488
  FACILITATOR_ADDRESSES.near; // uvd-facilitator.near
489
+ FACILITATOR_ADDRESSES.sui; // 0xe7bbf2b13f7d72714760aa16e024fa1b35a978793f9893d0568a4fbf356a764a
250
490
 
251
491
  // Or get by chain name
252
492
  getFacilitatorAddress('algorand'); // KIMS5H6...
253
493
  getFacilitatorAddress('base', 'evm'); // 0x1030...
494
+ getFacilitatorAddress('sui'); // 0xe7bbf...
254
495
  ```
255
496
 
256
497
  ## Backend
@@ -1,4 +1,4 @@
1
- import { l as X402Version, f as PaymentResult } from '../index-BYIugZlE.mjs';
1
+ import { m as X402Version, f as PaymentResult } from '../index-BE5cH7oS.mjs';
2
2
 
3
3
  /**
4
4
  * uvd-x402-sdk - Wagmi/Viem Adapter
@@ -1,4 +1,4 @@
1
- import { l as X402Version, f as PaymentResult } from '../index-BYIugZlE.js';
1
+ import { m as X402Version, f as PaymentResult } from '../index-BE5cH7oS.js';
2
2
 
3
3
  /**
4
4
  * uvd-x402-sdk - Wagmi/Viem Adapter
@@ -613,6 +613,84 @@ var SUPPORTED_CHAINS = {
613
613
  facilitatorUrl: DEFAULT_FACILITATOR_URL,
614
614
  enabled: true
615
615
  }
616
+ },
617
+ // ============================================================================
618
+ // SUI (2 networks) - Uses sponsored transactions (facilitator pays gas)
619
+ // ============================================================================
620
+ sui: {
621
+ chainId: 0,
622
+ // Non-EVM
623
+ chainIdHex: "0x0",
624
+ name: "sui",
625
+ displayName: "Sui",
626
+ networkType: "sui",
627
+ rpcUrl: "https://fullnode.mainnet.sui.io:443",
628
+ explorerUrl: "https://suiscan.xyz/mainnet",
629
+ nativeCurrency: {
630
+ name: "Sui",
631
+ symbol: "SUI",
632
+ decimals: 9
633
+ },
634
+ usdc: {
635
+ // USDC coin type on Sui mainnet
636
+ address: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
637
+ decimals: 6,
638
+ name: "USDC",
639
+ version: "1"
640
+ },
641
+ tokens: {
642
+ usdc: {
643
+ address: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
644
+ decimals: 6,
645
+ name: "USDC",
646
+ version: "1"
647
+ },
648
+ ausd: {
649
+ // AUSD (Agora USD) coin type on Sui mainnet
650
+ address: "0x2053d08c1e2bd02791056171aab0fd12bd7cd7efad2ab8f6b9c8902f14df2ff2::ausd::AUSD",
651
+ decimals: 6,
652
+ name: "AUSD",
653
+ version: "1"
654
+ }
655
+ },
656
+ x402: {
657
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
658
+ enabled: true
659
+ }
660
+ },
661
+ "sui-testnet": {
662
+ chainId: 0,
663
+ // Non-EVM
664
+ chainIdHex: "0x0",
665
+ name: "sui-testnet",
666
+ displayName: "Sui Testnet",
667
+ networkType: "sui",
668
+ rpcUrl: "https://fullnode.testnet.sui.io:443",
669
+ explorerUrl: "https://suiscan.xyz/testnet",
670
+ nativeCurrency: {
671
+ name: "Sui",
672
+ symbol: "SUI",
673
+ decimals: 9
674
+ },
675
+ usdc: {
676
+ // USDC coin type on Sui testnet
677
+ address: "0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC",
678
+ decimals: 6,
679
+ name: "USDC",
680
+ version: "1"
681
+ },
682
+ tokens: {
683
+ usdc: {
684
+ address: "0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC",
685
+ decimals: 6,
686
+ name: "USDC",
687
+ version: "1"
688
+ }
689
+ },
690
+ x402: {
691
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
692
+ enabled: true
693
+ }
616
694
  }
617
695
  };
618
696
  function getChainByName(name) {
@@ -641,7 +719,10 @@ var CAIP2_IDENTIFIERS = {
641
719
  near: "near:mainnet",
642
720
  // Algorand
643
721
  algorand: "algorand:mainnet",
644
- "algorand-testnet": "algorand:testnet"
722
+ "algorand-testnet": "algorand:testnet",
723
+ // Sui
724
+ sui: "sui:mainnet",
725
+ "sui-testnet": "sui:testnet"
645
726
  };
646
727
  Object.fromEntries(
647
728
  Object.entries(CAIP2_IDENTIFIERS).map(([k, v]) => [v, k])