mmn-client-js 1.0.5 → 1.0.7

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,19 +167,28 @@ 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
- getTransactionByWallet(wallet: string, page: number | undefined, limit: number | undefined, filter: number): Promise<ListTransactionResponse>;
186
+ getTransactionByWallet(wallet: string, page: number | undefined, limit: number | undefined, filter: number, sortBy?: string, sortOrder?: 'asc' | 'desc'): Promise<ListTransactionResponse>;
180
187
  getWalletDetail(wallet: string): Promise<WalletDetail>;
181
188
  }
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,31 +10,81 @@ 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`;
35
84
  const res = await this.makeRequest('GET', path);
36
85
  return res.data.transaction;
37
86
  }
38
- async getTransactionByWallet(wallet, page = 1, limit = 50, filter) {
87
+ async getTransactionByWallet(wallet, page = 1, limit = 50, filter, sortBy = 'transaction_timestamp', sortOrder = 'desc') {
39
88
  if (!wallet) {
40
89
  throw new Error('wallet address cannot be empty');
41
90
  }
@@ -48,8 +97,8 @@ class IndexerClient {
48
97
  const params = {
49
98
  page: page - 1,
50
99
  limit,
51
- sort_by: 'block_timestamp',
52
- sort_order: 'desc',
100
+ sort_by: sortBy,
101
+ sort_order: sortOrder,
53
102
  };
54
103
  switch (filter) {
55
104
  case API_FILTER_PARAMS.ALL:
@@ -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,36 @@ 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
+ console.log('🚀 ~ MmnClient ~ makeRequest ~ response:', response);
7717
+ clearTimeout(timeoutId);
7718
+ if (!response.ok) {
7719
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
7720
+ }
7721
+ const result = await response.json();
7643
7722
  if (result.error) {
7644
7723
  throw new Error(`JSON-RPC Error ${result.error.code}: ${result.error.message}`);
7645
7724
  }
7646
7725
  return result.result;
7647
7726
  }
7648
7727
  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
- }
7728
+ clearTimeout(timeoutId);
7663
7729
  if (error instanceof Error) {
7730
+ if (error.name === 'AbortError') {
7731
+ throw new Error(`Request timeout after ${this.config.timeout || 30000}ms`);
7732
+ }
7664
7733
  throw error;
7665
7734
  }
7666
7735
  throw new Error('Unknown error occurred');
@@ -7681,22 +7750,31 @@ class MmnClient {
7681
7750
  throw new Error(`Ed25519 private key must be exactly ${CRYPTO_CONSTANTS.ED25519_PRIVATE_KEY_LENGTH} bytes`);
7682
7751
  }
7683
7752
  try {
7684
- // Ed25519 OID: 1.3.101.112 (constant-time)
7685
- const ED25519_OID = BufferCompat.from([0x06, 0x03, 0x2b, 0x65, 0x70]);
7753
+ // Ed25519 OID: 1.3.101.112 (RFC 8410 - Algorithm Identifiers for Ed25519)
7754
+ const ED25519_OID = BufferCompat.from(CRYPTO_CONSTANTS.ED25519_OID_BYTES);
7686
7755
  const VERSION_BYTES = BufferCompat.from([
7687
7756
  CRYPTO_CONSTANTS.ASN1_INTEGER_TAG,
7688
- 0x01,
7757
+ 0x01, // Length of integer (1 byte)
7689
7758
  CRYPTO_CONSTANTS.PKCS8_VERSION,
7690
- ]); // INTEGER 0
7691
- // Create algorithm identifier sequence
7759
+ ]);
7760
+ // Create algorithm identifier sequence (AlgorithmIdentifier)
7692
7761
  const algorithmId = BufferCompat.concat([
7693
- BufferCompat.from([0x30, 0x0b]), // SEQUENCE, length 11
7762
+ BufferCompat.from([
7763
+ CRYPTO_CONSTANTS.ASN1_SEQUENCE_TAG,
7764
+ CRYPTO_CONSTANTS.PKCS8_ALGORITHM_ID_LENGTH,
7765
+ ]),
7694
7766
  ED25519_OID,
7695
7767
  ]);
7696
- // Create private key octet string
7768
+ // Create private key octet string (wrapped Ed25519 private key)
7697
7769
  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
7770
+ BufferCompat.from([
7771
+ CRYPTO_CONSTANTS.ASN1_OCTET_STRING_TAG,
7772
+ CRYPTO_CONSTANTS.PKCS8_PRIVATE_KEY_OCTET_OUTER_LENGTH,
7773
+ ]), // OCTET STRING, length 34
7774
+ BufferCompat.from([
7775
+ CRYPTO_CONSTANTS.ASN1_OCTET_STRING_TAG,
7776
+ CRYPTO_CONSTANTS.PKCS8_PRIVATE_KEY_OCTET_INNER_LENGTH,
7777
+ ]), // inner OCTET STRING, length 32
7700
7778
  raw,
7701
7779
  ]);
7702
7780
  // Create PKCS#8 body
@@ -7725,17 +7803,28 @@ class MmnClient {
7725
7803
  throw new Error(`Failed to convert private key to PKCS#8: ${error instanceof Error ? error.message : 'Unknown error'}`);
7726
7804
  }
7727
7805
  }
7806
+ /**
7807
+ * Encode length in ASN.1 DER format
7808
+ * ASN.1 length encoding rules:
7809
+ * - Short form (0-127): single byte with the length value
7810
+ * - Long form (128+): first byte is 0x80 + number of length bytes, followed by length bytes
7811
+ * @param length - The length value to encode
7812
+ * @returns ASN.1 DER encoded length bytes
7813
+ */
7728
7814
  encodeLength(length) {
7729
- if (length < 0x80) {
7815
+ if (length < CRYPTO_CONSTANTS.ASN1_LENGTH) {
7730
7816
  return BufferCompat.from([length]);
7731
7817
  }
7732
7818
  const bytes = [];
7733
7819
  let len = length;
7734
7820
  while (len > 0) {
7735
- bytes.unshift(len & 0xff);
7821
+ bytes.unshift(len & PRNG_CONSTANTS.BYTE_MASK);
7736
7822
  len >>= 8;
7737
7823
  }
7738
- return BufferCompat.from([0x80 | bytes.length, ...bytes]);
7824
+ return BufferCompat.from([
7825
+ CRYPTO_CONSTANTS.ASN1_LENGTH | bytes.length,
7826
+ ...bytes,
7827
+ ]);
7739
7828
  }
7740
7829
  /**
7741
7830
  * Generate secure entropy using multiple sources for maximum compatibility
@@ -7752,19 +7841,25 @@ class MmnClient {
7752
7841
  const random = Math.random();
7753
7842
  // Create initial seed from timestamp and random
7754
7843
  let seed = now + performance + random;
7755
- // Generate bytes using linear congruential generator with multiple mixing
7844
+ // Generate bytes using Linear Congruential Generator (LCG) with multiple entropy sources
7845
+ // This provides a fallback Pseudorandom Number Generator (PRNG) when crypto.getRandomValues is not available
7756
7846
  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
7847
+ // Xₙ₊₁ = (a * Xₙ + c) mod m
7848
+ seed =
7849
+ (seed * PRNG_CONSTANTS.LCG_MULTIPLIER + PRNG_CONSTANTS.LCG_INCREMENT) %
7850
+ PRNG_CONSTANTS.LCG_MODULUS;
7851
+ // Mix with timestamp to add time-based entropy
7852
+ seed ^= (now + i) * PRNG_CONSTANTS.TIMESTAMP_MULTIPLIER;
7853
+ // Mix with Math.random() for additional browser-provided randomness
7854
+ seed ^= Math.floor(Math.random() * PRNG_CONSTANTS.LCG_MODULUS);
7855
+ // Additional cryptographic mixing using SHA256 if CryptoJS is available
7762
7856
  if (typeof CryptoJS !== 'undefined') {
7763
7857
  const hashInput = seed.toString() + i.toString() + now.toString();
7764
7858
  const hash = CryptoJS.SHA256(hashInput).toString();
7765
- seed ^= parseInt(hash.substring(0, 8), 16);
7859
+ // Extract first 8 hex characters (32 bits) from hash for mixing
7860
+ seed ^= parseInt(hash.substring(0, PRNG_CONSTANTS.HASH_SUBSTRING_LENGTH), 16);
7766
7861
  }
7767
- entropy.push((seed >>> 24) & 0xff);
7862
+ entropy.push((seed >>> PRNG_CONSTANTS.BYTE_SHIFT) & PRNG_CONSTANTS.BYTE_MASK);
7768
7863
  }
7769
7864
  return entropy;
7770
7865
  }
@@ -7969,24 +8064,56 @@ var ETransferType;
7969
8064
  class ZkClient {
7970
8065
  constructor(config) {
7971
8066
  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
- });
8067
+ this.timeout = config.timeout || 30000;
8068
+ this.headers = {
8069
+ Accept: 'application/json',
8070
+ 'Content-Type': 'application/json',
8071
+ ...(config.headers || {}),
8072
+ };
7981
8073
  }
7982
8074
  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;
8075
+ let url = `${this.endpoint}/${path}`;
8076
+ // Add query parameters for GET requests
8077
+ if (params && Object.keys(params).length > 0) {
8078
+ const searchParams = new URLSearchParams();
8079
+ Object.entries(params).forEach(([key, value]) => {
8080
+ searchParams.append(key, String(value));
8081
+ });
8082
+ url += `?${searchParams.toString()}`;
8083
+ }
8084
+ // Create AbortController for timeout
8085
+ const controller = new AbortController();
8086
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
8087
+ try {
8088
+ const requestOptions = {
8089
+ method,
8090
+ mode: 'cors',
8091
+ credentials: 'omit',
8092
+ signal: controller.signal,
8093
+ headers: this.headers,
8094
+ };
8095
+ // Add body for POST requests
8096
+ if (method === 'POST' && body) {
8097
+ requestOptions.body = JSON.stringify(body);
8098
+ }
8099
+ const response = await fetch(url, requestOptions);
8100
+ clearTimeout(timeoutId);
8101
+ if (!response.ok) {
8102
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
8103
+ }
8104
+ const data = await response.json();
8105
+ return data;
8106
+ }
8107
+ catch (error) {
8108
+ clearTimeout(timeoutId);
8109
+ if (error instanceof Error) {
8110
+ if (error.name === 'AbortError') {
8111
+ throw new Error(`Request timeout after ${this.timeout}ms`);
8112
+ }
8113
+ throw error;
8114
+ }
8115
+ throw new Error('Request failed');
8116
+ }
7990
8117
  }
7991
8118
  async getZkProofs({ userId, ephemeralPublicKey, jwt, address, }) {
7992
8119
  const path = `prove`;