@diviswap/sdk 1.9.1 → 2.0.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/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { keccak_256 } from '@noble/hashes/sha3';
1
2
  import crypto from 'crypto';
2
3
 
3
4
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -64,31 +65,43 @@ var AuthModule = class {
64
65
  * ```
65
66
  */
66
67
  async register(data) {
68
+ if (!data.email || !data.password) {
69
+ throw new ValidationError("Email and password are required");
70
+ }
71
+ if (!data.firstName || !data.lastName) {
72
+ throw new ValidationError("First name and last name are required for KYC compliance");
73
+ }
74
+ if (!data.individual) {
75
+ throw new ValidationError(
76
+ "Complete individual KYC information is required for registration. Please provide residential address, date of birth, and government ID information."
77
+ );
78
+ }
79
+ const individual = data.individual;
80
+ const requiredIndividualFields = [
81
+ "residential_country_code",
82
+ "residential_address_line_one",
83
+ "residential_city",
84
+ "residential_state",
85
+ "residential_postal_code",
86
+ "id_country_code",
87
+ "dob",
88
+ "id_number"
89
+ ];
90
+ for (const field of requiredIndividualFields) {
91
+ const value = individual[field];
92
+ if (value === void 0 || value === null || value === "") {
93
+ throw new ValidationError(
94
+ `Missing required KYC field: ${field}. All identity information must be provided for compliance.`
95
+ );
96
+ }
97
+ }
67
98
  const requestData = {
68
99
  email: data.email,
69
100
  password: data.password,
70
- first_name: data.firstName || "Test",
71
- last_name: data.lastName || "User",
72
- // Individual object is REQUIRED by the backend
73
- individual: {
74
- // Use 'USA' (3-letter code) as seen in working lbx-landing implementation
75
- residential_country_code: "USA",
76
- residential_address_line_one: "123 Main Street",
77
- residential_address_line_two: "",
78
- residential_city: "Anytown",
79
- residential_state: "CA",
80
- residential_postal_code: "12345",
81
- id_type: "ssn",
82
- // Add id_type field as seen in lbx-landing
83
- id_country_code: "USA",
84
- dob: "1990-01-01",
85
- id_number: "123456789"
86
- // Remove dashes from SSN like lbx-landing does
87
- }
101
+ first_name: data.firstName,
102
+ last_name: data.lastName,
103
+ individual: data.individual
88
104
  };
89
- if (data.individual) {
90
- requestData.individual = data.individual;
91
- }
92
105
  if (data.phone) requestData.phone = data.phone;
93
106
  if (data.referralCode) requestData.referral_code = data.referralCode;
94
107
  const response = await this.client.post(
@@ -519,19 +532,116 @@ var STABLECOIN_ADDRESSES = {
519
532
  // Wrapped SOL
520
533
  }
521
534
  };
535
+ function isValidEthereumAddress(address) {
536
+ if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
537
+ return false;
538
+ }
539
+ if (address === address.toLowerCase() || address === address.toUpperCase()) {
540
+ return true;
541
+ }
542
+ return address === toChecksumAddress(address);
543
+ }
544
+ function toChecksumAddress(address) {
545
+ if (!address.startsWith("0x")) {
546
+ throw new Error("Invalid Ethereum address: must start with 0x");
547
+ }
548
+ const lowerAddress = address.toLowerCase().replace("0x", "");
549
+ const hash = keccak256(lowerAddress);
550
+ let checksumAddress = "0x";
551
+ for (let i = 0; i < lowerAddress.length; i++) {
552
+ const hashByte = parseInt(hash[i], 16);
553
+ checksumAddress += hashByte >= 8 ? lowerAddress[i].toUpperCase() : lowerAddress[i];
554
+ }
555
+ return checksumAddress;
556
+ }
557
+ function keccak256(input) {
558
+ const bytes = new TextEncoder().encode(input);
559
+ const hash = keccak_256(bytes);
560
+ return Array.from(hash).map((b) => b.toString(16).padStart(2, "0")).join("");
561
+ }
562
+ function isValidSolanaAddress(address) {
563
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
564
+ if (!base58Regex.test(address)) {
565
+ return false;
566
+ }
567
+ if (/[0OIl]/.test(address)) {
568
+ return false;
569
+ }
570
+ return true;
571
+ }
572
+ function isValidBitcoinAddress(address) {
573
+ const legacyRegex = /^1[1-9A-HJ-NP-Za-km-z]{25,34}$/;
574
+ const segwitRegex = /^3[1-9A-HJ-NP-Za-km-z]{25,34}$/;
575
+ const bech32Regex = /^bc1[a-z0-9]{39,87}$/i;
576
+ return legacyRegex.test(address) || segwitRegex.test(address) || bech32Regex.test(address);
577
+ }
578
+ function isValidTronAddress(address) {
579
+ if (!address.startsWith("T") || address.length !== 34) {
580
+ return false;
581
+ }
582
+ const base58Regex = /^T[1-9A-HJ-NP-Za-km-z]{33}$/;
583
+ return base58Regex.test(address);
584
+ }
585
+ function isValidCryptoAddress(address, chain) {
586
+ const chainLower = chain.toLowerCase();
587
+ switch (chainLower) {
588
+ case "ethereum":
589
+ case "eth":
590
+ case "polygon":
591
+ case "matic":
592
+ case "arbitrum":
593
+ case "optimism":
594
+ case "base":
595
+ case "avalanche":
596
+ case "bsc":
597
+ case "binance":
598
+ return isValidEthereumAddress(address);
599
+ case "solana":
600
+ case "sol":
601
+ return isValidSolanaAddress(address);
602
+ case "bitcoin":
603
+ case "btc":
604
+ return isValidBitcoinAddress(address);
605
+ case "tron":
606
+ case "trx":
607
+ return isValidTronAddress(address);
608
+ default:
609
+ throw new Error(`Unsupported chain: ${chain}`);
610
+ }
611
+ }
612
+ function extractTransactionHash(result) {
613
+ if (typeof result === "string") {
614
+ return result;
615
+ }
616
+ if ("hash" in result && result.hash) {
617
+ return result.hash;
618
+ }
619
+ if ("transactionHash" in result && result.transactionHash) {
620
+ return result.transactionHash;
621
+ }
622
+ throw new Error(
623
+ 'Unable to extract transaction hash from result. Expected string or object with "hash" or "transactionHash" property.'
624
+ );
625
+ }
522
626
 
523
627
  // src/modules/transactions.ts
524
- var TransactionsModule = class {
628
+ var _TransactionsModule = class _TransactionsModule {
525
629
  constructor(client, environment = "sandbox") {
526
630
  this.client = client;
527
631
  this.depositAddresses = getDepositAddresses(environment);
528
632
  }
529
633
  /**
530
- * Map chain name to chain ID
634
+ * Map chain name to chain ID with validation
531
635
  * @private
532
636
  */
533
637
  getChainId(chain) {
534
- const chainId = CHAIN_NAME_TO_ID[chain.toLowerCase()];
638
+ const chainLower = chain.toLowerCase();
639
+ if (!_TransactionsModule.SUPPORTED_CHAINS.includes(chainLower)) {
640
+ throw new ValidationError(
641
+ `Unsupported chain: ${chain}. Supported chains: ${_TransactionsModule.SUPPORTED_CHAINS.filter((c, i, arr) => arr.indexOf(c) === i).join(", ")}`
642
+ );
643
+ }
644
+ const chainId = CHAIN_NAME_TO_ID[chainLower];
535
645
  return chainId ? chainId.toString() : chain;
536
646
  }
537
647
  /**
@@ -620,17 +730,30 @@ var TransactionsModule = class {
620
730
  */
621
731
  async offramp(data) {
622
732
  if (!data.txHash || data.txHash.trim() === "") {
623
- throw new Error("txHash is required for offramp transactions.");
733
+ throw new ValidationError("txHash is required for offramp transactions.");
734
+ }
735
+ const chain = data.chain || "ethereum";
736
+ const chainId = this.getChainId(chain);
737
+ if (!isValidCryptoAddress(data.fromAddress, chain)) {
738
+ throw new ValidationError(
739
+ `Invalid ${chain} address: ${data.fromAddress}. Please check the address format.`
740
+ );
741
+ }
742
+ const toAddress = data.toAddress || this.getDepositAddress(chain);
743
+ if (!isValidCryptoAddress(toAddress, chain)) {
744
+ throw new ValidationError(
745
+ `Invalid ${chain} deposit address: ${toAddress}. Please contact support.`
746
+ );
624
747
  }
625
748
  const payload = {
626
749
  payee_id: data.payeeId,
627
750
  tx_hash: data.txHash,
628
751
  // Required: must send crypto first
629
752
  from_address: data.fromAddress,
630
- to_address: data.toAddress || this.getDepositAddress(data.chain || "ethereum"),
753
+ to_address: toAddress,
631
754
  amount: data.amount,
632
755
  currency: data.currency,
633
- chain_id: this.getChainId(data.chain || "ethereum")
756
+ chain_id: chainId
634
757
  };
635
758
  if (data.memo) {
636
759
  payload.memo = data.memo;
@@ -818,6 +941,26 @@ var TransactionsModule = class {
818
941
  return STABLECOIN_ADDRESSES[chainName] || {};
819
942
  }
820
943
  };
944
+ // Supported chains for validation
945
+ _TransactionsModule.SUPPORTED_CHAINS = [
946
+ "ethereum",
947
+ "eth",
948
+ "polygon",
949
+ "matic",
950
+ "arbitrum",
951
+ "optimism",
952
+ "base",
953
+ "avalanche",
954
+ "bsc",
955
+ "binance",
956
+ "solana",
957
+ "sol",
958
+ "bitcoin",
959
+ "btc",
960
+ "tron",
961
+ "trx"
962
+ ];
963
+ var TransactionsModule = _TransactionsModule;
821
964
 
822
965
  // src/modules/kyc.ts
823
966
  var KycModule = class {
@@ -1533,8 +1676,8 @@ var MemoryTokenStorage = class {
1533
1676
  }
1534
1677
  };
1535
1678
  var LocalStorageTokenStorage = class {
1536
- constructor() {
1537
- this.key = "liberex_tokens";
1679
+ constructor(userId) {
1680
+ this.key = userId ? `liberex_tokens_${userId}` : "liberex_tokens";
1538
1681
  }
1539
1682
  get() {
1540
1683
  if (typeof globalThis.window === "undefined") return null;
@@ -1556,9 +1699,14 @@ var LocalStorageTokenStorage = class {
1556
1699
  }
1557
1700
  };
1558
1701
  var TokenManager = class {
1559
- constructor(useLocalStorage = false) {
1702
+ /**
1703
+ * Create a new TokenManager
1704
+ * @param useLocalStorage Whether to use localStorage (default: memory storage for security)
1705
+ * @param userId Optional user ID for multi-user session isolation
1706
+ */
1707
+ constructor(useLocalStorage = false, userId) {
1560
1708
  this.refreshPromise = null;
1561
- this.storage = useLocalStorage && typeof globalThis.window !== "undefined" ? new LocalStorageTokenStorage() : new MemoryTokenStorage();
1709
+ this.storage = useLocalStorage && typeof globalThis.window !== "undefined" ? new LocalStorageTokenStorage(userId) : new MemoryTokenStorage();
1562
1710
  }
1563
1711
  /**
1564
1712
  * Parse JWT token to extract expiration
@@ -1659,6 +1807,18 @@ var PartnerAuth = class {
1659
1807
  constructor(credentials) {
1660
1808
  this.credentials = credentials;
1661
1809
  }
1810
+ /**
1811
+ * Override toJSON to prevent accidental serialization of secret key
1812
+ */
1813
+ toJSON() {
1814
+ return {
1815
+ keyId: this.credentials.keyId,
1816
+ customerId: this.credentials.customerId,
1817
+ customerEmail: this.credentials.customerEmail,
1818
+ secretKey: "***REDACTED***"
1819
+ // Never expose secret key
1820
+ };
1821
+ }
1662
1822
  /**
1663
1823
  * Generate HMAC authentication headers for a request
1664
1824
  */
@@ -1750,17 +1910,54 @@ var PartnerAuth = class {
1750
1910
  // src/api/unified-client.ts
1751
1911
  var UnifiedApiClient = class _UnifiedApiClient {
1752
1912
  constructor(config, useLocalStorage = true) {
1753
- this.config = config;
1913
+ Object.defineProperty(this, "_secureApiKey", {
1914
+ value: config.apiKey,
1915
+ writable: false,
1916
+ enumerable: false,
1917
+ configurable: false
1918
+ });
1919
+ Object.defineProperty(this, "_secureSecretKey", {
1920
+ value: config.secretKey,
1921
+ writable: false,
1922
+ enumerable: false,
1923
+ configurable: false
1924
+ });
1925
+ const { apiKey, secretKey, ...safeConfig } = config;
1926
+ this.config = safeConfig;
1754
1927
  this.tokenManager = new TokenManager(useLocalStorage);
1755
- if (config.mode === "partner" && config.keyId && config.secretKey) {
1928
+ const secureSecretKey = this._secureSecretKey;
1929
+ if (config.mode === "partner" && config.keyId && secureSecretKey) {
1756
1930
  this.partnerAuth = new PartnerAuth({
1757
1931
  keyId: config.keyId,
1758
- secretKey: config.secretKey,
1932
+ secretKey: secureSecretKey,
1759
1933
  customerId: config.customerId,
1760
1934
  customerEmail: config.customerEmail
1761
1935
  });
1762
1936
  }
1763
1937
  }
1938
+ /**
1939
+ * Override toJSON to prevent accidental serialization of sensitive data
1940
+ */
1941
+ toJSON() {
1942
+ return {
1943
+ mode: this.config.mode,
1944
+ baseUrl: this.config.baseUrl,
1945
+ timeout: this.config.timeout,
1946
+ debug: this.config.debug,
1947
+ // Never expose credentials
1948
+ apiKey: this.config.apiKey ? "***REDACTED***" : void 0,
1949
+ secretKey: this.config.secretKey ? "***REDACTED***" : void 0,
1950
+ keyId: this.config.keyId,
1951
+ clientId: this.config.clientId
1952
+ };
1953
+ }
1954
+ /**
1955
+ * Get the actual API key (for internal use only)
1956
+ * @internal
1957
+ */
1958
+ getApiKey() {
1959
+ return this._secureApiKey;
1960
+ }
1764
1961
  /**
1765
1962
  * Create client from legacy user config (backward compatibility)
1766
1963
  */
@@ -1908,7 +2105,15 @@ var UnifiedApiClient = class _UnifiedApiClient {
1908
2105
  this.config.timeout
1909
2106
  );
1910
2107
  if (this.config.debug) {
1911
- console.log(`[Diviswap SDK] ${method} ${url}`);
2108
+ const safeHeaders = { ...requestHeaders };
2109
+ if (safeHeaders["X-API-Key"]) safeHeaders["X-API-Key"] = "***REDACTED***";
2110
+ if (safeHeaders["Authorization"]?.startsWith("Bearer ")) {
2111
+ safeHeaders["Authorization"] = "Bearer ***REDACTED***";
2112
+ }
2113
+ if (safeHeaders["Authorization"]?.startsWith("HMAC ")) {
2114
+ safeHeaders["Authorization"] = "HMAC ***REDACTED***";
2115
+ }
2116
+ console.log(`[Diviswap SDK] ${method} ${url}`, { headers: safeHeaders });
1912
2117
  }
1913
2118
  const response = await fetch(url, {
1914
2119
  method,
@@ -1976,12 +2181,13 @@ var UnifiedApiClient = class _UnifiedApiClient {
1976
2181
  * Add user authentication headers (legacy)
1977
2182
  */
1978
2183
  async addUserAuth(useApiKey, headers) {
1979
- if (!this.config.apiKey || !this.config.clientId) {
2184
+ const apiKey = this.getApiKey();
2185
+ if (!apiKey || !this.config.clientId) {
1980
2186
  throw new AuthenticationError("User authentication not configured");
1981
2187
  }
1982
2188
  headers["X-CLIENT-ID"] = this.config.clientId;
1983
2189
  headers["X-TIMESTAMP"] = Math.floor(Date.now() / 1e3).toString();
1984
- headers["X-API-Key"] = this.config.apiKey;
2190
+ headers["X-API-Key"] = apiKey;
1985
2191
  if (!useApiKey) {
1986
2192
  const accessToken = await this.tokenManager.getValidAccessToken(
1987
2193
  this.refreshCallback
@@ -2417,17 +2623,6 @@ function setupWalletTracking(diviswap, wallet, config) {
2417
2623
  return tracker;
2418
2624
  }
2419
2625
 
2420
- // src/utils/web3.ts
2421
- function extractTransactionHash(result) {
2422
- if (typeof result === "string") {
2423
- return result;
2424
- }
2425
- if (typeof result === "object" && result !== null && "hash" in result) {
2426
- return result.hash;
2427
- }
2428
- return result;
2429
- }
2430
-
2431
2626
  export { AuthenticationError, CHAIN_IDS, CHAIN_ID_TO_NAME, CHAIN_NAME_TO_ID, ConfigurationError, Diviswap, DiviswapError, Diviswap as LiberEx, LiberExError, NetworkError, PRODUCTION_DEPOSIT_ADDRESSES, SANDBOX_DEPOSIT_ADDRESSES, STABLECOIN_ADDRESSES, ValidationError, WalletTracker, connectWallet, extractTransactionHash, getDepositAddresses, setupWalletTracking, trackCurrentWallet };
2432
2627
  //# sourceMappingURL=index.mjs.map
2433
2628
  //# sourceMappingURL=index.mjs.map