@pioneer-platform/pioneer-cache 1.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/.turbo/turbo-build.log +2 -0
- package/README.md +451 -0
- package/dist/core/base-cache.d.ts +75 -0
- package/dist/core/base-cache.js +493 -0
- package/dist/core/cache-manager.d.ts +62 -0
- package/dist/core/cache-manager.js +238 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +25 -0
- package/dist/stores/balance-cache.d.ts +47 -0
- package/dist/stores/balance-cache.js +158 -0
- package/dist/stores/price-cache.d.ts +39 -0
- package/dist/stores/price-cache.js +179 -0
- package/dist/stores/transaction-cache.d.ts +42 -0
- package/dist/stores/transaction-cache.js +148 -0
- package/dist/types/index.d.ts +98 -0
- package/dist/types/index.js +5 -0
- package/dist/workers/refresh-worker.d.ts +57 -0
- package/dist/workers/refresh-worker.js +212 -0
- package/package.json +31 -0
- package/src/core/base-cache.ts +595 -0
- package/src/core/cache-manager.ts +293 -0
- package/src/index.ts +36 -0
- package/src/stores/balance-cache.ts +196 -0
- package/src/stores/price-cache.ts +215 -0
- package/src/stores/transaction-cache.ts +172 -0
- package/src/types/index.ts +121 -0
- package/src/workers/refresh-worker.ts +267 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
CacheManager - Orchestrates all cache instances
|
|
4
|
+
|
|
5
|
+
Central coordinator for all cache types (balance, price, transaction).
|
|
6
|
+
Manages cache lifecycle, health monitoring, and worker coordination.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CacheManager = void 0;
|
|
10
|
+
const balance_cache_1 = require("../stores/balance-cache");
|
|
11
|
+
const price_cache_1 = require("../stores/price-cache");
|
|
12
|
+
const transaction_cache_1 = require("../stores/transaction-cache");
|
|
13
|
+
const refresh_worker_1 = require("../workers/refresh-worker");
|
|
14
|
+
const log = require('@pioneer-platform/loggerdog')();
|
|
15
|
+
const TAG = ' | CacheManager | ';
|
|
16
|
+
/**
|
|
17
|
+
* CacheManager - Central coordinator for all caches
|
|
18
|
+
*/
|
|
19
|
+
class CacheManager {
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.workers = [];
|
|
22
|
+
this.redis = config.redis;
|
|
23
|
+
// Initialize Balance Cache
|
|
24
|
+
if (config.enableBalanceCache !== false && config.balanceModule) {
|
|
25
|
+
this.balanceCache = new balance_cache_1.BalanceCache(this.redis, config.balanceModule);
|
|
26
|
+
log.info(TAG, '✅ Balance cache initialized');
|
|
27
|
+
}
|
|
28
|
+
// Initialize Price Cache
|
|
29
|
+
if (config.enablePriceCache !== false && config.markets) {
|
|
30
|
+
this.priceCache = new price_cache_1.PriceCache(this.redis, config.markets);
|
|
31
|
+
log.info(TAG, '✅ Price cache initialized');
|
|
32
|
+
}
|
|
33
|
+
// Initialize Transaction Cache
|
|
34
|
+
if (config.enableTransactionCache !== false) {
|
|
35
|
+
this.transactionCache = new transaction_cache_1.TransactionCache(this.redis);
|
|
36
|
+
log.info(TAG, '✅ Transaction cache initialized');
|
|
37
|
+
}
|
|
38
|
+
// Auto-start workers if requested
|
|
39
|
+
if (config.startWorkers) {
|
|
40
|
+
setImmediate(() => {
|
|
41
|
+
this.startWorkers();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
log.info(TAG, '✅ CacheManager initialized');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start background refresh workers for all caches
|
|
48
|
+
*/
|
|
49
|
+
async startWorkers() {
|
|
50
|
+
const tag = TAG + 'startWorkers | ';
|
|
51
|
+
try {
|
|
52
|
+
log.info(tag, 'Starting refresh workers...');
|
|
53
|
+
// Create cache registry for workers
|
|
54
|
+
const cacheRegistry = new Map();
|
|
55
|
+
if (this.balanceCache) {
|
|
56
|
+
cacheRegistry.set('balance', this.balanceCache);
|
|
57
|
+
}
|
|
58
|
+
if (this.priceCache) {
|
|
59
|
+
cacheRegistry.set('price', this.priceCache);
|
|
60
|
+
}
|
|
61
|
+
// Start unified worker if we have any caches with queues
|
|
62
|
+
if (cacheRegistry.size > 0) {
|
|
63
|
+
const worker = await (0, refresh_worker_1.startUnifiedWorker)(this.redis, cacheRegistry, 'cache-refresh', // Unified queue name
|
|
64
|
+
{
|
|
65
|
+
maxRetries: 3,
|
|
66
|
+
retryDelay: 5000,
|
|
67
|
+
pollInterval: 100
|
|
68
|
+
});
|
|
69
|
+
this.workers.push(worker);
|
|
70
|
+
log.info(tag, '✅ Refresh worker started for all caches');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
log.error(tag, '❌ Failed to start workers:', error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Stop all workers gracefully
|
|
80
|
+
*/
|
|
81
|
+
async stopWorkers() {
|
|
82
|
+
const tag = TAG + 'stopWorkers | ';
|
|
83
|
+
try {
|
|
84
|
+
log.info(tag, 'Stopping all workers...');
|
|
85
|
+
for (const worker of this.workers) {
|
|
86
|
+
await worker.stop();
|
|
87
|
+
}
|
|
88
|
+
this.workers = [];
|
|
89
|
+
log.info(tag, '✅ All workers stopped');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
log.error(tag, 'Error stopping workers:', error);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get aggregate health status for all caches
|
|
98
|
+
* @param forceRefresh - Force refresh stats (bypasses 30s cache)
|
|
99
|
+
*/
|
|
100
|
+
async getHealth(forceRefresh = false) {
|
|
101
|
+
const tag = TAG + 'getHealth | ';
|
|
102
|
+
try {
|
|
103
|
+
const checks = {};
|
|
104
|
+
const issues = [];
|
|
105
|
+
const warnings = [];
|
|
106
|
+
// Check balance cache
|
|
107
|
+
if (this.balanceCache) {
|
|
108
|
+
const balanceHealth = await this.balanceCache.getHealth(forceRefresh);
|
|
109
|
+
checks.balance = balanceHealth;
|
|
110
|
+
if (balanceHealth.status === 'unhealthy') {
|
|
111
|
+
issues.push(...balanceHealth.issues.map(i => `Balance: ${i}`));
|
|
112
|
+
}
|
|
113
|
+
else if (balanceHealth.status === 'degraded') {
|
|
114
|
+
warnings.push(...balanceHealth.warnings.map(w => `Balance: ${w}`));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Check price cache
|
|
118
|
+
if (this.priceCache) {
|
|
119
|
+
const priceHealth = await this.priceCache.getHealth(forceRefresh);
|
|
120
|
+
checks.price = priceHealth;
|
|
121
|
+
if (priceHealth.status === 'unhealthy') {
|
|
122
|
+
issues.push(...priceHealth.issues.map(i => `Price: ${i}`));
|
|
123
|
+
}
|
|
124
|
+
else if (priceHealth.status === 'degraded') {
|
|
125
|
+
warnings.push(...priceHealth.warnings.map(w => `Price: ${w}`));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Check transaction cache (simple stats check)
|
|
129
|
+
if (this.transactionCache) {
|
|
130
|
+
const txStats = await this.transactionCache.getStats();
|
|
131
|
+
checks.transaction = {
|
|
132
|
+
status: 'healthy',
|
|
133
|
+
stats: txStats
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// Check workers
|
|
137
|
+
for (const worker of this.workers) {
|
|
138
|
+
const workerStats = await worker.getStats();
|
|
139
|
+
checks.worker = workerStats;
|
|
140
|
+
if (!workerStats.isRunning) {
|
|
141
|
+
issues.push('Refresh worker not running');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Determine overall status
|
|
145
|
+
let status = 'healthy';
|
|
146
|
+
if (issues.length > 0) {
|
|
147
|
+
status = 'unhealthy';
|
|
148
|
+
}
|
|
149
|
+
else if (warnings.length > 0) {
|
|
150
|
+
status = 'degraded';
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
status,
|
|
154
|
+
queueInitialized: this.workers.length > 0,
|
|
155
|
+
redisConnected: true,
|
|
156
|
+
stats: {
|
|
157
|
+
totalEntries: 0,
|
|
158
|
+
staleEntries: 0,
|
|
159
|
+
freshEntries: 0,
|
|
160
|
+
stalenessRate: '0%'
|
|
161
|
+
},
|
|
162
|
+
issues,
|
|
163
|
+
warnings,
|
|
164
|
+
timestamp: Date.now(),
|
|
165
|
+
timestampISO: new Date().toISOString(),
|
|
166
|
+
checks
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
log.error(tag, 'Health check failed:', error);
|
|
171
|
+
return {
|
|
172
|
+
status: 'unhealthy',
|
|
173
|
+
queueInitialized: false,
|
|
174
|
+
redisConnected: false,
|
|
175
|
+
stats: {
|
|
176
|
+
totalEntries: 0,
|
|
177
|
+
staleEntries: 0,
|
|
178
|
+
freshEntries: 0,
|
|
179
|
+
stalenessRate: '0%'
|
|
180
|
+
},
|
|
181
|
+
issues: [`Health check error: ${error instanceof Error ? error.message : String(error)}`],
|
|
182
|
+
warnings: [],
|
|
183
|
+
timestamp: Date.now(),
|
|
184
|
+
timestampISO: new Date().toISOString()
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get all cache instances
|
|
190
|
+
*/
|
|
191
|
+
getCaches() {
|
|
192
|
+
return {
|
|
193
|
+
balance: this.balanceCache,
|
|
194
|
+
price: this.priceCache,
|
|
195
|
+
transaction: this.transactionCache
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get specific cache by name
|
|
200
|
+
*/
|
|
201
|
+
getCache(name) {
|
|
202
|
+
switch (name) {
|
|
203
|
+
case 'balance':
|
|
204
|
+
return this.balanceCache;
|
|
205
|
+
case 'price':
|
|
206
|
+
return this.priceCache;
|
|
207
|
+
case 'transaction':
|
|
208
|
+
return this.transactionCache;
|
|
209
|
+
default:
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Clear all caches (use with caution!)
|
|
215
|
+
*/
|
|
216
|
+
async clearAll() {
|
|
217
|
+
const tag = TAG + 'clearAll | ';
|
|
218
|
+
try {
|
|
219
|
+
const result = {};
|
|
220
|
+
if (this.balanceCache) {
|
|
221
|
+
result.balance = await this.balanceCache.clearAll();
|
|
222
|
+
}
|
|
223
|
+
if (this.priceCache) {
|
|
224
|
+
result.price = await this.priceCache.clearAll();
|
|
225
|
+
}
|
|
226
|
+
if (this.transactionCache) {
|
|
227
|
+
result.transaction = await this.transactionCache.clearAll();
|
|
228
|
+
}
|
|
229
|
+
log.info(tag, 'Cleared all caches:', result);
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
log.error(tag, 'Error clearing caches:', error);
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.CacheManager = CacheManager;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { BaseCache } from './core/base-cache';
|
|
2
|
+
export { CacheManager } from './core/cache-manager';
|
|
3
|
+
export { BalanceCache } from './stores/balance-cache';
|
|
4
|
+
export { PriceCache } from './stores/price-cache';
|
|
5
|
+
export { TransactionCache } from './stores/transaction-cache';
|
|
6
|
+
export { RefreshWorker, startUnifiedWorker } from './workers/refresh-worker';
|
|
7
|
+
export type { CacheConfig, CachedValue, CacheResult, RefreshJob, HealthCheckResult, CacheStats } from './types';
|
|
8
|
+
export type { BalanceData } from './stores/balance-cache';
|
|
9
|
+
export type { PriceData } from './stores/price-cache';
|
|
10
|
+
export type { CacheManagerConfig } from './core/cache-manager';
|
|
11
|
+
export type { WorkerConfig } from './workers/refresh-worker';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
@pioneer-platform/pioneer-cache
|
|
4
|
+
|
|
5
|
+
Unified caching system for Pioneer Platform.
|
|
6
|
+
Provides stale-while-revalidate caching with TTL, background refresh, and health monitoring.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.startUnifiedWorker = exports.RefreshWorker = exports.TransactionCache = exports.PriceCache = exports.BalanceCache = exports.CacheManager = exports.BaseCache = void 0;
|
|
10
|
+
// Core exports
|
|
11
|
+
var base_cache_1 = require("./core/base-cache");
|
|
12
|
+
Object.defineProperty(exports, "BaseCache", { enumerable: true, get: function () { return base_cache_1.BaseCache; } });
|
|
13
|
+
var cache_manager_1 = require("./core/cache-manager");
|
|
14
|
+
Object.defineProperty(exports, "CacheManager", { enumerable: true, get: function () { return cache_manager_1.CacheManager; } });
|
|
15
|
+
// Cache implementations
|
|
16
|
+
var balance_cache_1 = require("./stores/balance-cache");
|
|
17
|
+
Object.defineProperty(exports, "BalanceCache", { enumerable: true, get: function () { return balance_cache_1.BalanceCache; } });
|
|
18
|
+
var price_cache_1 = require("./stores/price-cache");
|
|
19
|
+
Object.defineProperty(exports, "PriceCache", { enumerable: true, get: function () { return price_cache_1.PriceCache; } });
|
|
20
|
+
var transaction_cache_1 = require("./stores/transaction-cache");
|
|
21
|
+
Object.defineProperty(exports, "TransactionCache", { enumerable: true, get: function () { return transaction_cache_1.TransactionCache; } });
|
|
22
|
+
// Worker exports
|
|
23
|
+
var refresh_worker_1 = require("./workers/refresh-worker");
|
|
24
|
+
Object.defineProperty(exports, "RefreshWorker", { enumerable: true, get: function () { return refresh_worker_1.RefreshWorker; } });
|
|
25
|
+
Object.defineProperty(exports, "startUnifiedWorker", { enumerable: true, get: function () { return refresh_worker_1.startUnifiedWorker; } });
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseCache } from '../core/base-cache';
|
|
2
|
+
import type { CacheConfig } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Balance data structure
|
|
5
|
+
*/
|
|
6
|
+
export interface BalanceData {
|
|
7
|
+
caip: string;
|
|
8
|
+
pubkey: string;
|
|
9
|
+
balance: string;
|
|
10
|
+
priceUsd?: string;
|
|
11
|
+
valueUsd?: string;
|
|
12
|
+
symbol?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
networkId?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* BalanceCache - Caches blockchain balance data
|
|
18
|
+
*/
|
|
19
|
+
export declare class BalanceCache extends BaseCache<BalanceData> {
|
|
20
|
+
private balanceModule;
|
|
21
|
+
constructor(redis: any, balanceModule: any, config?: Partial<CacheConfig>);
|
|
22
|
+
/**
|
|
23
|
+
* Build Redis key for balance data
|
|
24
|
+
* Format: balance_v2:caip:pubkey
|
|
25
|
+
*/
|
|
26
|
+
protected buildKey(params: Record<string, any>): string;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch balance from blockchain via balance module
|
|
29
|
+
*/
|
|
30
|
+
protected fetchFromSource(params: Record<string, any>): Promise<BalanceData>;
|
|
31
|
+
/**
|
|
32
|
+
* Try to get balance from legacy cache format
|
|
33
|
+
*/
|
|
34
|
+
protected getLegacyCached(params: Record<string, any>): Promise<BalanceData | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Get balance for a specific asset and pubkey
|
|
37
|
+
* Convenience method that wraps base get()
|
|
38
|
+
*/
|
|
39
|
+
getBalance(caip: string, pubkey: string, waitForFresh?: boolean): Promise<BalanceData>;
|
|
40
|
+
/**
|
|
41
|
+
* Get balances for multiple assets (batch operation)
|
|
42
|
+
*/
|
|
43
|
+
getBatchBalances(items: Array<{
|
|
44
|
+
caip: string;
|
|
45
|
+
pubkey: string;
|
|
46
|
+
}>, waitForFresh?: boolean): Promise<BalanceData[]>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
BalanceCache - Balance-specific cache implementation
|
|
4
|
+
|
|
5
|
+
Extends BaseCache with balance-specific logic.
|
|
6
|
+
All common logic is inherited from BaseCache.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.BalanceCache = void 0;
|
|
10
|
+
const base_cache_1 = require("../core/base-cache");
|
|
11
|
+
const log = require('@pioneer-platform/loggerdog')();
|
|
12
|
+
/**
|
|
13
|
+
* BalanceCache - Caches blockchain balance data
|
|
14
|
+
*/
|
|
15
|
+
class BalanceCache extends base_cache_1.BaseCache {
|
|
16
|
+
constructor(redis, balanceModule, config) {
|
|
17
|
+
const defaultConfig = {
|
|
18
|
+
name: 'balance',
|
|
19
|
+
keyPrefix: 'balance_v2:',
|
|
20
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
21
|
+
staleThreshold: 2 * 60 * 1000, // 2 minutes
|
|
22
|
+
enableTTL: true,
|
|
23
|
+
queueName: 'balance-refresh-v2',
|
|
24
|
+
enableQueue: true,
|
|
25
|
+
maxRetries: 3,
|
|
26
|
+
retryDelay: 10000,
|
|
27
|
+
blockOnMiss: true, // Wait for fresh data on first request
|
|
28
|
+
enableLegacyFallback: true,
|
|
29
|
+
defaultValue: {
|
|
30
|
+
caip: '',
|
|
31
|
+
pubkey: '',
|
|
32
|
+
balance: '0',
|
|
33
|
+
},
|
|
34
|
+
maxConcurrentJobs: 10,
|
|
35
|
+
apiTimeout: 15000,
|
|
36
|
+
logCacheHits: false,
|
|
37
|
+
logCacheMisses: true,
|
|
38
|
+
logRefreshJobs: true
|
|
39
|
+
};
|
|
40
|
+
super(redis, { ...defaultConfig, ...config });
|
|
41
|
+
this.balanceModule = balanceModule;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build Redis key for balance data
|
|
45
|
+
* Format: balance_v2:caip:pubkey
|
|
46
|
+
*/
|
|
47
|
+
buildKey(params) {
|
|
48
|
+
const { caip, pubkey } = params;
|
|
49
|
+
if (!caip || !pubkey) {
|
|
50
|
+
throw new Error('BalanceCache.buildKey: caip and pubkey required');
|
|
51
|
+
}
|
|
52
|
+
const normalizedCaip = caip.toLowerCase();
|
|
53
|
+
// Don't lowercase pubkeys - xpub/ypub/zpub are case-sensitive base58
|
|
54
|
+
const normalizedPubkey = pubkey;
|
|
55
|
+
return `${this.config.keyPrefix}${normalizedCaip}:${normalizedPubkey}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Fetch balance from blockchain via balance module
|
|
59
|
+
*/
|
|
60
|
+
async fetchFromSource(params) {
|
|
61
|
+
const tag = this.TAG + 'fetchFromSource | ';
|
|
62
|
+
try {
|
|
63
|
+
const { caip, pubkey } = params;
|
|
64
|
+
// Fetch balance using balance module
|
|
65
|
+
const asset = { caip };
|
|
66
|
+
const owner = { pubkey };
|
|
67
|
+
const balanceInfo = await this.balanceModule.getBalance(asset, owner);
|
|
68
|
+
if (!balanceInfo || !balanceInfo.balance) {
|
|
69
|
+
log.warn(tag, `No balance returned for ${caip}/${pubkey.substring(0, 10)}...`);
|
|
70
|
+
return {
|
|
71
|
+
caip,
|
|
72
|
+
pubkey,
|
|
73
|
+
balance: '0'
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Get asset metadata
|
|
77
|
+
const assetData = require('@pioneer-platform/pioneer-discovery').assetData;
|
|
78
|
+
const { caipToNetworkId } = require('@pioneer-platform/pioneer-caip');
|
|
79
|
+
const assetInfo = assetData[caip.toUpperCase()] || assetData[caip.toLowerCase()] || {};
|
|
80
|
+
const networkId = caipToNetworkId(caip);
|
|
81
|
+
return {
|
|
82
|
+
caip,
|
|
83
|
+
pubkey,
|
|
84
|
+
balance: balanceInfo.balance,
|
|
85
|
+
symbol: assetInfo.symbol,
|
|
86
|
+
name: assetInfo.name,
|
|
87
|
+
networkId
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
log.error(tag, 'Error fetching balance:', error);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Try to get balance from legacy cache format
|
|
97
|
+
*/
|
|
98
|
+
async getLegacyCached(params) {
|
|
99
|
+
const tag = this.TAG + 'getLegacyCached | ';
|
|
100
|
+
try {
|
|
101
|
+
const { caip, pubkey } = params;
|
|
102
|
+
const { caipToNetworkId } = require('@pioneer-platform/pioneer-caip');
|
|
103
|
+
const networkId = caipToNetworkId(caip);
|
|
104
|
+
// Try legacy format: cache:balance:pubkey:asset
|
|
105
|
+
const legacyKey = `cache:balance:${pubkey}:${networkId}`;
|
|
106
|
+
const legacyData = await this.redis.get(legacyKey);
|
|
107
|
+
if (legacyData) {
|
|
108
|
+
const parsed = JSON.parse(legacyData);
|
|
109
|
+
if (parsed.balance !== undefined) {
|
|
110
|
+
return {
|
|
111
|
+
caip,
|
|
112
|
+
pubkey,
|
|
113
|
+
balance: typeof parsed.balance === 'string' ? parsed.balance : String(parsed.balance)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
log.error(tag, 'Error getting legacy cached balance:', error);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get balance for a specific asset and pubkey
|
|
126
|
+
* Convenience method that wraps base get()
|
|
127
|
+
*/
|
|
128
|
+
async getBalance(caip, pubkey, waitForFresh) {
|
|
129
|
+
const result = await this.get({ caip, pubkey }, waitForFresh);
|
|
130
|
+
return result.value || this.config.defaultValue;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get balances for multiple assets (batch operation)
|
|
134
|
+
*/
|
|
135
|
+
async getBatchBalances(items, waitForFresh) {
|
|
136
|
+
const tag = this.TAG + 'getBatchBalances | ';
|
|
137
|
+
const startTime = Date.now();
|
|
138
|
+
try {
|
|
139
|
+
log.info(tag, `Batch request for ${items.length} balances`);
|
|
140
|
+
// Get all balances in parallel
|
|
141
|
+
const promises = items.map(item => this.getBalance(item.caip, item.pubkey, waitForFresh));
|
|
142
|
+
const results = await Promise.all(promises);
|
|
143
|
+
const responseTime = Date.now() - startTime;
|
|
144
|
+
log.info(tag, `Batch completed: ${results.length} balances in ${responseTime}ms (${(responseTime / results.length).toFixed(1)}ms avg)`);
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
log.error(tag, 'Error in batch balance request:', error);
|
|
149
|
+
// Return defaults for all items
|
|
150
|
+
return items.map(item => ({
|
|
151
|
+
caip: item.caip,
|
|
152
|
+
pubkey: item.pubkey,
|
|
153
|
+
balance: '0'
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.BalanceCache = BalanceCache;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseCache } from '../core/base-cache';
|
|
2
|
+
import type { CacheConfig } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Price data structure
|
|
5
|
+
*/
|
|
6
|
+
export interface PriceData {
|
|
7
|
+
caip: string;
|
|
8
|
+
price: number;
|
|
9
|
+
source?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* PriceCache - Caches USD prices for assets
|
|
13
|
+
*/
|
|
14
|
+
export declare class PriceCache extends BaseCache<PriceData> {
|
|
15
|
+
private markets;
|
|
16
|
+
constructor(redis: any, markets: any, config?: Partial<CacheConfig>);
|
|
17
|
+
/**
|
|
18
|
+
* Build Redis key for price data
|
|
19
|
+
* Format: price_v2:caip
|
|
20
|
+
*/
|
|
21
|
+
protected buildKey(params: Record<string, any>): string;
|
|
22
|
+
/**
|
|
23
|
+
* Fetch price from markets API
|
|
24
|
+
*/
|
|
25
|
+
protected fetchFromSource(params: Record<string, any>): Promise<PriceData>;
|
|
26
|
+
/**
|
|
27
|
+
* Try to get price from legacy cache formats
|
|
28
|
+
*/
|
|
29
|
+
protected getLegacyCached(params: Record<string, any>): Promise<PriceData | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Get price for a specific asset
|
|
32
|
+
* Convenience method that wraps base get()
|
|
33
|
+
*/
|
|
34
|
+
getPrice(caip: string, waitForFresh?: boolean): Promise<number>;
|
|
35
|
+
/**
|
|
36
|
+
* Get prices for multiple assets (batch operation)
|
|
37
|
+
*/
|
|
38
|
+
getBatchPrices(caips: string[], waitForFresh?: boolean): Promise<Map<string, number>>;
|
|
39
|
+
}
|