troubleshooting - saltict/Demo-Docs GitHub Wiki
Common issues, solutions, and debugging techniques for the SubWallet Services SDK.
📖 Navigation
- Common Issues
- Installation Problems
- Configuration Issues
- Network and API Errors
- Platform-Specific Issues
- Performance Issues
- Debugging Techniques
- Getting Help
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;
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'
});
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');
}
}
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
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
}
}
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']
}
});
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 });
}
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);
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;
}
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': ''
}
}));
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')
);
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();
}
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));
}
}
);
});
}
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;
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' });
});
}
}, []);
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)
);
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;
};
}, []);
// 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;
};
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;
}
// 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);
}
});
When reporting issues, please include:
-
SDK Version:
console.log('SDK Version:', subwalletApiSdk.version);
-
Platform Information:
console.log('Platform:', navigator.userAgent); console.log('Environment:', typeof window !== 'undefined' ? 'browser' : 'node');
-
Configuration:
console.log('SDK Config:', { headers: subwalletApiSdk.headers, version: subwalletApiSdk.version });
-
Error Details:
try { // Your failing code } catch (error) { console.error('Error details:', { message: error.message, stack: error.stack, name: error.name }); }
- GitHub Issues: Repository Issues
- Documentation: Full Documentation
- Community Discord: SubWallet Discord
- Email Support: [email protected]
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