mmn-client-js 1.0.6 → 1.0.8

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/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { AxiosRequestConfig } from 'axios';
2
-
3
1
  interface JsonRpcRequest {
4
2
  jsonrpc: '2.0';
5
3
  method: string;
@@ -91,7 +89,6 @@ interface MmnClientConfig {
91
89
  baseUrl: string;
92
90
  timeout?: number;
93
91
  headers?: Record<string, string>;
94
- axiosConfig?: AxiosRequestConfig;
95
92
  }
96
93
  interface Transaction {
97
94
  chain_id: string;
@@ -170,10 +167,20 @@ interface ZkClientConfig {
170
167
  }
171
168
 
172
169
  declare class IndexerClient {
173
- private axiosInstance;
174
170
  private endpoint;
175
171
  private chainId;
172
+ private timeout;
173
+ private headers;
176
174
  constructor(config: IndexerClientConfig);
175
+ /**
176
+ * Make HTTP request with automatic CORS handling
177
+ * Works out-of-the-box without CORS configuration
178
+ * @param method - HTTP method (GET or POST)
179
+ * @param path - API endpoint path
180
+ * @param params - URL query parameters
181
+ * @param body - Request body for POST requests
182
+ * @returns Promise resolving to response data
183
+ */
177
184
  private makeRequest;
178
185
  getTransactionByHash(hash: string): Promise<Transaction>;
179
186
  getTransactionByWallet(wallet: string, page: number | undefined, limit: number | undefined, filter: number, sortBy?: string, sortOrder?: 'asc' | 'desc'): Promise<ListTransactionResponse>;
@@ -182,7 +189,6 @@ declare class IndexerClient {
182
189
 
183
190
  declare class MmnClient {
184
191
  private config;
185
- private axiosInstance;
186
192
  private requestId;
187
193
  constructor(config: MmnClientConfig);
188
194
  private makeRequest;
@@ -193,6 +199,14 @@ declare class MmnClient {
193
199
  * @throws Error if input validation fails
194
200
  */
195
201
  private rawEd25519ToPkcs8Hex;
202
+ /**
203
+ * Encode length in ASN.1 DER format
204
+ * ASN.1 length encoding rules:
205
+ * - Short form (0-127): single byte with the length value
206
+ * - Long form (128+): first byte is 0x80 + number of length bytes, followed by length bytes
207
+ * @param length - The length value to encode
208
+ * @returns ASN.1 DER encoded length bytes
209
+ */
196
210
  private encodeLength;
197
211
  /**
198
212
  * Generate secure entropy using multiple sources for maximum compatibility
@@ -241,8 +255,9 @@ declare class MmnClient {
241
255
  declare function createMmnClient(config: MmnClientConfig): MmnClient;
242
256
 
243
257
  declare class ZkClient {
244
- private axiosInstance;
245
258
  private endpoint;
259
+ private timeout;
260
+ private headers;
246
261
  constructor(config: ZkClientConfig);
247
262
  private makeRequest;
248
263
  getZkProofs({ userId, ephemeralPublicKey, jwt, address, }: {
package/dist/index.esm.js CHANGED
@@ -1,4 +1,3 @@
1
- import axios from 'axios';
2
1
  import bs58 from 'bs58';
3
2
  import nacl from 'tweetnacl';
4
3
 
@@ -11,24 +10,74 @@ class IndexerClient {
11
10
  constructor(config) {
12
11
  this.endpoint = config.endpoint;
13
12
  this.chainId = config.chainId;
14
- this.axiosInstance = axios.create({
15
- baseURL: this.endpoint,
16
- timeout: config.timeout || 30000,
17
- headers: {
18
- 'Content-Type': 'application/json',
19
- Accept: 'application/json',
20
- ...(config.headers || {}),
21
- },
22
- });
13
+ this.timeout = config.timeout || 30000;
14
+ // Minimal headers to avoid CORS preflight issues
15
+ this.headers = {
16
+ Accept: 'application/json',
17
+ ...(config.headers || {}),
18
+ };
23
19
  }
20
+ /**
21
+ * Make HTTP request with automatic CORS handling
22
+ * Works out-of-the-box without CORS configuration
23
+ * @param method - HTTP method (GET or POST)
24
+ * @param path - API endpoint path
25
+ * @param params - URL query parameters
26
+ * @param body - Request body for POST requests
27
+ * @returns Promise resolving to response data
28
+ */
24
29
  async makeRequest(method, path, params, body) {
25
- const resp = await this.axiosInstance.request({
26
- method,
27
- url: path,
28
- params,
29
- data: body,
30
- });
31
- return resp.data;
30
+ // Build full URL
31
+ let url = `${this.endpoint}/${path}`;
32
+ // Add query parameters
33
+ if (params && Object.keys(params).length > 0) {
34
+ const searchParams = new URLSearchParams();
35
+ Object.entries(params).forEach(([key, value]) => {
36
+ searchParams.append(key, String(value));
37
+ });
38
+ url += `?${searchParams.toString()}`;
39
+ }
40
+ // Create AbortController for timeout
41
+ const controller = new AbortController();
42
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
43
+ try {
44
+ // Simple fetch with automatic CORS handling
45
+ const requestOptions = {
46
+ method,
47
+ mode: 'cors',
48
+ credentials: 'omit',
49
+ signal: controller.signal,
50
+ headers: {
51
+ Accept: 'application/json',
52
+ ...this.headers,
53
+ },
54
+ };
55
+ // Add body and Content-Type for POST requests
56
+ if (method === 'POST' && body) {
57
+ requestOptions.body = JSON.stringify(body);
58
+ requestOptions.headers['Content-Type'] =
59
+ 'application/json';
60
+ }
61
+ const response = await fetch(url, requestOptions);
62
+ clearTimeout(timeoutId);
63
+ // Handle response errors
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
66
+ }
67
+ // Parse JSON response
68
+ const data = await response.json();
69
+ return data;
70
+ }
71
+ catch (error) {
72
+ clearTimeout(timeoutId);
73
+ if (error instanceof Error) {
74
+ if (error.name === 'AbortError') {
75
+ throw new Error(`Request timeout after ${this.timeout}ms`);
76
+ }
77
+ throw error;
78
+ }
79
+ throw new Error('Request failed');
80
+ }
32
81
  }
33
82
  async getTransactionByHash(hash) {
34
83
  const path = `${this.chainId}/tx/${hash}/detail`;
@@ -7604,9 +7653,28 @@ const CRYPTO_CONSTANTS = {
7604
7653
  ED25519_PUBLIC_KEY_LENGTH: 32,
7605
7654
  MNEMONIC_ENTROPY_BITS: 128,
7606
7655
  PKCS8_VERSION: 0,
7656
+ // ASN.1 DER encoding
7607
7657
  ASN1_SEQUENCE_TAG: 0x30,
7608
7658
  ASN1_OCTET_STRING_TAG: 0x04,
7609
7659
  ASN1_INTEGER_TAG: 0x02,
7660
+ ASN1_LENGTH: 0x80,
7661
+ // Ed25519 OID bytes: 1.3.101.112 (RFC 8410)
7662
+ ED25519_OID_BYTES: [0x06, 0x03, 0x2b, 0x65, 0x70],
7663
+ // PKCS#8 structure length constants
7664
+ PKCS8_ALGORITHM_ID_LENGTH: 0x0b, // SEQUENCE length for algorithm identifier
7665
+ PKCS8_PRIVATE_KEY_OCTET_OUTER_LENGTH: 0x22, // Outer OCTET STRING length (34 bytes)
7666
+ PKCS8_PRIVATE_KEY_OCTET_INNER_LENGTH: 0x20, // Inner OCTET STRING length (32 bytes)
7667
+ };
7668
+ // PRNG (Pseudo-Random Number Generator) constants
7669
+ const PRNG_CONSTANTS = {
7670
+ // Numerical Recipes LCG
7671
+ LCG_MULTIPLIER: 1664525, // LCG multiplier (from Numerical Recipes)
7672
+ LCG_INCREMENT: 1013904223, // LCG increment
7673
+ LCG_MODULUS: 4294967296, // 2^32 modulus for LCG
7674
+ TIMESTAMP_MULTIPLIER: 2654435761, // Golden Ratio constant
7675
+ HASH_SUBSTRING_LENGTH: 8,
7676
+ BYTE_SHIFT: 24,
7677
+ BYTE_MASK: 0xff,
7610
7678
  };
7611
7679
  const TX_TYPE = {
7612
7680
  TRANSFER: 0,
@@ -7620,15 +7688,10 @@ class MmnClient {
7620
7688
  timeout: 30000,
7621
7689
  headers: {
7622
7690
  'Content-Type': 'application/json',
7691
+ Accept: 'application/json',
7623
7692
  },
7624
7693
  ...config,
7625
7694
  };
7626
- this.axiosInstance = axios.create({
7627
- baseURL: this.config.baseUrl,
7628
- timeout: this.config.timeout || 30000,
7629
- headers: this.config.headers || {},
7630
- ...(this.config.axiosConfig || {}),
7631
- });
7632
7695
  }
7633
7696
  async makeRequest(method, params) {
7634
7697
  const request = {
@@ -7637,30 +7700,35 @@ class MmnClient {
7637
7700
  params,
7638
7701
  id: ++this.requestId,
7639
7702
  };
7703
+ // Create AbortController for timeout
7704
+ const controller = new AbortController();
7705
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout || 30000);
7640
7706
  try {
7641
- const response = await this.axiosInstance.post('', request);
7642
- const result = response.data;
7707
+ const requestOptions = {
7708
+ method: 'POST',
7709
+ mode: 'cors',
7710
+ credentials: 'omit',
7711
+ signal: controller.signal,
7712
+ headers: this.config.headers || {},
7713
+ body: JSON.stringify(request),
7714
+ };
7715
+ const response = await fetch(this.config.baseUrl, requestOptions);
7716
+ clearTimeout(timeoutId);
7717
+ if (!response.ok) {
7718
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
7719
+ }
7720
+ const result = await response.json();
7643
7721
  if (result.error) {
7644
7722
  throw new Error(`JSON-RPC Error ${result.error.code}: ${result.error.message}`);
7645
7723
  }
7646
7724
  return result.result;
7647
7725
  }
7648
7726
  catch (error) {
7649
- if (axios.isAxiosError(error)) {
7650
- if (error.response) {
7651
- // Server responded with error status
7652
- throw new Error(`HTTP ${error.response.status}: ${error.response.statusText}`);
7653
- }
7654
- else if (error.request) {
7655
- // Request was made but no response received
7656
- throw new Error('Network error: No response received');
7657
- }
7658
- else {
7659
- // Something else happened
7660
- throw new Error(`Request error: ${error.message}`);
7661
- }
7662
- }
7727
+ clearTimeout(timeoutId);
7663
7728
  if (error instanceof Error) {
7729
+ if (error.name === 'AbortError') {
7730
+ throw new Error(`Request timeout after ${this.config.timeout || 30000}ms`);
7731
+ }
7664
7732
  throw error;
7665
7733
  }
7666
7734
  throw new Error('Unknown error occurred');
@@ -7681,22 +7749,31 @@ class MmnClient {
7681
7749
  throw new Error(`Ed25519 private key must be exactly ${CRYPTO_CONSTANTS.ED25519_PRIVATE_KEY_LENGTH} bytes`);
7682
7750
  }
7683
7751
  try {
7684
- // Ed25519 OID: 1.3.101.112 (constant-time)
7685
- const ED25519_OID = BufferCompat.from([0x06, 0x03, 0x2b, 0x65, 0x70]);
7752
+ // Ed25519 OID: 1.3.101.112 (RFC 8410 - Algorithm Identifiers for Ed25519)
7753
+ const ED25519_OID = BufferCompat.from(CRYPTO_CONSTANTS.ED25519_OID_BYTES);
7686
7754
  const VERSION_BYTES = BufferCompat.from([
7687
7755
  CRYPTO_CONSTANTS.ASN1_INTEGER_TAG,
7688
- 0x01,
7756
+ 0x01, // Length of integer (1 byte)
7689
7757
  CRYPTO_CONSTANTS.PKCS8_VERSION,
7690
- ]); // INTEGER 0
7691
- // Create algorithm identifier sequence
7758
+ ]);
7759
+ // Create algorithm identifier sequence (AlgorithmIdentifier)
7692
7760
  const algorithmId = BufferCompat.concat([
7693
- BufferCompat.from([0x30, 0x0b]), // SEQUENCE, length 11
7761
+ BufferCompat.from([
7762
+ CRYPTO_CONSTANTS.ASN1_SEQUENCE_TAG,
7763
+ CRYPTO_CONSTANTS.PKCS8_ALGORITHM_ID_LENGTH,
7764
+ ]),
7694
7765
  ED25519_OID,
7695
7766
  ]);
7696
- // Create private key octet string
7767
+ // Create private key octet string (wrapped Ed25519 private key)
7697
7768
  const privateKeyOctetString = BufferCompat.concat([
7698
- BufferCompat.from([CRYPTO_CONSTANTS.ASN1_OCTET_STRING_TAG, 0x22]), // OCTET STRING, length 34
7699
- BufferCompat.from([CRYPTO_CONSTANTS.ASN1_OCTET_STRING_TAG, 0x20]), // inner OCTET STRING, length 32
7769
+ BufferCompat.from([
7770
+ CRYPTO_CONSTANTS.ASN1_OCTET_STRING_TAG,
7771
+ CRYPTO_CONSTANTS.PKCS8_PRIVATE_KEY_OCTET_OUTER_LENGTH,
7772
+ ]), // OCTET STRING, length 34
7773
+ BufferCompat.from([
7774
+ CRYPTO_CONSTANTS.ASN1_OCTET_STRING_TAG,
7775
+ CRYPTO_CONSTANTS.PKCS8_PRIVATE_KEY_OCTET_INNER_LENGTH,
7776
+ ]), // inner OCTET STRING, length 32
7700
7777
  raw,
7701
7778
  ]);
7702
7779
  // Create PKCS#8 body
@@ -7725,17 +7802,28 @@ class MmnClient {
7725
7802
  throw new Error(`Failed to convert private key to PKCS#8: ${error instanceof Error ? error.message : 'Unknown error'}`);
7726
7803
  }
7727
7804
  }
7805
+ /**
7806
+ * Encode length in ASN.1 DER format
7807
+ * ASN.1 length encoding rules:
7808
+ * - Short form (0-127): single byte with the length value
7809
+ * - Long form (128+): first byte is 0x80 + number of length bytes, followed by length bytes
7810
+ * @param length - The length value to encode
7811
+ * @returns ASN.1 DER encoded length bytes
7812
+ */
7728
7813
  encodeLength(length) {
7729
- if (length < 0x80) {
7814
+ if (length < CRYPTO_CONSTANTS.ASN1_LENGTH) {
7730
7815
  return BufferCompat.from([length]);
7731
7816
  }
7732
7817
  const bytes = [];
7733
7818
  let len = length;
7734
7819
  while (len > 0) {
7735
- bytes.unshift(len & 0xff);
7820
+ bytes.unshift(len & PRNG_CONSTANTS.BYTE_MASK);
7736
7821
  len >>= 8;
7737
7822
  }
7738
- return BufferCompat.from([0x80 | bytes.length, ...bytes]);
7823
+ return BufferCompat.from([
7824
+ CRYPTO_CONSTANTS.ASN1_LENGTH | bytes.length,
7825
+ ...bytes,
7826
+ ]);
7739
7827
  }
7740
7828
  /**
7741
7829
  * Generate secure entropy using multiple sources for maximum compatibility
@@ -7752,19 +7840,25 @@ class MmnClient {
7752
7840
  const random = Math.random();
7753
7841
  // Create initial seed from timestamp and random
7754
7842
  let seed = now + performance + random;
7755
- // Generate bytes using linear congruential generator with multiple mixing
7843
+ // Generate bytes using Linear Congruential Generator (LCG) with multiple entropy sources
7844
+ // This provides a fallback Pseudorandom Number Generator (PRNG) when crypto.getRandomValues is not available
7756
7845
  for (let i = 0; i < targetLength; i++) {
7757
- // Mix multiple sources
7758
- seed = (seed * 1664525 + 1013904223) % 4294967296; // LCG
7759
- seed ^= (now + i) * 2654435761; // Mix with timestamp
7760
- seed ^= Math.floor(Math.random() * 4294967296); // Mix with Math.random
7761
- // Additional mixing with crypto-js if available
7846
+ // Xₙ₊₁ = (a * Xₙ + c) mod m
7847
+ seed =
7848
+ (seed * PRNG_CONSTANTS.LCG_MULTIPLIER + PRNG_CONSTANTS.LCG_INCREMENT) %
7849
+ PRNG_CONSTANTS.LCG_MODULUS;
7850
+ // Mix with timestamp to add time-based entropy
7851
+ seed ^= (now + i) * PRNG_CONSTANTS.TIMESTAMP_MULTIPLIER;
7852
+ // Mix with Math.random() for additional browser-provided randomness
7853
+ seed ^= Math.floor(Math.random() * PRNG_CONSTANTS.LCG_MODULUS);
7854
+ // Additional cryptographic mixing using SHA256 if CryptoJS is available
7762
7855
  if (typeof CryptoJS !== 'undefined') {
7763
7856
  const hashInput = seed.toString() + i.toString() + now.toString();
7764
7857
  const hash = CryptoJS.SHA256(hashInput).toString();
7765
- seed ^= parseInt(hash.substring(0, 8), 16);
7858
+ // Extract first 8 hex characters (32 bits) from hash for mixing
7859
+ seed ^= parseInt(hash.substring(0, PRNG_CONSTANTS.HASH_SUBSTRING_LENGTH), 16);
7766
7860
  }
7767
- entropy.push((seed >>> 24) & 0xff);
7861
+ entropy.push((seed >>> PRNG_CONSTANTS.BYTE_SHIFT) & PRNG_CONSTANTS.BYTE_MASK);
7768
7862
  }
7769
7863
  return entropy;
7770
7864
  }
@@ -7969,24 +8063,56 @@ var ETransferType;
7969
8063
  class ZkClient {
7970
8064
  constructor(config) {
7971
8065
  this.endpoint = config.endpoint;
7972
- this.axiosInstance = axios.create({
7973
- baseURL: this.endpoint,
7974
- timeout: config.timeout || 30000,
7975
- headers: {
7976
- 'Content-Type': 'application/json',
7977
- Accept: 'application/json',
7978
- ...(config.headers || {}),
7979
- },
7980
- });
8066
+ this.timeout = config.timeout || 30000;
8067
+ this.headers = {
8068
+ Accept: 'application/json',
8069
+ 'Content-Type': 'application/json',
8070
+ ...(config.headers || {}),
8071
+ };
7981
8072
  }
7982
8073
  async makeRequest(method, path, params, body) {
7983
- const resp = await this.axiosInstance.request({
7984
- method,
7985
- url: path,
7986
- params,
7987
- data: body,
7988
- });
7989
- return resp.data;
8074
+ let url = `${this.endpoint}/${path}`;
8075
+ // Add query parameters for GET requests
8076
+ if (params && Object.keys(params).length > 0) {
8077
+ const searchParams = new URLSearchParams();
8078
+ Object.entries(params).forEach(([key, value]) => {
8079
+ searchParams.append(key, String(value));
8080
+ });
8081
+ url += `?${searchParams.toString()}`;
8082
+ }
8083
+ // Create AbortController for timeout
8084
+ const controller = new AbortController();
8085
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
8086
+ try {
8087
+ const requestOptions = {
8088
+ method,
8089
+ mode: 'cors',
8090
+ credentials: 'omit',
8091
+ signal: controller.signal,
8092
+ headers: this.headers,
8093
+ };
8094
+ // Add body for POST requests
8095
+ if (method === 'POST' && body) {
8096
+ requestOptions.body = JSON.stringify(body);
8097
+ }
8098
+ const response = await fetch(url, requestOptions);
8099
+ clearTimeout(timeoutId);
8100
+ if (!response.ok) {
8101
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
8102
+ }
8103
+ const data = await response.json();
8104
+ return data;
8105
+ }
8106
+ catch (error) {
8107
+ clearTimeout(timeoutId);
8108
+ if (error instanceof Error) {
8109
+ if (error.name === 'AbortError') {
8110
+ throw new Error(`Request timeout after ${this.timeout}ms`);
8111
+ }
8112
+ throw error;
8113
+ }
8114
+ throw new Error('Request failed');
8115
+ }
7990
8116
  }
7991
8117
  async getZkProofs({ userId, ephemeralPublicKey, jwt, address, }) {
7992
8118
  const path = `prove`;