troubleshooting - saltict/Demo-Docs GitHub Wiki

Troubleshooting Guide

Common issues, solutions, and debugging techniques for the SubWallet Services SDK.

📖 Navigation


Table of Contents

Common Issues

Error: "Cannot resolve module '@subwallet-monorepos/subwallet-services-sdk'"

Cause: Package not installed or incorrect import path.

Solution:

# Reinstall the package
npm uninstall @subwallet-monorepos/subwallet-services-sdk
npm install @subwallet-monorepos/subwallet-services-sdk

# Verify installation
npm list @subwallet-monorepos/subwallet-services-sdk

Alternative import methods:

// Default import (recommended)
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';

// Named import
import { SubWalletServiceSDK } from '@subwallet-monorepos/subwallet-services-sdk';
const sdk = SubWalletServiceSDK.getInstance();

// CommonJS (if required)
const subwalletApiSdk = require('@subwallet-monorepos/subwallet-services-sdk').default;

Error: "Failed to fetch data: Network request failed"

Cause: Network connectivity issues or CORS restrictions.

Solution:

// Check network connectivity
async function checkConnectivity() {
  try {
    const response = await fetch('https://sw-services.subwallet.app/health');
    console.log('API Status:', response.status);
  } catch (error) {
    console.error('Network issue:', error);
  }
}

// Configure custom endpoint if needed
subwalletApiSdk.updateConfig({
  baseUrl: 'https://your-proxy-endpoint.com'
});

Error: "TypeError: Cannot read property 'getPriceHistory' of undefined"

Cause: SDK not properly initialized or service not available.

Solution:

// Ensure SDK is properly initialized
const sdk = SubWalletServiceSDK.getInstance();
console.log('SDK Version:', sdk.version);
console.log('Available services:', Object.keys(sdk));

// Wait for SDK initialization in async context
async function waitForSDK() {
  let retries = 0;
  while (!sdk.priceHistoryApi && retries < 10) {
    await new Promise(resolve => setTimeout(resolve, 100));
    retries++;
  }
  
  if (!sdk.priceHistoryApi) {
    throw new Error('SDK failed to initialize');
  }
}

Installation Problems

Issue: Dependency Resolution Conflicts

Symptoms:

npm ERR! peer dep missing: @types/node@^18.0.0
npm ERR! conflicting peer dependency

Solution:

# Install missing peer dependencies
npm install --save-dev @types/node@^18.0.0

# Or use --legacy-peer-deps flag
npm install --legacy-peer-deps

# For yarn
yarn install --ignore-engines

Issue: TypeScript Compilation Errors

Symptoms:

error TS2307: Cannot find module '@subwallet-monorepos/subwallet-services-sdk'
error TS2339: Property 'priceHistoryApi' does not exist

Solution:

// tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "skipLibCheck": true
  }
}

Issue: Build Tool Compatibility

Webpack 4 Issues:

// webpack.config.js
module.exports = {
  resolve: {
    fallback: {
      "crypto": require.resolve("crypto-browserify"),
      "stream": require.resolve("stream-browserify"),
      "buffer": require.resolve("buffer"),
      "process": require.resolve("process/browser")
    }
  },
  plugins: [
    new webpack.ProvidePlugin({
      Buffer: ['buffer', 'Buffer'],
      process: 'process/browser',
    })
  ]
};

Vite Issues:

// vite.config.ts
export default defineConfig({
  define: {
    global: 'globalThis',
  },
  resolve: {
    alias: {
      buffer: 'buffer',
    },
  },
  optimizeDeps: {
    include: ['buffer']
  }
});

Configuration Issues

Issue: Invalid Platform Configuration

Symptoms:

// Error: Invalid platform type
subwalletApiSdk.updateConfig({ platform: 'invalid' });

Solution:

// Valid platform values
const validPlatforms = ['extension', 'mobile', 'webapp'] as const;
type Platform = typeof validPlatforms[number];

// Validate before setting
function setPlatform(platform: string) {
  if (!validPlatforms.includes(platform as Platform)) {
    throw new Error(`Invalid platform: ${platform}. Must be one of: ${validPlatforms.join(', ')}`);
  }
  
  subwalletApiSdk.updateConfig({ platform: platform as Platform });
}

Issue: Configuration Not Applied

Symptoms: Changes to configuration don't take effect.

Solution:

// Verify configuration is applied
subwalletApiSdk.updateConfig({
  platform: 'mobile',
  baseUrl: 'https://staging-api.subwallet.app'
});

// Check current configuration
console.log('Current config:', {
  headers: subwalletApiSdk.headers,
  version: subwalletApiSdk.version
});

// Ensure services are updated
console.log('Services updated:', subwalletApiSdk.serviceList.length);

Issue: Environment Variables Not Loaded

Solution:

// Manual environment loading
function loadEnvironmentConfig() {
  const config = {
    baseUrl: process.env.SUBWALLET_API_URL || 'https://sw-services.subwallet.app',
    platform: (process.env.SUBWALLET_PLATFORM as any) || 'webapp',
    chainListVersion: process.env.SUBWALLET_CHAIN_LIST_VERSION || '0.2.108'
  };
  
  console.log('Loading config:', config);
  subwalletApiSdk.updateConfig(config);
  
  return config;
}

Network and API Errors

Issue: CORS Errors in Browser

Symptoms:

Access to fetch at 'https://sw-services.subwallet.app/' from origin 'http://localhost:3000' 
has been blocked by CORS policy

Solution:

// Use a proxy in development
// package.json (for Create React App)
{
  "proxy": "https://sw-services.subwallet.app"
}

// Or configure a custom proxy
subwalletApiSdk.updateConfig({
  baseUrl: '/api/proxy' // Your proxy endpoint
});

// Proxy server implementation
app.use('/api/proxy', createProxyMiddleware({
  target: 'https://sw-services.subwallet.app',
  changeOrigin: true,
  pathRewrite: {
    '^/api/proxy': ''
  }
}));

Issue: Request Timeout

Solution:

// Implement retry logic
async function retryRequest<T>(
  requestFn: () => Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await requestFn();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      
      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      delay *= 2; // Exponential backoff
    }
  }
  throw new Error('Max retries exceeded');
}

// Usage
const priceHistory = await retryRequest(() =>
  subwalletApiSdk.priceHistoryApi.getPriceHistory('DOT', '1W')
);

Issue: Rate Limiting

Symptoms:

HTTP 429: Too Many Requests

Solution:

// Implement rate limiting
class RateLimiter {
  private requests: number[] = [];
  private maxRequests: number;
  private timeWindow: number;

  constructor(maxRequests: number = 100, timeWindow: number = 60000) {
    this.maxRequests = maxRequests;
    this.timeWindow = timeWindow;
  }

  async checkLimit(): Promise<void> {
    const now = Date.now();
    this.requests = this.requests.filter(time => now - time < this.timeWindow);

    if (this.requests.length >= this.maxRequests) {
      const waitTime = this.requests[0] + this.timeWindow - now;
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }

    this.requests.push(now);
  }
}

const rateLimiter = new RateLimiter();

// Apply rate limiting to API calls
async function rateLimitedApiCall<T>(apiCall: () => Promise<T>): Promise<T> {
  await rateLimiter.checkLimit();
  return apiCall();
}

Platform-Specific Issues

Browser Extension Issues

Issue: Content Security Policy (CSP) Violations

Solution:

// manifest.json
{
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'; connect-src https://sw-services.subwallet.app"
  }
}

Issue: Background Script Limitations

Solution:

// Use messaging between content script and background
// background.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'apiCall') {
    handleApiCall(request.method, request.params)
      .then(result => sendResponse({ success: true, data: result }))
      .catch(error => sendResponse({ success: false, error: error.message }));
    return true; // Keep message channel open
  }
});

// content script
function apiCall(method: string, params: any) {
  return new Promise((resolve, reject) => {
    chrome.runtime.sendMessage(
      { action: 'apiCall', method, params },
      response => {
        if (response.success) {
          resolve(response.data);
        } else {
          reject(new Error(response.error));
        }
      }
    );
  });
}

React Native Issues

Issue: Metro bundler errors

Solution:

// metro.config.js
module.exports = {
  resolver: {
    alias: {
      crypto: 'crypto-browserify',
      stream: 'stream-browserify',
    },
  },
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};

Issue: Missing polyfills

Solution:

// index.js (React Native entry point)
import 'react-native-polyfill-globals/auto';

// Or manually install polyfills
import 'react-native-get-random-values';
import { Buffer } from 'buffer';
global.Buffer = Buffer;

Next.js Issues

Issue: Server-side rendering errors

Solution:

// Use dynamic imports with no SSR
import dynamic from 'next/dynamic';

const WalletComponent = dynamic(
  () => import('../components/WalletComponent'),
  { ssr: false }
);

// Or check for browser environment
useEffect(() => {
  if (typeof window !== 'undefined') {
    import('@subwallet-monorepos/subwallet-services-sdk').then(({ default: sdk }) => {
      // Use SDK only on client side
      sdk.updateConfig({ platform: 'webapp' });
    });
  }
}, []);

Performance Issues

Issue: Slow API Response Times

Solution:

// Implement request caching
class APICache {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private ttl: number;

  constructor(ttl: number = 60000) { // 1 minute default TTL
    this.ttl = ttl;
  }

  async get<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
    const cached = this.cache.get(key);
    const now = Date.now();

    if (cached && (now - cached.timestamp) < this.ttl) {
      return cached.data;
    }

    const data = await fetcher();
    this.cache.set(key, { data, timestamp: now });
    return data;
  }

  clear(): void {
    this.cache.clear();
  }
}

const apiCache = new APICache();

// Usage
const priceHistory = await apiCache.get(
  `price-${token}-${timeframe}`,
  () => subwalletApiSdk.priceHistoryApi.getPriceHistory(token, timeframe)
);

Issue: Memory Leaks

Solution:

// Proper cleanup in React components
useEffect(() => {
  let isMounted = true;

  const fetchData = async () => {
    try {
      const data = await subwalletApiSdk.priceHistoryApi.getPriceHistory('DOT', '1W');
      if (isMounted) {
        setData(data);
      }
    } catch (error) {
      if (isMounted) {
        setError(error);
      }
    }
  };

  fetchData();

  return () => {
    isMounted = false;
  };
}, []);

Debugging Techniques

Enable Debug Logging

// Enable verbose logging
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, options) => {
  console.log('🔍 API Request:', {
    url,
    method: options?.method || 'GET',
    headers: options?.headers,
    body: options?.body
  });
  
  const response = await originalFetch(url, options);
  
  console.log('📡 API Response:', {
    url,
    status: response.status,
    statusText: response.statusText
  });
  
  return response;
};

SDK Health Check

export async function sdkHealthCheck() {
  const diagnostics = {
    version: subwalletApiSdk.version,
    services: {},
    connectivity: false,
    timestamp: new Date().toISOString()
  };

  // Check each service
  const services = [
    'balanceDetectionApi',
    'priceHistoryApi',
    'swapApi',
    'xcmApi',
    'cardanoApi'
  ];

  for (const service of services) {
    diagnostics.services[service] = !!subwalletApiSdk[service];
  }

  // Test connectivity
  try {
    await subwalletApiSdk.priceHistoryApi.getPriceHistory('DOT', '1D');
    diagnostics.connectivity = true;
  } catch (error) {
    diagnostics.connectivity = false;
    diagnostics.error = error.message;
  }

  return diagnostics;
}

Error Tracking

// Global error handler for SDK errors
window.addEventListener('unhandledrejection', (event) => {
  if (event.reason?.message?.includes('SubWallet')) {
    console.error('🚨 SubWallet SDK Error:', {
      message: event.reason.message,
      stack: event.reason.stack,
      timestamp: new Date().toISOString()
    });
    
    // Send to error tracking service
    // errorTracker.captureException(event.reason);
  }
});

Getting Help

Debug Information to Collect

When reporting issues, please include:

  1. SDK Version:

    console.log('SDK Version:', subwalletApiSdk.version);
  2. Platform Information:

    console.log('Platform:', navigator.userAgent);
    console.log('Environment:', typeof window !== 'undefined' ? 'browser' : 'node');
  3. Configuration:

    console.log('SDK Config:', {
      headers: subwalletApiSdk.headers,
      version: subwalletApiSdk.version
    });
  4. Error Details:

    try {
      // Your failing code
    } catch (error) {
      console.error('Error details:', {
        message: error.message,
        stack: error.stack,
        name: error.name
      });
    }

Support Channels

Creating Bug Reports

Please include:

  • Steps to reproduce the issue
  • Expected vs. actual behavior
  • Error messages and stack traces
  • Environment details (OS, browser, Node.js version)
  • SDK version and configuration
  • Minimal code example that reproduces the issue

🔗 Related Documentation

⚠️ **GitHub.com Fallback** ⚠️