UNPKG

@wasserstoff/tribes-sdk

Version:

SDK for integrating with Tribes by Astrix platform on any EVM compatible chain

241 lines (240 loc) 8.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseModule = void 0; const ethers_1 = require("ethers"); const core_1 = require("../types/core"); const errors_1 = require("../types/errors"); /** * Base class for all SDK modules with caching support */ class BaseModule { /** * Create a new module instance * @param provider Provider for read operations * @param config SDK configuration */ constructor(provider, config) { this.signer = null; // Use unknown for cached value type this.cache = new Map(); this.currentBlockNumber = 0; // Default cache settings this.DEFAULT_CACHE_TIME = 30000; // 30 seconds this.provider = provider; this.config = config; // Create block listener function this.blockListener = (blockNumber) => { this.currentBlockNumber = blockNumber; // Invalidate cache entries that depend on block number this.invalidateBlockBasedCache(blockNumber); }; // Set up block monitoring for cache invalidation this.setupBlockMonitoring(); } /** * Sets up block monitoring to invalidate cache based on new blocks */ setupBlockMonitoring() { // Clean up any existing subscription this.cleanupBlockSubscription(); // Start monitoring blocks this.provider.on('block', this.blockListener); // Initialize current block number this.provider.getBlockNumber() .then(blockNumber => { this.currentBlockNumber = blockNumber; }) .catch(error => { this.log(`Error getting initial block number: ${error.message}`); }); } /** * Invalidates cache entries that are bound to specific blocks */ invalidateBlockBasedCache(currentBlock) { for (const [key, entry] of this.cache.entries()) { // If this entry has a blockNumber and it's not the current block if (entry.blockNumber && entry.blockNumber < currentBlock) { this.cache.delete(key); } } } /** * Clean up block subscription */ cleanupBlockSubscription() { if (this.blockListener) { this.provider.off('block', this.blockListener); } } /** * Set the signer for write operations * @param signer Ethers signer object */ setSigner(signer) { this.signer = signer; } /** * Check if a signer is connected */ requireSigner() { if (!this.signer) { throw new errors_1.AstrixSDKError(core_1.ErrorType.CONNECTION_ERROR, 'Signer is required for this operation. Please connect a wallet.'); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.signer; } /** * Get a contract instance * @param address Contract address * @param abi Contract ABI * @param useSigner Whether to use the signer or provider */ getContract(address, abi, useSigner = false) { if (useSigner) { const signer = this.requireSigner(); return new ethers_1.ethers.Contract(address, abi, signer); } return new ethers_1.ethers.Contract(address, abi, this.provider); } /** * Get cached data or fetch it if not available * @param key Cache key * @param fetchFn Function to fetch data if not in cache * @param options Cache options */ async getWithCache(key, fetchFn, options) { const cacheKey = this.buildCacheKey(key); const cachedItem = this.cache.get(cacheKey); // Determine whether to use cache const shouldUseCache = this.shouldUseCache(cachedItem, options); if (shouldUseCache) { return cachedItem.value; } // Cache miss or expired, fetch fresh data try { const result = await fetchFn(); // Store in cache this.cache.set(cacheKey, { value: result, timestamp: Date.now(), blockNumber: options?.blockBased ? this.currentBlockNumber : undefined }); return result; } catch (error) { this.handleError(error, `Failed to fetch data for cache key: ${cacheKey}`); throw error; } } /** * Determine if cached data should be used */ shouldUseCache(cachedItem, options) { if (!cachedItem) return false; // If caching is disabled if (options?.disabled) return false; // If block-based caching is used if (options?.blockBased && cachedItem.blockNumber !== undefined) { return cachedItem.blockNumber === this.currentBlockNumber; } // Time-based caching const maxAge = options?.maxAge || this.config.cache?.defaultMaxAge || this.DEFAULT_CACHE_TIME; const now = Date.now(); const age = now - cachedItem.timestamp; return age < maxAge; } /** * Build a standardized cache key */ buildCacheKey(key) { const chainId = this.config.chainId || 'unknown'; return `${chainId}:${key}`; } /** * Invalidate a specific cache entry * @param key Cache key to invalidate */ invalidateCache(key) { const cacheKey = this.buildCacheKey(key); this.cache.delete(cacheKey); } /** * Invalidate cache entries that match a pattern * @param pattern Pattern to match cache keys against */ invalidateCacheByPattern(pattern) { const prefix = this.config.chainId ? `${this.config.chainId}:` : ''; const fullPattern = `${prefix}${pattern}`; for (const key of this.cache.keys()) { if (key.includes(fullPattern)) { this.cache.delete(key); } } } /** * Clear all cached data */ clearCache() { this.cache.clear(); } /** * Handle errors by wrapping them in AstrixSDKError * @param error Original error * @param message Error message * @param type Error type */ handleError(error, message = 'Operation failed', type = core_1.ErrorType.CONTRACT_ERROR) { if (error instanceof errors_1.AstrixSDKError) { throw error; } let errorMessage = message; let code = undefined; // Initialize code explicitly // Try to extract code and reason from ethers error if (typeof error === 'object' && error !== null) { const ethersError = error; if (ethersError.code) { code = ethersError.code; } else { // Optionally set a default SDK error code string if needed, otherwise leave undefined code = 'SDK_ERROR_FALLBACK'; // Example fallback code string } if (typeof ethersError.reason === 'string') { errorMessage += `: ${ethersError.reason}`; } else if (typeof ethersError.message === 'string') { errorMessage += `: ${ethersError.message}`; } } else { code = 'SDK_ERROR_FALLBACK'; // Fallback code string for non-object errors } throw new errors_1.AstrixSDKError(type, errorMessage, code, error); // Pass the extracted/fallback code } /** * Log a message if verbose mode is enabled * @param message Message to log * @param data Optional data to log */ log(_message, _data) { // Logging is currently disabled within the SDK itself. // Consuming applications can implement their own logging if needed. // if (this.config.verbose) { // console.log(`[Astrix SDK] ${message}`); // if (data !== undefined) { // console.log(data); // } // } } /** * Dispose of resources and subscriptions */ dispose() { this.cleanupBlockSubscription(); this.clearCache(); } } exports.BaseModule = BaseModule;