tiwiflix-wallet-connector 1.6.7 → 1.6.9

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
@@ -418,8 +418,6 @@ declare class WalletConnector {
418
418
  * Get TWC token balance for EVM wallets
419
419
  * TWC is ONLY available on BSC (chainId 56)
420
420
  */
421
- private twcBalanceCache;
422
- private readonly TWC_CACHE_TTL;
423
421
  private balanceFetchErrors;
424
422
  getTWCBalance(): Promise<{
425
423
  amount: string;
@@ -492,10 +490,6 @@ declare class WalletConnector {
492
490
  * Can optionally filter by collection addresses
493
491
  */
494
492
  private queryAllTONNFTs;
495
- /**
496
- * Query TON API for NFTs owned by an address in a specific collection
497
- */
498
- private queryTONNFTs;
499
493
  }
500
494
 
501
495
  /**
package/dist/index.esm.js CHANGED
@@ -1907,7 +1907,7 @@ const defaultMultiChainConfig = {
1907
1907
  {
1908
1908
  chainId: 56,
1909
1909
  name: 'BNB Smart Chain',
1910
- rpcUrl: 'https://bsc-dataseed.binance.org',
1910
+ rpcUrl: 'https://bsc.publicnode.com',
1911
1911
  blockExplorerUrl: 'https://bscscan.com',
1912
1912
  nativeCurrency: {
1913
1913
  name: 'BNB',
@@ -2312,6 +2312,7 @@ class WalletConnector {
2312
2312
  error: null,
2313
2313
  twcBalance: null,
2314
2314
  usdValue: null,
2315
+ chainId: null,
2315
2316
  };
2316
2317
  // Performance monitoring
2317
2318
  this.performanceMetrics = {
@@ -2325,8 +2326,6 @@ class WalletConnector {
2325
2326
  * TWC is ONLY available on BSC (chainId 56)
2326
2327
  */
2327
2328
  // Cache for TWC balance to avoid redundant fetches
2328
- this.twcBalanceCache = null;
2329
- this.TWC_CACHE_TTL = 30000; // 30 seconds cache
2330
2329
  // Error tracking for balance fetching
2331
2330
  this.balanceFetchErrors = [];
2332
2331
  this.config = config;
@@ -2425,13 +2424,38 @@ class WalletConnector {
2425
2424
  * Connect to a wallet
2426
2425
  */
2427
2426
  async connect(walletType) {
2427
+ // Only allow EVM wallet connections on BSC (chainId 56)
2428
+ let adapter = this.adapters.get(walletType);
2429
+ if (adapter && adapter.chainType === ChainType.EVM && typeof adapter.getChainId === 'function') {
2430
+ try {
2431
+ const chainId = await adapter.getChainId();
2432
+ if (chainId !== 56) {
2433
+ const error = new Error('Only Binance Smart Chain (BSC) is supported for EVM wallet connections. Please switch your wallet to BSC and try again.');
2434
+ this.updateState({
2435
+ status: ConnectionStatus.ERROR,
2436
+ error,
2437
+ });
2438
+ this.eventEmitter.emit(WalletEvent.ERROR, error);
2439
+ throw error;
2440
+ }
2441
+ }
2442
+ catch (e) {
2443
+ const error = new Error('Failed to detect EVM chain. Please ensure your wallet is connected to BSC.');
2444
+ this.updateState({
2445
+ status: ConnectionStatus.ERROR,
2446
+ error,
2447
+ });
2448
+ this.eventEmitter.emit(WalletEvent.ERROR, error);
2449
+ throw error;
2450
+ }
2451
+ }
2428
2452
  // Prevent duplicate connection requests
2429
2453
  if (this.state.status === ConnectionStatus.CONNECTING) {
2430
2454
  const error = new Error('Connection already in progress. Please wait...');
2431
2455
  console.warn('Duplicate connection attempt blocked:', walletType);
2432
2456
  throw error;
2433
2457
  }
2434
- const adapter = this.adapters.get(walletType);
2458
+ // adapter already declared above
2435
2459
  if (!adapter) {
2436
2460
  const error = new Error(`Wallet ${walletType} is not registered`);
2437
2461
  this.updateState({
@@ -2668,7 +2692,7 @@ class WalletConnector {
2668
2692
  clearTimeout(safetyTimeout);
2669
2693
  clearTimeout(connectionTimeout);
2670
2694
  document.removeEventListener('visibilitychange', handleVisibilityChange);
2671
- error instanceof Error ? error.message : 'Connection failed';
2695
+ // removed unused errorMsg
2672
2696
  this.debugTools.error('WALLET_CONNECTOR', 'Connection error caught', error);
2673
2697
  // Reset state on connection error
2674
2698
  this.updateState({
@@ -2750,7 +2774,7 @@ class WalletConnector {
2750
2774
  }
2751
2775
  else {
2752
2776
  // Connection failed - reset state properly
2753
- result.error?.message || 'Failed to connect';
2777
+ // removed unused errorMsg
2754
2778
  this.debugTools.error('WALLET_CONNECTOR', 'Connection failed', result.error);
2755
2779
  this.updateState({
2756
2780
  status: ConnectionStatus.DISCONNECTED,
@@ -3126,153 +3150,29 @@ class WalletConnector {
3126
3150
  this.sdkLoader.clearCache();
3127
3151
  }
3128
3152
  async getTWCBalance() {
3129
- if (!this.state.account) {
3130
- if (typeof window !== 'undefined')
3131
- window.showDebugLog?.('[getTWCBalance] No wallet connected');
3132
- throw new Error('No wallet connected');
3153
+ if (!this.state.account || this.state.account.chainType !== 'evm') {
3154
+ throw new Error('No EVM account connected');
3133
3155
  }
3134
3156
  const accountAddress = this.state.account.address;
3135
- const chainId = this.state.chainId;
3136
- performance.now();
3137
- if (typeof window !== 'undefined')
3138
- window.showDebugLog?.(`[getTWCBalance] Called for account=${accountAddress}, chainId=${chainId}`);
3139
- // Check cache first
3140
- if (this.twcBalanceCache &&
3141
- this.twcBalanceCache.address === accountAddress &&
3142
- Date.now() - this.twcBalanceCache.timestamp < this.TWC_CACHE_TTL) {
3143
- if (typeof window !== 'undefined')
3144
- window.showDebugLog?.(`[getTWCBalance] Using cache for ${accountAddress}: ${JSON.stringify(this.twcBalanceCache)}`);
3145
- return {
3146
- amount: this.twcBalanceCache.balance,
3147
- symbol: 'TWC',
3148
- usdValue: this.twcBalanceCache.usdValue,
3149
- usdFormatted: priceService.formatUSDValue(this.twcBalanceCache.usdValue)
3150
- };
3151
- }
3152
- // TWC contract on BSC
3153
3157
  const TWC_CONTRACT = '0xDA1060158F7D593667cCE0a15DB346BB3FfB3596';
3154
3158
  const TWC_DECIMALS = 9;
3155
3159
  const TWC_SYMBOL = 'TWC';
3156
- try {
3157
- // Get balance directly from wallet provider via EVM adapter
3158
- // Use the currently connected wallet's adapter
3159
- const currentWallet = this.state.wallet;
3160
- if (!currentWallet) {
3161
- if (typeof window !== 'undefined')
3162
- window.showDebugLog?.('[getTWCBalance] No wallet connected (no currentWallet)');
3163
- throw new Error('No wallet connected');
3164
- }
3165
- const adapter = this.adapters.get(currentWallet);
3166
- if (!adapter || adapter.chainType !== ChainType.EVM) {
3167
- if (typeof window !== 'undefined')
3168
- window.showDebugLog?.('[getTWCBalance] No EVM adapter available');
3169
- throw new Error('No EVM adapter available');
3170
- }
3171
- // Debug: log adapter type and available methods
3172
- if (typeof window !== 'undefined') {
3173
- window.showDebugLog?.(`[getTWCBalance] Using adapter: ${adapter.constructor?.name || typeof adapter}`);
3174
- window.showDebugLog?.(`[getTWCBalance] Adapter keys: ${Object.keys(adapter).join(', ')}`);
3175
- window.showDebugLog?.(`[getTWCBalance] typeof getTokenBalance: ${typeof adapter.getTokenBalance}`);
3176
- }
3177
- let balance;
3178
- // Always use ethers fallback if EVM is connected, regardless of chainId
3179
- if (this.state.chainId !== 56) {
3180
- if (typeof window !== 'undefined')
3181
- window.showDebugLog?.(`[getTWCBalance] Warning: Not on BSC (chainId=${this.state.chainId}), but fetching TWC balance using ethers fallback.`);
3182
- // Fallback: use ethers.js to fetch balance from chain
3183
- let provider = adapter.provider || this.getProvider();
3184
- if (!provider) {
3185
- if (typeof window !== 'undefined')
3186
- window.showDebugLog?.('[getTWCBalance] No provider available for ethers fallback');
3187
- throw new Error('No provider available for ethers fallback');
3188
- }
3189
- if (!provider._isProvider) {
3190
- provider = new ethers.BrowserProvider(provider);
3191
- }
3192
- const erc20Abi = ["function balanceOf(address) view returns (uint256)"];
3193
- const contract = new ethers.Contract(TWC_CONTRACT, erc20Abi, provider);
3194
- const rawBalance = await contract.balanceOf(accountAddress);
3195
- if (typeof window !== 'undefined')
3196
- window.showDebugLog?.(`[getTWCBalance] [ethers fallback] rawBalance (BigInt) for ${accountAddress}: ${rawBalance?.toString?.()}`);
3197
- balance = ethers.formatUnits(rawBalance, TWC_DECIMALS);
3198
- if (typeof window !== 'undefined')
3199
- window.showDebugLog?.(`[getTWCBalance] Raw balance for ${accountAddress} (ethers): ${balance}`);
3200
- }
3201
- else if (typeof adapter.getTokenBalance === 'function') {
3202
- // Use adapter method if available and on BSC
3203
- const balancePromise = adapter.getTokenBalance(TWC_CONTRACT, TWC_DECIMALS, true);
3204
- const timeoutPromise = new Promise((_, reject) => {
3205
- setTimeout(() => reject(new Error('Balance fetch timeout after 10 seconds')), 10000);
3206
- });
3207
- balance = await Promise.race([balancePromise, timeoutPromise]);
3208
- if (typeof window !== 'undefined')
3209
- window.showDebugLog?.(`[getTWCBalance] Raw balance for ${accountAddress} (adapter): ${balance}`);
3210
- }
3211
- else {
3212
- // Fallback: use ethers.js to fetch balance from chain (should not happen, but for safety)
3213
- if (typeof window !== 'undefined')
3214
- window.showDebugLog?.('[getTWCBalance] Fallback to ethers.js for TWC balance (on BSC)');
3215
- let provider = adapter.provider || this.getProvider();
3216
- if (!provider) {
3217
- if (typeof window !== 'undefined')
3218
- window.showDebugLog?.('[getTWCBalance] No provider available for ethers fallback');
3219
- throw new Error('No provider available for ethers fallback');
3220
- }
3221
- if (!provider._isProvider) {
3222
- provider = new ethers.BrowserProvider(provider);
3223
- }
3224
- const erc20Abi = ["function balanceOf(address) view returns (uint256)"];
3225
- const contract = new ethers.Contract(TWC_CONTRACT, erc20Abi, provider);
3226
- const rawBalance = await contract.balanceOf(accountAddress);
3227
- if (typeof window !== 'undefined')
3228
- window.showDebugLog?.(`[getTWCBalance] [ethers fallback] rawBalance (BigInt) for ${accountAddress}: ${rawBalance?.toString?.()}`);
3229
- balance = ethers.formatUnits(rawBalance, TWC_DECIMALS);
3230
- if (typeof window !== 'undefined')
3231
- window.showDebugLog?.(`[getTWCBalance] Raw balance for ${accountAddress} (ethers): ${balance}`);
3232
- }
3233
- // Calculate USD value with 5 second timeout
3234
- const usdPromise = priceService.calculateTWCValue(balance);
3235
- const usdTimeoutPromise = new Promise((resolve) => {
3236
- setTimeout(() => resolve(0), 5000); // Default to 0 on timeout
3237
- });
3238
- const usdValue = await Promise.race([usdPromise, usdTimeoutPromise]);
3239
- if (typeof window !== 'undefined')
3240
- window.showDebugLog?.(`[getTWCBalance] USD value for ${accountAddress}: ${usdValue}`);
3241
- // Update cache
3242
- this.twcBalanceCache = {
3243
- balance,
3244
- usdValue,
3245
- timestamp: Date.now(),
3246
- address: accountAddress
3247
- };
3248
- const result = {
3249
- amount: balance,
3250
- symbol: TWC_SYMBOL,
3251
- usdValue,
3252
- usdFormatted: priceService.formatUSDValue(usdValue)
3253
- };
3254
- // Keep state in sync
3255
- this.updateState({ twcBalance: result.amount, usdValue: result.usdValue ?? null });
3256
- if (typeof window !== 'undefined')
3257
- window.showDebugLog?.(`[getTWCBalance] Final result for ${accountAddress}: ${JSON.stringify(result)}`);
3258
- return result;
3259
- }
3260
- catch (error) {
3261
- const errorMsg = error instanceof Error ? error.message : String(error);
3262
- if (typeof window !== 'undefined')
3263
- window.showDebugLog?.(`[getTWCBalance] ERROR: ${errorMsg}`);
3264
- // Track error
3265
- this.balanceFetchErrors.push({
3266
- endpoint: 'wallet-provider',
3267
- error: errorMsg,
3268
- timestamp: Date.now()
3269
- });
3270
- // Keep only last 10 errors
3271
- if (this.balanceFetchErrors.length > 10) {
3272
- this.balanceFetchErrors = this.balanceFetchErrors.slice(-10);
3273
- }
3274
- throw error;
3275
- }
3160
+ let provider = this.getProvider();
3161
+ // If provider is missing or not a valid ethers.js provider, use a public BSC provider
3162
+ if (!provider || typeof provider.call !== 'function') {
3163
+ provider = new ethers.JsonRpcProvider('https://bsc.publicnode.com', 56);
3164
+ }
3165
+ const erc20Abi = ["function balanceOf(address) view returns (uint256)"];
3166
+ const contract = new ethers.Contract(TWC_CONTRACT, erc20Abi, provider);
3167
+ const rawBalance = await contract.balanceOf(accountAddress);
3168
+ const balance = ethers.formatUnits(rawBalance, TWC_DECIMALS);
3169
+ const usdValue = await priceService.calculateTWCValue(balance);
3170
+ return {
3171
+ amount: balance,
3172
+ symbol: TWC_SYMBOL,
3173
+ usdValue,
3174
+ usdFormatted: priceService.formatUSDValue(usdValue)
3175
+ };
3276
3176
  }
3277
3177
  /**
3278
3178
  * Get balance fetch error history (for debugging)
@@ -3534,73 +3434,6 @@ class WalletConnector {
3534
3434
  return [];
3535
3435
  }
3536
3436
  }
3537
- /**
3538
- * Query TON API for NFTs owned by an address in a specific collection
3539
- */
3540
- async queryTONNFTs(ownerAddress, collectionAddress) {
3541
- // TON API endpoints to try
3542
- const endpoints = [
3543
- 'https://tonapi.io/v1',
3544
- 'https://testnet.tonapi.io/v1'
3545
- ];
3546
- for (const baseUrl of endpoints) {
3547
- try {
3548
- // First, get NFT items for the collection
3549
- const collectionResponse = await fetch(`${baseUrl}/nft/getItems?collection=${collectionAddress}&limit=1000`, {
3550
- method: 'GET',
3551
- headers: {
3552
- 'Accept': 'application/json'
3553
- }
3554
- });
3555
- if (!collectionResponse.ok) {
3556
- continue; // Try next endpoint
3557
- }
3558
- const collectionData = await collectionResponse.json();
3559
- const nftItems = collectionData.nft_items || [];
3560
- // Filter NFTs owned by the specified address
3561
- const ownedNFTs = [];
3562
- for (const nft of nftItems) {
3563
- if (nft.owner?.address === ownerAddress) {
3564
- ownedNFTs.push({
3565
- collection: collectionAddress,
3566
- nftAddress: nft.address,
3567
- name: nft.metadata?.name,
3568
- description: nft.metadata?.description
3569
- });
3570
- }
3571
- }
3572
- return ownedNFTs;
3573
- }
3574
- catch (error) {
3575
- this.debugTools.warn('WALLET_CONNECTOR', `TON API call failed for ${baseUrl}`, error);
3576
- continue; // Try next endpoint
3577
- }
3578
- }
3579
- // If all endpoints fail, try a simpler approach using getAccountNftItems
3580
- try {
3581
- const accountResponse = await fetch(`${endpoints[0]}/account/getNftItems?account=${ownerAddress}&collection=${collectionAddress}&limit=100`, {
3582
- method: 'GET',
3583
- headers: {
3584
- 'Accept': 'application/json'
3585
- }
3586
- });
3587
- if (accountResponse.ok) {
3588
- const accountData = await accountResponse.json();
3589
- const nftItems = accountData.nft_items || [];
3590
- return nftItems.map((nft) => ({
3591
- collection: collectionAddress,
3592
- nftAddress: nft.address,
3593
- name: nft.metadata?.name,
3594
- description: nft.metadata?.description
3595
- }));
3596
- }
3597
- }
3598
- catch (error) {
3599
- this.debugTools.warn('WALLET_CONNECTOR', 'Fallback NFT query also failed', error);
3600
- }
3601
- // Return empty array if all queries fail
3602
- return [];
3603
- }
3604
3437
  }
3605
3438
 
3606
3439
  /**
@@ -7651,8 +7484,41 @@ function showDebugLog(msg) {
7651
7484
  const tonApiCache = new Map();
7652
7485
  const CACHE_DURATION = 30000; // 30 seconds
7653
7486
  const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className, style, showBalance = false, modalPosition = 'center', theme = 'auto', buttonText = 'Connect Wallet', getExplorerUrl, }) => {
7487
+ // Move all state hooks that are referenced early to the top to avoid use-before-init errors
7488
+ const [isInitializing, setIsInitializing] = useState(true);
7489
+ const [isConnecting, setIsConnecting] = useState(false);
7490
+ const [balanceLoading, setBalanceLoading] = useState(false);
7654
7491
  // Always fetch balance automatically when wallet is connected
7655
7492
  // Balance is always fetched automatically from the chain
7493
+ // Helper: get the best available TWC balance (cached or real)
7494
+ const getDisplayBalance = () => {
7495
+ // Prefer real fetched balance if available and non-zero
7496
+ if (twcBalance && twcBalance !== '0' && twcBalance !== '0.00')
7497
+ return twcBalance;
7498
+ // Fallback to cached balance if available and non-zero
7499
+ if (account?.address) {
7500
+ const cached = loadTWCBalanceFromCache(account.address);
7501
+ if (cached?.balance && cached.balance !== '0' && cached.balance !== '0.00')
7502
+ return cached.balance;
7503
+ }
7504
+ // If loading, keep showing last known value (even if zero)
7505
+ if (isInitializing || isConnecting) {
7506
+ if (twcBalance)
7507
+ return twcBalance;
7508
+ if (account?.address) {
7509
+ const cached = loadTWCBalanceFromCache(account.address);
7510
+ if (cached?.balance)
7511
+ return cached.balance;
7512
+ }
7513
+ }
7514
+ // Otherwise, show 0
7515
+ return '0';
7516
+ };
7517
+ // ...existing code...
7518
+ // ...existing code...
7519
+ // Helper: show spinner if loading/connecting
7520
+ // Show spinner if initializing, connecting, or balance is loading
7521
+ const showSpinner = Boolean(isInitializing || isConnecting || balanceLoading);
7656
7522
  // Detect dark mode
7657
7523
  const [isDarkMode, setIsDarkMode] = useState(false);
7658
7524
  useEffect(() => {
@@ -7670,7 +7536,6 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
7670
7536
  }, [theme]);
7671
7537
  const [account, setAccount] = useState(null);
7672
7538
  const [status, setStatus] = useState(ConnectionStatus.DISCONNECTED);
7673
- const [isInitializing, setIsInitializing] = useState(true);
7674
7539
  const [showModal, setShowModal] = useState(false);
7675
7540
  // Preload Reown SDK when modal opens for faster connection
7676
7541
  React.useEffect(() => {
@@ -7692,7 +7557,6 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
7692
7557
  const [showActivityModal, setShowActivityModal] = useState(false);
7693
7558
  const [showTWCBalanceModal, setShowTWCBalanceModal] = useState(false);
7694
7559
  const [availableWallets, setAvailableWallets] = useState([]);
7695
- const [isConnecting, setIsConnecting] = useState(false);
7696
7560
  const [error, setError] = useState(null);
7697
7561
  const [nftCount, setNftCount] = useState(0);
7698
7562
  const [nftLoading, setNftLoading] = useState(false);
@@ -7703,7 +7567,6 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
7703
7567
  const [txsLoading, setTxsLoading] = useState(false);
7704
7568
  const [txsError, setTxsError] = useState(null);
7705
7569
  const [balance, setBalance] = useState({ amount: '', symbol: '' });
7706
- const [balanceLoading, setBalanceLoading] = useState(false);
7707
7570
  const [currentChainId, setCurrentChainId] = useState(null);
7708
7571
  const [isWrongNetwork, setIsWrongNetwork] = useState(false);
7709
7572
  // TWC Balance cache key prefix
@@ -8531,10 +8394,13 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
8531
8394
  // Fetch TWC balance for all EVM chains (for debugging)
8532
8395
  try {
8533
8396
  const state = connector.getState();
8534
- if (state.account?.chainType !== 'evm') {
8535
- // Not EVM, do not fetch TWC balance
8397
+ if (state.status !== 'connected' ||
8398
+ !state.account ||
8399
+ state.account.chainType !== 'evm') {
8400
+ // Not connected, no account, or not EVM, do not fetch TWC balance
8536
8401
  return null;
8537
8402
  }
8403
+ // Allow fetching even if chainId is null, as long as account exists and is EVM
8538
8404
  // Log chainId for debug
8539
8405
  showDebugLog?.(`[fetchTWCBalanceWithRetry] Fetching TWC for chainId=${state.chainId}`);
8540
8406
  const twcBalance = await connector.getTWCBalance();
@@ -8878,8 +8744,8 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
8878
8744
  }, [connector, account]);
8879
8745
  // Render button based on connection status
8880
8746
  const renderButton = () => {
8881
- // Only show connect button if we are fully initialized and truly disconnected (no account and status is DISCONNECTED)
8882
- if (!isInitializing && (!account && status === ConnectionStatus.DISCONNECTED)) {
8747
+ // Always show connect button for first-time visitors or when not connected
8748
+ if (!account && status === ConnectionStatus.DISCONNECTED) {
8883
8749
  // Wallet icon SVG component
8884
8750
  const WalletIcon = () => (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { display: 'block' }, children: [jsx("path", { d: "M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" }), jsx("path", { d: "M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" })] }));
8885
8751
  return (jsxs("button", { onClick: () => handleOpenModal(), style: {
@@ -9053,34 +8919,75 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
9053
8919
  }
9054
8920
  // Wallet icon SVG component
9055
8921
  const WalletIcon = () => (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { display: 'block' }, children: [jsx("path", { d: "M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" }), jsx("path", { d: "M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" })] }));
9056
- // Only show connect button if not connected
9057
- if (!account || status !== ConnectionStatus.CONNECTED) {
9058
- return (jsxs("button", { onClick: () => handleOpenModal(), style: {
9059
- display: 'flex',
9060
- alignItems: 'center',
9061
- gap: '10px',
9062
- padding: '12px 12px',
9063
- borderRadius: '12px',
9064
- border: 'none',
9065
- backgroundColor: '#FF9814', // Warm golden-orange
9066
- color: '#000000', // Black text
9067
- fontWeight: '700',
9068
- fontSize: '15px',
9069
- cursor: 'pointer',
9070
- transition: 'all 0.15s ease',
9071
- fontFamily: 'Lexend, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
9072
- boxShadow: '0 2px 8px rgba(255, 152, 20, 0.3)',
9073
- ...style,
9074
- }, className: className, onMouseEnter: (e) => {
9075
- e.currentTarget.style.backgroundColor = '#E8870F';
9076
- e.currentTarget.style.transform = 'translateY(-1px)';
9077
- e.currentTarget.style.boxShadow = '0 4px 12px rgba(255, 152, 20, 0.4)';
9078
- }, onMouseLeave: (e) => {
9079
- e.currentTarget.style.backgroundColor = '#FF9814';
9080
- e.currentTarget.style.transform = 'translateY(0)';
9081
- e.currentTarget.style.boxShadow = '0 2px 8px rgba(255, 152, 20, 0.3)';
9082
- }, children: [jsx("span", { style: { color: '#000000', display: 'flex', alignItems: 'center' }, children: jsx(WalletIcon, {}) }), jsx("span", { style: { color: '#000000', fontWeight: '500', fontSize: '14px' }, children: buttonText })] }));
9083
- }
8922
+ // Always show the button, but change content based on connection state
8923
+ const displayBalance = getDisplayBalance();
8924
+ const isConnected = !!account && status === ConnectionStatus.CONNECTED;
8925
+ // Helper for details modal open
8926
+ const handleOpenDetailsModal = () => setShowDetailsModal(true);
8927
+ // Wallet icon for display
8928
+ const walletIcon = account ? getWalletIcon(connector.getState().wallet || '') : jsx("span", { children: "\uD83D\uDCBC" });
8929
+ // Current wallet type
8930
+ const currentWalletType = connector.getState().wallet;
8931
+ return (jsxs(React.Fragment, { children: [jsx("style", { children: `
8932
+ @import url('https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap');
8933
+ .tiwiflix-scrollbar-hide::-webkit-scrollbar {
8934
+ display: none;
8935
+ }
8936
+ .twc-balance-spinner {
8937
+ display: inline-block;
8938
+ width: 16px;
8939
+ height: 16px;
8940
+ border: 2px solid #FF9814;
8941
+ border-top: 2px solid #fff;
8942
+ border-radius: 50%;
8943
+ animation: spin 0.7s linear infinite;
8944
+ margin-left: 6px;
8945
+ vertical-align: middle;
8946
+ }
8947
+ @keyframes spin {
8948
+ 0% { transform: rotate(0deg); }
8949
+ 100% { transform: rotate(360deg); }
8950
+ }
8951
+ ` }), jsxs("button", { onClick: () => (isConnected ? handleOpenDetailsModal() : handleOpenModal()), style: {
8952
+ display: 'flex',
8953
+ alignItems: 'center',
8954
+ gap: '10px',
8955
+ padding: '12px 12px',
8956
+ borderRadius: '12px',
8957
+ border: 'none',
8958
+ backgroundColor: isConnected ? '#0A0A0A' : '#FF9814',
8959
+ color: isConnected ? '#FF9814' : '#000000',
8960
+ fontWeight: '700',
8961
+ fontSize: '15px',
8962
+ cursor: showSpinner ? 'wait' : 'pointer',
8963
+ transition: 'all 0.15s ease',
8964
+ fontFamily: 'Lexend, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
8965
+ boxShadow: isConnected ? '0 2px 8px rgba(10,10,10,0.3)' : '0 2px 8px rgba(255, 152, 20, 0.3)',
8966
+ opacity: showSpinner ? 0.85 : 1,
8967
+ pointerEvents: showSpinner ? 'none' : 'auto',
8968
+ ...style,
8969
+ }, className: className, disabled: showSpinner, onMouseEnter: (e) => {
8970
+ if (!isConnected) {
8971
+ e.currentTarget.style.backgroundColor = '#E8870F';
8972
+ e.currentTarget.style.transform = 'translateY(-1px)';
8973
+ e.currentTarget.style.boxShadow = '0 4px 12px rgba(255, 152, 20, 0.4)';
8974
+ }
8975
+ }, onMouseLeave: (e) => {
8976
+ if (!isConnected) {
8977
+ e.currentTarget.style.backgroundColor = '#FF9814';
8978
+ e.currentTarget.style.transform = 'translateY(0)';
8979
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(255, 152, 20, 0.3)';
8980
+ }
8981
+ }, children: [jsx("span", { style: { color: isConnected ? '#FF9814' : '#000000', display: 'flex', alignItems: 'center' }, children: jsx(WalletIcon, {}) }), jsx("span", { style: { color: isConnected ? '#FF9814' : '#000000', fontWeight: '500', fontSize: '14px' }, children: isConnected ? (jsxs(Fragment, { children: [twcIconMemoized, jsx("span", { style: { marginLeft: 6, fontWeight: 600, fontFamily: 'SF Mono, Monaco, Inconsolata, Roboto Mono, monospace' }, children: displayBalance }), showSpinner && jsx("span", { className: "twc-balance-spinner" })] })) : (jsxs(Fragment, { children: [buttonText, showSpinner && jsx("span", { className: "twc-balance-spinner" })] })) })] }), jsx(AccountDetailsModal, { isOpen: showDetailsModal, onClose: () => setShowDetailsModal(false), account: account, onDisconnect: handleDisconnect, onCopyAddress: handleCopyAddress, onOpenSwap: () => setShowSwapModal(true), onOpenSend: () => setShowSendModal(true), onOpenActivity: () => setShowActivityModal(true), balance: balance, balanceLoading: balanceLoading, twcBalance: twcBalance, usdValue: usdValue, nftCount: nftCount, nftLoading: nftLoading, tonBalance: balance.amount || tonBalance, tonUsdValue: balance.usdValue ?? tonUsdValue, walletIcon: walletIcon, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition,
8982
+ // Add a Switch Wallet button to allow explicit wallet switching
8983
+ onSwitchWallet: () => handleOpenModal(true) }), jsx(ActivityModal, { isOpen: showActivityModal, onClose: () => setShowActivityModal(false), transactions: txs, loading: txsLoading, error: txsError, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition }), jsx(SwapModal, { isOpen: showSwapModal && account?.chainType !== 'ton', onClose: () => setShowSwapModal(false), colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition }), jsx(SendModal, { isOpen: showSendModal, onClose: () => setShowSendModal(false), balance: balance, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition, onSend: async () => {
8984
+ alert('Send functionality coming soon');
8985
+ setShowSendModal(false);
8986
+ } }), jsx(TWCBalanceModal, { isOpen: showTWCBalanceModal, onClose: () => setShowTWCBalanceModal(false), twcBalance: twcBalance, usdValue: usdValue, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition, onBuyTWC: () => {
8987
+ // Handle Buy TWC action
8988
+ }, onLearnMore: () => {
8989
+ // Handle Learn More action
8990
+ } }), jsx(WalletSelectModal, { isOpen: showModal, onClose: () => setShowModal(false), onSelectWallet: handleConnect, availableWallets: availableWallets, currentWalletType: currentWalletType, isConnecting: isConnecting, error: error, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition })] }));
9084
8991
  };
9085
8992
  // Group wallets by chain
9086
8993
  availableWallets.reduce((acc, wallet) => {
package/dist/index.js CHANGED
@@ -1909,7 +1909,7 @@ const defaultMultiChainConfig = {
1909
1909
  {
1910
1910
  chainId: 56,
1911
1911
  name: 'BNB Smart Chain',
1912
- rpcUrl: 'https://bsc-dataseed.binance.org',
1912
+ rpcUrl: 'https://bsc.publicnode.com',
1913
1913
  blockExplorerUrl: 'https://bscscan.com',
1914
1914
  nativeCurrency: {
1915
1915
  name: 'BNB',
@@ -2314,6 +2314,7 @@ class WalletConnector {
2314
2314
  error: null,
2315
2315
  twcBalance: null,
2316
2316
  usdValue: null,
2317
+ chainId: null,
2317
2318
  };
2318
2319
  // Performance monitoring
2319
2320
  this.performanceMetrics = {
@@ -2327,8 +2328,6 @@ class WalletConnector {
2327
2328
  * TWC is ONLY available on BSC (chainId 56)
2328
2329
  */
2329
2330
  // Cache for TWC balance to avoid redundant fetches
2330
- this.twcBalanceCache = null;
2331
- this.TWC_CACHE_TTL = 30000; // 30 seconds cache
2332
2331
  // Error tracking for balance fetching
2333
2332
  this.balanceFetchErrors = [];
2334
2333
  this.config = config;
@@ -2427,13 +2426,38 @@ class WalletConnector {
2427
2426
  * Connect to a wallet
2428
2427
  */
2429
2428
  async connect(walletType) {
2429
+ // Only allow EVM wallet connections on BSC (chainId 56)
2430
+ let adapter = this.adapters.get(walletType);
2431
+ if (adapter && adapter.chainType === exports.ChainType.EVM && typeof adapter.getChainId === 'function') {
2432
+ try {
2433
+ const chainId = await adapter.getChainId();
2434
+ if (chainId !== 56) {
2435
+ const error = new Error('Only Binance Smart Chain (BSC) is supported for EVM wallet connections. Please switch your wallet to BSC and try again.');
2436
+ this.updateState({
2437
+ status: exports.ConnectionStatus.ERROR,
2438
+ error,
2439
+ });
2440
+ this.eventEmitter.emit(exports.WalletEvent.ERROR, error);
2441
+ throw error;
2442
+ }
2443
+ }
2444
+ catch (e) {
2445
+ const error = new Error('Failed to detect EVM chain. Please ensure your wallet is connected to BSC.');
2446
+ this.updateState({
2447
+ status: exports.ConnectionStatus.ERROR,
2448
+ error,
2449
+ });
2450
+ this.eventEmitter.emit(exports.WalletEvent.ERROR, error);
2451
+ throw error;
2452
+ }
2453
+ }
2430
2454
  // Prevent duplicate connection requests
2431
2455
  if (this.state.status === exports.ConnectionStatus.CONNECTING) {
2432
2456
  const error = new Error('Connection already in progress. Please wait...');
2433
2457
  console.warn('Duplicate connection attempt blocked:', walletType);
2434
2458
  throw error;
2435
2459
  }
2436
- const adapter = this.adapters.get(walletType);
2460
+ // adapter already declared above
2437
2461
  if (!adapter) {
2438
2462
  const error = new Error(`Wallet ${walletType} is not registered`);
2439
2463
  this.updateState({
@@ -2670,7 +2694,7 @@ class WalletConnector {
2670
2694
  clearTimeout(safetyTimeout);
2671
2695
  clearTimeout(connectionTimeout);
2672
2696
  document.removeEventListener('visibilitychange', handleVisibilityChange);
2673
- error instanceof Error ? error.message : 'Connection failed';
2697
+ // removed unused errorMsg
2674
2698
  this.debugTools.error('WALLET_CONNECTOR', 'Connection error caught', error);
2675
2699
  // Reset state on connection error
2676
2700
  this.updateState({
@@ -2752,7 +2776,7 @@ class WalletConnector {
2752
2776
  }
2753
2777
  else {
2754
2778
  // Connection failed - reset state properly
2755
- result.error?.message || 'Failed to connect';
2779
+ // removed unused errorMsg
2756
2780
  this.debugTools.error('WALLET_CONNECTOR', 'Connection failed', result.error);
2757
2781
  this.updateState({
2758
2782
  status: exports.ConnectionStatus.DISCONNECTED,
@@ -3128,153 +3152,29 @@ class WalletConnector {
3128
3152
  this.sdkLoader.clearCache();
3129
3153
  }
3130
3154
  async getTWCBalance() {
3131
- if (!this.state.account) {
3132
- if (typeof window !== 'undefined')
3133
- window.showDebugLog?.('[getTWCBalance] No wallet connected');
3134
- throw new Error('No wallet connected');
3155
+ if (!this.state.account || this.state.account.chainType !== 'evm') {
3156
+ throw new Error('No EVM account connected');
3135
3157
  }
3136
3158
  const accountAddress = this.state.account.address;
3137
- const chainId = this.state.chainId;
3138
- performance.now();
3139
- if (typeof window !== 'undefined')
3140
- window.showDebugLog?.(`[getTWCBalance] Called for account=${accountAddress}, chainId=${chainId}`);
3141
- // Check cache first
3142
- if (this.twcBalanceCache &&
3143
- this.twcBalanceCache.address === accountAddress &&
3144
- Date.now() - this.twcBalanceCache.timestamp < this.TWC_CACHE_TTL) {
3145
- if (typeof window !== 'undefined')
3146
- window.showDebugLog?.(`[getTWCBalance] Using cache for ${accountAddress}: ${JSON.stringify(this.twcBalanceCache)}`);
3147
- return {
3148
- amount: this.twcBalanceCache.balance,
3149
- symbol: 'TWC',
3150
- usdValue: this.twcBalanceCache.usdValue,
3151
- usdFormatted: priceService.formatUSDValue(this.twcBalanceCache.usdValue)
3152
- };
3153
- }
3154
- // TWC contract on BSC
3155
3159
  const TWC_CONTRACT = '0xDA1060158F7D593667cCE0a15DB346BB3FfB3596';
3156
3160
  const TWC_DECIMALS = 9;
3157
3161
  const TWC_SYMBOL = 'TWC';
3158
- try {
3159
- // Get balance directly from wallet provider via EVM adapter
3160
- // Use the currently connected wallet's adapter
3161
- const currentWallet = this.state.wallet;
3162
- if (!currentWallet) {
3163
- if (typeof window !== 'undefined')
3164
- window.showDebugLog?.('[getTWCBalance] No wallet connected (no currentWallet)');
3165
- throw new Error('No wallet connected');
3166
- }
3167
- const adapter = this.adapters.get(currentWallet);
3168
- if (!adapter || adapter.chainType !== exports.ChainType.EVM) {
3169
- if (typeof window !== 'undefined')
3170
- window.showDebugLog?.('[getTWCBalance] No EVM adapter available');
3171
- throw new Error('No EVM adapter available');
3172
- }
3173
- // Debug: log adapter type and available methods
3174
- if (typeof window !== 'undefined') {
3175
- window.showDebugLog?.(`[getTWCBalance] Using adapter: ${adapter.constructor?.name || typeof adapter}`);
3176
- window.showDebugLog?.(`[getTWCBalance] Adapter keys: ${Object.keys(adapter).join(', ')}`);
3177
- window.showDebugLog?.(`[getTWCBalance] typeof getTokenBalance: ${typeof adapter.getTokenBalance}`);
3178
- }
3179
- let balance;
3180
- // Always use ethers fallback if EVM is connected, regardless of chainId
3181
- if (this.state.chainId !== 56) {
3182
- if (typeof window !== 'undefined')
3183
- window.showDebugLog?.(`[getTWCBalance] Warning: Not on BSC (chainId=${this.state.chainId}), but fetching TWC balance using ethers fallback.`);
3184
- // Fallback: use ethers.js to fetch balance from chain
3185
- let provider = adapter.provider || this.getProvider();
3186
- if (!provider) {
3187
- if (typeof window !== 'undefined')
3188
- window.showDebugLog?.('[getTWCBalance] No provider available for ethers fallback');
3189
- throw new Error('No provider available for ethers fallback');
3190
- }
3191
- if (!provider._isProvider) {
3192
- provider = new ethers.ethers.BrowserProvider(provider);
3193
- }
3194
- const erc20Abi = ["function balanceOf(address) view returns (uint256)"];
3195
- const contract = new ethers.ethers.Contract(TWC_CONTRACT, erc20Abi, provider);
3196
- const rawBalance = await contract.balanceOf(accountAddress);
3197
- if (typeof window !== 'undefined')
3198
- window.showDebugLog?.(`[getTWCBalance] [ethers fallback] rawBalance (BigInt) for ${accountAddress}: ${rawBalance?.toString?.()}`);
3199
- balance = ethers.ethers.formatUnits(rawBalance, TWC_DECIMALS);
3200
- if (typeof window !== 'undefined')
3201
- window.showDebugLog?.(`[getTWCBalance] Raw balance for ${accountAddress} (ethers): ${balance}`);
3202
- }
3203
- else if (typeof adapter.getTokenBalance === 'function') {
3204
- // Use adapter method if available and on BSC
3205
- const balancePromise = adapter.getTokenBalance(TWC_CONTRACT, TWC_DECIMALS, true);
3206
- const timeoutPromise = new Promise((_, reject) => {
3207
- setTimeout(() => reject(new Error('Balance fetch timeout after 10 seconds')), 10000);
3208
- });
3209
- balance = await Promise.race([balancePromise, timeoutPromise]);
3210
- if (typeof window !== 'undefined')
3211
- window.showDebugLog?.(`[getTWCBalance] Raw balance for ${accountAddress} (adapter): ${balance}`);
3212
- }
3213
- else {
3214
- // Fallback: use ethers.js to fetch balance from chain (should not happen, but for safety)
3215
- if (typeof window !== 'undefined')
3216
- window.showDebugLog?.('[getTWCBalance] Fallback to ethers.js for TWC balance (on BSC)');
3217
- let provider = adapter.provider || this.getProvider();
3218
- if (!provider) {
3219
- if (typeof window !== 'undefined')
3220
- window.showDebugLog?.('[getTWCBalance] No provider available for ethers fallback');
3221
- throw new Error('No provider available for ethers fallback');
3222
- }
3223
- if (!provider._isProvider) {
3224
- provider = new ethers.ethers.BrowserProvider(provider);
3225
- }
3226
- const erc20Abi = ["function balanceOf(address) view returns (uint256)"];
3227
- const contract = new ethers.ethers.Contract(TWC_CONTRACT, erc20Abi, provider);
3228
- const rawBalance = await contract.balanceOf(accountAddress);
3229
- if (typeof window !== 'undefined')
3230
- window.showDebugLog?.(`[getTWCBalance] [ethers fallback] rawBalance (BigInt) for ${accountAddress}: ${rawBalance?.toString?.()}`);
3231
- balance = ethers.ethers.formatUnits(rawBalance, TWC_DECIMALS);
3232
- if (typeof window !== 'undefined')
3233
- window.showDebugLog?.(`[getTWCBalance] Raw balance for ${accountAddress} (ethers): ${balance}`);
3234
- }
3235
- // Calculate USD value with 5 second timeout
3236
- const usdPromise = priceService.calculateTWCValue(balance);
3237
- const usdTimeoutPromise = new Promise((resolve) => {
3238
- setTimeout(() => resolve(0), 5000); // Default to 0 on timeout
3239
- });
3240
- const usdValue = await Promise.race([usdPromise, usdTimeoutPromise]);
3241
- if (typeof window !== 'undefined')
3242
- window.showDebugLog?.(`[getTWCBalance] USD value for ${accountAddress}: ${usdValue}`);
3243
- // Update cache
3244
- this.twcBalanceCache = {
3245
- balance,
3246
- usdValue,
3247
- timestamp: Date.now(),
3248
- address: accountAddress
3249
- };
3250
- const result = {
3251
- amount: balance,
3252
- symbol: TWC_SYMBOL,
3253
- usdValue,
3254
- usdFormatted: priceService.formatUSDValue(usdValue)
3255
- };
3256
- // Keep state in sync
3257
- this.updateState({ twcBalance: result.amount, usdValue: result.usdValue ?? null });
3258
- if (typeof window !== 'undefined')
3259
- window.showDebugLog?.(`[getTWCBalance] Final result for ${accountAddress}: ${JSON.stringify(result)}`);
3260
- return result;
3261
- }
3262
- catch (error) {
3263
- const errorMsg = error instanceof Error ? error.message : String(error);
3264
- if (typeof window !== 'undefined')
3265
- window.showDebugLog?.(`[getTWCBalance] ERROR: ${errorMsg}`);
3266
- // Track error
3267
- this.balanceFetchErrors.push({
3268
- endpoint: 'wallet-provider',
3269
- error: errorMsg,
3270
- timestamp: Date.now()
3271
- });
3272
- // Keep only last 10 errors
3273
- if (this.balanceFetchErrors.length > 10) {
3274
- this.balanceFetchErrors = this.balanceFetchErrors.slice(-10);
3275
- }
3276
- throw error;
3277
- }
3162
+ let provider = this.getProvider();
3163
+ // If provider is missing or not a valid ethers.js provider, use a public BSC provider
3164
+ if (!provider || typeof provider.call !== 'function') {
3165
+ provider = new ethers.ethers.JsonRpcProvider('https://bsc.publicnode.com', 56);
3166
+ }
3167
+ const erc20Abi = ["function balanceOf(address) view returns (uint256)"];
3168
+ const contract = new ethers.ethers.Contract(TWC_CONTRACT, erc20Abi, provider);
3169
+ const rawBalance = await contract.balanceOf(accountAddress);
3170
+ const balance = ethers.ethers.formatUnits(rawBalance, TWC_DECIMALS);
3171
+ const usdValue = await priceService.calculateTWCValue(balance);
3172
+ return {
3173
+ amount: balance,
3174
+ symbol: TWC_SYMBOL,
3175
+ usdValue,
3176
+ usdFormatted: priceService.formatUSDValue(usdValue)
3177
+ };
3278
3178
  }
3279
3179
  /**
3280
3180
  * Get balance fetch error history (for debugging)
@@ -3536,73 +3436,6 @@ class WalletConnector {
3536
3436
  return [];
3537
3437
  }
3538
3438
  }
3539
- /**
3540
- * Query TON API for NFTs owned by an address in a specific collection
3541
- */
3542
- async queryTONNFTs(ownerAddress, collectionAddress) {
3543
- // TON API endpoints to try
3544
- const endpoints = [
3545
- 'https://tonapi.io/v1',
3546
- 'https://testnet.tonapi.io/v1'
3547
- ];
3548
- for (const baseUrl of endpoints) {
3549
- try {
3550
- // First, get NFT items for the collection
3551
- const collectionResponse = await fetch(`${baseUrl}/nft/getItems?collection=${collectionAddress}&limit=1000`, {
3552
- method: 'GET',
3553
- headers: {
3554
- 'Accept': 'application/json'
3555
- }
3556
- });
3557
- if (!collectionResponse.ok) {
3558
- continue; // Try next endpoint
3559
- }
3560
- const collectionData = await collectionResponse.json();
3561
- const nftItems = collectionData.nft_items || [];
3562
- // Filter NFTs owned by the specified address
3563
- const ownedNFTs = [];
3564
- for (const nft of nftItems) {
3565
- if (nft.owner?.address === ownerAddress) {
3566
- ownedNFTs.push({
3567
- collection: collectionAddress,
3568
- nftAddress: nft.address,
3569
- name: nft.metadata?.name,
3570
- description: nft.metadata?.description
3571
- });
3572
- }
3573
- }
3574
- return ownedNFTs;
3575
- }
3576
- catch (error) {
3577
- this.debugTools.warn('WALLET_CONNECTOR', `TON API call failed for ${baseUrl}`, error);
3578
- continue; // Try next endpoint
3579
- }
3580
- }
3581
- // If all endpoints fail, try a simpler approach using getAccountNftItems
3582
- try {
3583
- const accountResponse = await fetch(`${endpoints[0]}/account/getNftItems?account=${ownerAddress}&collection=${collectionAddress}&limit=100`, {
3584
- method: 'GET',
3585
- headers: {
3586
- 'Accept': 'application/json'
3587
- }
3588
- });
3589
- if (accountResponse.ok) {
3590
- const accountData = await accountResponse.json();
3591
- const nftItems = accountData.nft_items || [];
3592
- return nftItems.map((nft) => ({
3593
- collection: collectionAddress,
3594
- nftAddress: nft.address,
3595
- name: nft.metadata?.name,
3596
- description: nft.metadata?.description
3597
- }));
3598
- }
3599
- }
3600
- catch (error) {
3601
- this.debugTools.warn('WALLET_CONNECTOR', 'Fallback NFT query also failed', error);
3602
- }
3603
- // Return empty array if all queries fail
3604
- return [];
3605
- }
3606
3439
  }
3607
3440
 
3608
3441
  /**
@@ -7653,8 +7486,41 @@ function showDebugLog(msg) {
7653
7486
  const tonApiCache = new Map();
7654
7487
  const CACHE_DURATION = 30000; // 30 seconds
7655
7488
  const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className, style, showBalance = false, modalPosition = 'center', theme = 'auto', buttonText = 'Connect Wallet', getExplorerUrl, }) => {
7489
+ // Move all state hooks that are referenced early to the top to avoid use-before-init errors
7490
+ const [isInitializing, setIsInitializing] = React.useState(true);
7491
+ const [isConnecting, setIsConnecting] = React.useState(false);
7492
+ const [balanceLoading, setBalanceLoading] = React.useState(false);
7656
7493
  // Always fetch balance automatically when wallet is connected
7657
7494
  // Balance is always fetched automatically from the chain
7495
+ // Helper: get the best available TWC balance (cached or real)
7496
+ const getDisplayBalance = () => {
7497
+ // Prefer real fetched balance if available and non-zero
7498
+ if (twcBalance && twcBalance !== '0' && twcBalance !== '0.00')
7499
+ return twcBalance;
7500
+ // Fallback to cached balance if available and non-zero
7501
+ if (account?.address) {
7502
+ const cached = loadTWCBalanceFromCache(account.address);
7503
+ if (cached?.balance && cached.balance !== '0' && cached.balance !== '0.00')
7504
+ return cached.balance;
7505
+ }
7506
+ // If loading, keep showing last known value (even if zero)
7507
+ if (isInitializing || isConnecting) {
7508
+ if (twcBalance)
7509
+ return twcBalance;
7510
+ if (account?.address) {
7511
+ const cached = loadTWCBalanceFromCache(account.address);
7512
+ if (cached?.balance)
7513
+ return cached.balance;
7514
+ }
7515
+ }
7516
+ // Otherwise, show 0
7517
+ return '0';
7518
+ };
7519
+ // ...existing code...
7520
+ // ...existing code...
7521
+ // Helper: show spinner if loading/connecting
7522
+ // Show spinner if initializing, connecting, or balance is loading
7523
+ const showSpinner = Boolean(isInitializing || isConnecting || balanceLoading);
7658
7524
  // Detect dark mode
7659
7525
  const [isDarkMode, setIsDarkMode] = React.useState(false);
7660
7526
  React.useEffect(() => {
@@ -7672,7 +7538,6 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
7672
7538
  }, [theme]);
7673
7539
  const [account, setAccount] = React.useState(null);
7674
7540
  const [status, setStatus] = React.useState(exports.ConnectionStatus.DISCONNECTED);
7675
- const [isInitializing, setIsInitializing] = React.useState(true);
7676
7541
  const [showModal, setShowModal] = React.useState(false);
7677
7542
  // Preload Reown SDK when modal opens for faster connection
7678
7543
  React.useEffect(() => {
@@ -7694,7 +7559,6 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
7694
7559
  const [showActivityModal, setShowActivityModal] = React.useState(false);
7695
7560
  const [showTWCBalanceModal, setShowTWCBalanceModal] = React.useState(false);
7696
7561
  const [availableWallets, setAvailableWallets] = React.useState([]);
7697
- const [isConnecting, setIsConnecting] = React.useState(false);
7698
7562
  const [error, setError] = React.useState(null);
7699
7563
  const [nftCount, setNftCount] = React.useState(0);
7700
7564
  const [nftLoading, setNftLoading] = React.useState(false);
@@ -7705,7 +7569,6 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
7705
7569
  const [txsLoading, setTxsLoading] = React.useState(false);
7706
7570
  const [txsError, setTxsError] = React.useState(null);
7707
7571
  const [balance, setBalance] = React.useState({ amount: '', symbol: '' });
7708
- const [balanceLoading, setBalanceLoading] = React.useState(false);
7709
7572
  const [currentChainId, setCurrentChainId] = React.useState(null);
7710
7573
  const [isWrongNetwork, setIsWrongNetwork] = React.useState(false);
7711
7574
  // TWC Balance cache key prefix
@@ -8533,10 +8396,13 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
8533
8396
  // Fetch TWC balance for all EVM chains (for debugging)
8534
8397
  try {
8535
8398
  const state = connector.getState();
8536
- if (state.account?.chainType !== 'evm') {
8537
- // Not EVM, do not fetch TWC balance
8399
+ if (state.status !== 'connected' ||
8400
+ !state.account ||
8401
+ state.account.chainType !== 'evm') {
8402
+ // Not connected, no account, or not EVM, do not fetch TWC balance
8538
8403
  return null;
8539
8404
  }
8405
+ // Allow fetching even if chainId is null, as long as account exists and is EVM
8540
8406
  // Log chainId for debug
8541
8407
  showDebugLog?.(`[fetchTWCBalanceWithRetry] Fetching TWC for chainId=${state.chainId}`);
8542
8408
  const twcBalance = await connector.getTWCBalance();
@@ -8880,8 +8746,8 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
8880
8746
  }, [connector, account]);
8881
8747
  // Render button based on connection status
8882
8748
  const renderButton = () => {
8883
- // Only show connect button if we are fully initialized and truly disconnected (no account and status is DISCONNECTED)
8884
- if (!isInitializing && (!account && status === exports.ConnectionStatus.DISCONNECTED)) {
8749
+ // Always show connect button for first-time visitors or when not connected
8750
+ if (!account && status === exports.ConnectionStatus.DISCONNECTED) {
8885
8751
  // Wallet icon SVG component
8886
8752
  const WalletIcon = () => (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { display: 'block' }, children: [jsxRuntime.jsx("path", { d: "M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" }), jsxRuntime.jsx("path", { d: "M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" })] }));
8887
8753
  return (jsxRuntime.jsxs("button", { onClick: () => handleOpenModal(), style: {
@@ -9055,34 +8921,75 @@ const ConnectButton = ({ connector, onConnect, onDisconnect, onError, className,
9055
8921
  }
9056
8922
  // Wallet icon SVG component
9057
8923
  const WalletIcon = () => (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { display: 'block' }, children: [jsxRuntime.jsx("path", { d: "M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" }), jsxRuntime.jsx("path", { d: "M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" })] }));
9058
- // Only show connect button if not connected
9059
- if (!account || status !== exports.ConnectionStatus.CONNECTED) {
9060
- return (jsxRuntime.jsxs("button", { onClick: () => handleOpenModal(), style: {
9061
- display: 'flex',
9062
- alignItems: 'center',
9063
- gap: '10px',
9064
- padding: '12px 12px',
9065
- borderRadius: '12px',
9066
- border: 'none',
9067
- backgroundColor: '#FF9814', // Warm golden-orange
9068
- color: '#000000', // Black text
9069
- fontWeight: '700',
9070
- fontSize: '15px',
9071
- cursor: 'pointer',
9072
- transition: 'all 0.15s ease',
9073
- fontFamily: 'Lexend, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
9074
- boxShadow: '0 2px 8px rgba(255, 152, 20, 0.3)',
9075
- ...style,
9076
- }, className: className, onMouseEnter: (e) => {
9077
- e.currentTarget.style.backgroundColor = '#E8870F';
9078
- e.currentTarget.style.transform = 'translateY(-1px)';
9079
- e.currentTarget.style.boxShadow = '0 4px 12px rgba(255, 152, 20, 0.4)';
9080
- }, onMouseLeave: (e) => {
9081
- e.currentTarget.style.backgroundColor = '#FF9814';
9082
- e.currentTarget.style.transform = 'translateY(0)';
9083
- e.currentTarget.style.boxShadow = '0 2px 8px rgba(255, 152, 20, 0.3)';
9084
- }, children: [jsxRuntime.jsx("span", { style: { color: '#000000', display: 'flex', alignItems: 'center' }, children: jsxRuntime.jsx(WalletIcon, {}) }), jsxRuntime.jsx("span", { style: { color: '#000000', fontWeight: '500', fontSize: '14px' }, children: buttonText })] }));
9085
- }
8924
+ // Always show the button, but change content based on connection state
8925
+ const displayBalance = getDisplayBalance();
8926
+ const isConnected = !!account && status === exports.ConnectionStatus.CONNECTED;
8927
+ // Helper for details modal open
8928
+ const handleOpenDetailsModal = () => setShowDetailsModal(true);
8929
+ // Wallet icon for display
8930
+ const walletIcon = account ? getWalletIcon(connector.getState().wallet || '') : jsxRuntime.jsx("span", { children: "\uD83D\uDCBC" });
8931
+ // Current wallet type
8932
+ const currentWalletType = connector.getState().wallet;
8933
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("style", { children: `
8934
+ @import url('https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap');
8935
+ .tiwiflix-scrollbar-hide::-webkit-scrollbar {
8936
+ display: none;
8937
+ }
8938
+ .twc-balance-spinner {
8939
+ display: inline-block;
8940
+ width: 16px;
8941
+ height: 16px;
8942
+ border: 2px solid #FF9814;
8943
+ border-top: 2px solid #fff;
8944
+ border-radius: 50%;
8945
+ animation: spin 0.7s linear infinite;
8946
+ margin-left: 6px;
8947
+ vertical-align: middle;
8948
+ }
8949
+ @keyframes spin {
8950
+ 0% { transform: rotate(0deg); }
8951
+ 100% { transform: rotate(360deg); }
8952
+ }
8953
+ ` }), jsxRuntime.jsxs("button", { onClick: () => (isConnected ? handleOpenDetailsModal() : handleOpenModal()), style: {
8954
+ display: 'flex',
8955
+ alignItems: 'center',
8956
+ gap: '10px',
8957
+ padding: '12px 12px',
8958
+ borderRadius: '12px',
8959
+ border: 'none',
8960
+ backgroundColor: isConnected ? '#0A0A0A' : '#FF9814',
8961
+ color: isConnected ? '#FF9814' : '#000000',
8962
+ fontWeight: '700',
8963
+ fontSize: '15px',
8964
+ cursor: showSpinner ? 'wait' : 'pointer',
8965
+ transition: 'all 0.15s ease',
8966
+ fontFamily: 'Lexend, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
8967
+ boxShadow: isConnected ? '0 2px 8px rgba(10,10,10,0.3)' : '0 2px 8px rgba(255, 152, 20, 0.3)',
8968
+ opacity: showSpinner ? 0.85 : 1,
8969
+ pointerEvents: showSpinner ? 'none' : 'auto',
8970
+ ...style,
8971
+ }, className: className, disabled: showSpinner, onMouseEnter: (e) => {
8972
+ if (!isConnected) {
8973
+ e.currentTarget.style.backgroundColor = '#E8870F';
8974
+ e.currentTarget.style.transform = 'translateY(-1px)';
8975
+ e.currentTarget.style.boxShadow = '0 4px 12px rgba(255, 152, 20, 0.4)';
8976
+ }
8977
+ }, onMouseLeave: (e) => {
8978
+ if (!isConnected) {
8979
+ e.currentTarget.style.backgroundColor = '#FF9814';
8980
+ e.currentTarget.style.transform = 'translateY(0)';
8981
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(255, 152, 20, 0.3)';
8982
+ }
8983
+ }, children: [jsxRuntime.jsx("span", { style: { color: isConnected ? '#FF9814' : '#000000', display: 'flex', alignItems: 'center' }, children: jsxRuntime.jsx(WalletIcon, {}) }), jsxRuntime.jsx("span", { style: { color: isConnected ? '#FF9814' : '#000000', fontWeight: '500', fontSize: '14px' }, children: isConnected ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [twcIconMemoized, jsxRuntime.jsx("span", { style: { marginLeft: 6, fontWeight: 600, fontFamily: 'SF Mono, Monaco, Inconsolata, Roboto Mono, monospace' }, children: displayBalance }), showSpinner && jsxRuntime.jsx("span", { className: "twc-balance-spinner" })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [buttonText, showSpinner && jsxRuntime.jsx("span", { className: "twc-balance-spinner" })] })) })] }), jsxRuntime.jsx(AccountDetailsModal, { isOpen: showDetailsModal, onClose: () => setShowDetailsModal(false), account: account, onDisconnect: handleDisconnect, onCopyAddress: handleCopyAddress, onOpenSwap: () => setShowSwapModal(true), onOpenSend: () => setShowSendModal(true), onOpenActivity: () => setShowActivityModal(true), balance: balance, balanceLoading: balanceLoading, twcBalance: twcBalance, usdValue: usdValue, nftCount: nftCount, nftLoading: nftLoading, tonBalance: balance.amount || tonBalance, tonUsdValue: balance.usdValue ?? tonUsdValue, walletIcon: walletIcon, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition,
8984
+ // Add a Switch Wallet button to allow explicit wallet switching
8985
+ onSwitchWallet: () => handleOpenModal(true) }), jsxRuntime.jsx(ActivityModal, { isOpen: showActivityModal, onClose: () => setShowActivityModal(false), transactions: txs, loading: txsLoading, error: txsError, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition }), jsxRuntime.jsx(SwapModal, { isOpen: showSwapModal && account?.chainType !== 'ton', onClose: () => setShowSwapModal(false), colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition }), jsxRuntime.jsx(SendModal, { isOpen: showSendModal, onClose: () => setShowSendModal(false), balance: balance, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition, onSend: async () => {
8986
+ alert('Send functionality coming soon');
8987
+ setShowSendModal(false);
8988
+ } }), jsxRuntime.jsx(TWCBalanceModal, { isOpen: showTWCBalanceModal, onClose: () => setShowTWCBalanceModal(false), twcBalance: twcBalance, usdValue: usdValue, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition, onBuyTWC: () => {
8989
+ // Handle Buy TWC action
8990
+ }, onLearnMore: () => {
8991
+ // Handle Learn More action
8992
+ } }), jsxRuntime.jsx(WalletSelectModal, { isOpen: showModal, onClose: () => setShowModal(false), onSelectWallet: handleConnect, availableWallets: availableWallets, currentWalletType: currentWalletType, isConnecting: isConnecting, error: error, colors: colors, isDarkMode: isDarkMode, modalPosition: modalPosition })] }));
9086
8993
  };
9087
8994
  // Group wallets by chain
9088
8995
  availableWallets.reduce((acc, wallet) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiwiflix-wallet-connector",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "description": "Multi-chain wallet connector for Tiwiflix supporting EVM, TON, Solana, and Tron wallets",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -60,7 +60,7 @@
60
60
  "@tonconnect/ui": "^2.3.1",
61
61
  "@walletconnect/ethereum-provider": "^2.13.0",
62
62
  "@walletconnect/universal-provider": "^2.21.10",
63
- "ethers": "^6.13.0",
63
+ "ethers": "^6.16.0",
64
64
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
65
65
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
66
66
  "viem": "^2.42.1",