uvd-x402-sdk 2.3.0 → 2.5.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 +7 -1
- package/dist/adapters/index.d.ts +7 -1
- package/dist/adapters/index.js +87 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +87 -3
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +244 -146
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +243 -147
- package/dist/index.mjs.map +1 -1
- package/dist/providers/evm/index.d.mts +7 -2
- package/dist/providers/evm/index.d.ts +7 -2
- package/dist/providers/evm/index.js +101 -13
- package/dist/providers/evm/index.js.map +1 -1
- package/dist/providers/evm/index.mjs +101 -13
- package/dist/providers/evm/index.mjs.map +1 -1
- package/dist/providers/near/index.d.mts +6 -2
- package/dist/providers/near/index.d.ts +6 -2
- package/dist/providers/near/index.js +562 -5
- package/dist/providers/near/index.js.map +1 -1
- package/dist/providers/near/index.mjs +562 -5
- package/dist/providers/near/index.mjs.map +1 -1
- package/dist/providers/solana/index.d.mts +6 -4
- package/dist/providers/solana/index.d.ts +6 -4
- package/dist/providers/solana/index.js +33 -7
- package/dist/providers/solana/index.js.map +1 -1
- package/dist/providers/solana/index.mjs +33 -7
- package/dist/providers/solana/index.mjs.map +1 -1
- package/dist/providers/stellar/index.d.mts +6 -2
- package/dist/providers/stellar/index.d.ts +6 -2
- package/dist/providers/stellar/index.js +568 -11
- package/dist/providers/stellar/index.js.map +1 -1
- package/dist/providers/stellar/index.mjs +568 -11
- package/dist/providers/stellar/index.mjs.map +1 -1
- package/dist/react/index.js +96 -12
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +96 -12
- package/dist/react/index.mjs.map +1 -1
- package/dist/utils/index.d.mts +30 -1
- package/dist/utils/index.d.ts +30 -1
- package/dist/utils/index.js +101 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +100 -1
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/wagmi.ts +20 -5
- package/src/client/X402Client.ts +32 -15
- package/src/index.ts +3 -0
- package/src/providers/evm/index.ts +40 -15
- package/src/providers/near/index.ts +25 -8
- package/src/providers/solana/index.ts +29 -10
- package/src/providers/stellar/index.ts +31 -14
- package/src/utils/index.ts +5 -0
- package/src/utils/validation.ts +151 -0
|
@@ -43,9 +43,11 @@ import type {
|
|
|
43
43
|
PaymentInfo,
|
|
44
44
|
SolanaPaymentPayload,
|
|
45
45
|
WalletAdapter,
|
|
46
|
+
X402Version,
|
|
46
47
|
} from '../../types';
|
|
47
48
|
import { X402Error } from '../../types';
|
|
48
49
|
import { getChainByName } from '../../chains';
|
|
50
|
+
import { chainToCAIP2 } from '../../utils';
|
|
49
51
|
|
|
50
52
|
/**
|
|
51
53
|
* Browser-compatible base64 encoding for Uint8Array
|
|
@@ -395,24 +397,41 @@ export class SVMProvider implements WalletAdapter {
|
|
|
395
397
|
/**
|
|
396
398
|
* Encode SVM payment as X-PAYMENT header
|
|
397
399
|
*
|
|
398
|
-
* @param paymentPayload -
|
|
399
|
-
* @param chainConfig -
|
|
400
|
+
* @param paymentPayload - JSON-encoded payment payload from signPayment()
|
|
401
|
+
* @param chainConfig - Chain configuration (optional, defaults to 'solana')
|
|
402
|
+
* @param version - x402 protocol version (1 or 2, defaults to 1)
|
|
403
|
+
* @returns Base64-encoded X-PAYMENT header value
|
|
400
404
|
*/
|
|
401
|
-
encodePaymentHeader(
|
|
405
|
+
encodePaymentHeader(
|
|
406
|
+
paymentPayload: string,
|
|
407
|
+
chainConfig?: ChainConfig,
|
|
408
|
+
version: X402Version = 1
|
|
409
|
+
): string {
|
|
402
410
|
const payload = JSON.parse(paymentPayload) as SolanaPaymentPayload;
|
|
403
411
|
|
|
404
412
|
// Use chain name from config, or default to 'solana' for backward compatibility
|
|
405
413
|
const networkName = chainConfig?.name || 'solana';
|
|
406
414
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
network: networkName,
|
|
411
|
-
payload: {
|
|
412
|
-
transaction: payload.transaction,
|
|
413
|
-
},
|
|
415
|
+
// Build the payload data
|
|
416
|
+
const payloadData = {
|
|
417
|
+
transaction: payload.transaction,
|
|
414
418
|
};
|
|
415
419
|
|
|
420
|
+
// Format in x402 standard format (v1 or v2)
|
|
421
|
+
const x402Payload = version === 2
|
|
422
|
+
? {
|
|
423
|
+
x402Version: 2 as const,
|
|
424
|
+
scheme: 'exact' as const,
|
|
425
|
+
network: chainToCAIP2(networkName), // CAIP-2 format for v2
|
|
426
|
+
payload: payloadData,
|
|
427
|
+
}
|
|
428
|
+
: {
|
|
429
|
+
x402Version: 1 as const,
|
|
430
|
+
scheme: 'exact' as const,
|
|
431
|
+
network: networkName, // Plain chain name for v1
|
|
432
|
+
payload: payloadData,
|
|
433
|
+
};
|
|
434
|
+
|
|
416
435
|
return btoa(JSON.stringify(x402Payload));
|
|
417
436
|
}
|
|
418
437
|
|
|
@@ -25,8 +25,10 @@ import type {
|
|
|
25
25
|
PaymentInfo,
|
|
26
26
|
StellarPaymentPayload,
|
|
27
27
|
WalletAdapter,
|
|
28
|
+
X402Version,
|
|
28
29
|
} from '../../types';
|
|
29
30
|
import { X402Error } from '../../types';
|
|
31
|
+
import { chainToCAIP2 } from '../../utils';
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Browser-compatible text to Uint8Array encoding
|
|
@@ -343,25 +345,40 @@ export class StellarProvider implements WalletAdapter {
|
|
|
343
345
|
|
|
344
346
|
/**
|
|
345
347
|
* Encode Stellar payment as X-PAYMENT header
|
|
348
|
+
*
|
|
349
|
+
* @param paymentPayload - JSON-encoded payment payload from signPayment()
|
|
350
|
+
* @param version - x402 protocol version (1 or 2, defaults to 1)
|
|
351
|
+
* @returns Base64-encoded X-PAYMENT header value
|
|
346
352
|
*/
|
|
347
|
-
encodePaymentHeader(paymentPayload: string): string {
|
|
353
|
+
encodePaymentHeader(paymentPayload: string, version: X402Version = 1): string {
|
|
348
354
|
const payload = JSON.parse(paymentPayload) as StellarPaymentPayload;
|
|
349
355
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
authorizationEntryXdr: payload.authorizationEntryXdr,
|
|
360
|
-
nonce: payload.nonce,
|
|
361
|
-
signatureExpirationLedger: payload.signatureExpirationLedger,
|
|
362
|
-
},
|
|
356
|
+
// Build the payload data
|
|
357
|
+
const payloadData = {
|
|
358
|
+
from: payload.from,
|
|
359
|
+
to: payload.to,
|
|
360
|
+
amount: payload.amount,
|
|
361
|
+
tokenContract: payload.tokenContract,
|
|
362
|
+
authorizationEntryXdr: payload.authorizationEntryXdr,
|
|
363
|
+
nonce: payload.nonce,
|
|
364
|
+
signatureExpirationLedger: payload.signatureExpirationLedger,
|
|
363
365
|
};
|
|
364
366
|
|
|
367
|
+
// Format in x402 standard format (v1 or v2)
|
|
368
|
+
const x402Payload = version === 2
|
|
369
|
+
? {
|
|
370
|
+
x402Version: 2 as const,
|
|
371
|
+
scheme: 'exact' as const,
|
|
372
|
+
network: chainToCAIP2('stellar'), // CAIP-2 format for v2
|
|
373
|
+
payload: payloadData,
|
|
374
|
+
}
|
|
375
|
+
: {
|
|
376
|
+
x402Version: 1 as const,
|
|
377
|
+
scheme: 'exact' as const,
|
|
378
|
+
network: 'stellar', // Plain chain name for v1
|
|
379
|
+
payload: payloadData,
|
|
380
|
+
};
|
|
381
|
+
|
|
365
382
|
return btoa(JSON.stringify(x402Payload));
|
|
366
383
|
}
|
|
367
384
|
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* uvd-x402-sdk - Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions for validating payment parameters to prevent
|
|
5
|
+
* invalid or empty values from being processed.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { X402Error } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Regular expression for validating Ethereum addresses
|
|
12
|
+
*/
|
|
13
|
+
const ETH_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Regular expression for validating Solana addresses (base58, 32-44 chars)
|
|
17
|
+
*/
|
|
18
|
+
const SOLANA_ADDRESS_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Regular expression for validating Stellar addresses (G... format)
|
|
22
|
+
*/
|
|
23
|
+
const STELLAR_ADDRESS_REGEX = /^G[A-Z2-7]{55}$/;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Regular expression for validating NEAR addresses
|
|
27
|
+
*/
|
|
28
|
+
const NEAR_ADDRESS_REGEX = /^[a-z0-9._-]+$/;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate that a recipient address is present and valid
|
|
32
|
+
*
|
|
33
|
+
* This function ensures that:
|
|
34
|
+
* 1. The recipient is not null, undefined, or empty
|
|
35
|
+
* 2. The recipient is not just whitespace
|
|
36
|
+
* 3. For EVM, it's a valid checksummed or lowercase 0x address
|
|
37
|
+
*
|
|
38
|
+
* @param recipient - The recipient address to validate
|
|
39
|
+
* @param networkType - Optional network type for format validation
|
|
40
|
+
* @throws X402Error with code 'INVALID_RECIPIENT' if validation fails
|
|
41
|
+
*/
|
|
42
|
+
export function validateRecipient(
|
|
43
|
+
recipient: string | undefined | null,
|
|
44
|
+
networkType?: 'evm' | 'svm' | 'solana' | 'stellar' | 'near'
|
|
45
|
+
): asserts recipient is string {
|
|
46
|
+
// Check for null, undefined, or empty
|
|
47
|
+
if (!recipient) {
|
|
48
|
+
throw new X402Error(
|
|
49
|
+
'Recipient address is required. The payTo/recipient field cannot be empty. ' +
|
|
50
|
+
'Please provide a valid recipient address where payments should be sent.',
|
|
51
|
+
'INVALID_RECIPIENT'
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check for whitespace-only
|
|
56
|
+
const trimmed = recipient.trim();
|
|
57
|
+
if (trimmed === '') {
|
|
58
|
+
throw new X402Error(
|
|
59
|
+
'Recipient address cannot be empty or whitespace. ' +
|
|
60
|
+
'Please provide a valid recipient address.',
|
|
61
|
+
'INVALID_RECIPIENT'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Network-specific validation
|
|
66
|
+
if (networkType) {
|
|
67
|
+
switch (networkType) {
|
|
68
|
+
case 'evm':
|
|
69
|
+
if (!ETH_ADDRESS_REGEX.test(trimmed)) {
|
|
70
|
+
throw new X402Error(
|
|
71
|
+
`Invalid EVM recipient address: "${trimmed}". ` +
|
|
72
|
+
'Expected a 40-character hexadecimal address starting with 0x.',
|
|
73
|
+
'INVALID_RECIPIENT'
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'svm':
|
|
79
|
+
case 'solana':
|
|
80
|
+
if (!SOLANA_ADDRESS_REGEX.test(trimmed)) {
|
|
81
|
+
throw new X402Error(
|
|
82
|
+
`Invalid Solana recipient address: "${trimmed}". ` +
|
|
83
|
+
'Expected a base58-encoded public key (32-44 characters).',
|
|
84
|
+
'INVALID_RECIPIENT'
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'stellar':
|
|
90
|
+
if (!STELLAR_ADDRESS_REGEX.test(trimmed)) {
|
|
91
|
+
throw new X402Error(
|
|
92
|
+
`Invalid Stellar recipient address: "${trimmed}". ` +
|
|
93
|
+
'Expected a G-prefixed public key (56 characters).',
|
|
94
|
+
'INVALID_RECIPIENT'
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
case 'near':
|
|
100
|
+
if (!NEAR_ADDRESS_REGEX.test(trimmed) || trimmed.length > 64) {
|
|
101
|
+
throw new X402Error(
|
|
102
|
+
`Invalid NEAR recipient address: "${trimmed}". ` +
|
|
103
|
+
'Expected a valid NEAR account ID.',
|
|
104
|
+
'INVALID_RECIPIENT'
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Validate payment amount
|
|
114
|
+
*
|
|
115
|
+
* Ensures the amount is a valid positive number string
|
|
116
|
+
*
|
|
117
|
+
* @param amount - The amount string to validate
|
|
118
|
+
* @throws X402Error with code 'INVALID_AMOUNT' if validation fails
|
|
119
|
+
*/
|
|
120
|
+
export function validateAmount(amount: string | undefined | null): asserts amount is string {
|
|
121
|
+
if (!amount) {
|
|
122
|
+
throw new X402Error(
|
|
123
|
+
'Payment amount is required.',
|
|
124
|
+
'INVALID_AMOUNT'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const trimmed = amount.trim();
|
|
129
|
+
if (trimmed === '') {
|
|
130
|
+
throw new X402Error(
|
|
131
|
+
'Payment amount cannot be empty.',
|
|
132
|
+
'INVALID_AMOUNT'
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Parse as number
|
|
137
|
+
const num = parseFloat(trimmed);
|
|
138
|
+
if (isNaN(num)) {
|
|
139
|
+
throw new X402Error(
|
|
140
|
+
`Invalid payment amount: "${trimmed}". Expected a valid number.`,
|
|
141
|
+
'INVALID_AMOUNT'
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (num <= 0) {
|
|
146
|
+
throw new X402Error(
|
|
147
|
+
`Payment amount must be positive. Got: ${trimmed}`,
|
|
148
|
+
'INVALID_AMOUNT'
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|