• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-client-gateway / 20091945613

10 Dec 2025 08:19AM UTC coverage: 89.03% (-1.2%) from 90.212%
20091945613

push

github

web-flow
refactor(cache): change Zerion cache keys to be per address only (#2839)

* refactor(cache): change Zerion cache keys to be per address only

Remove chainId from Zerion cache keys for balances, collectibles, and positions.
This changes the caching strategy from per-chain-per-address to per-address only,
allowing data to be shared across chains for the same address.

Cache key changes:
- Before: {chainId}_zerion_balances_{safeAddress}
- After: zerion_balances_{safeAddress}

Similar changes applied to positions and collectibles cache keys.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Prettier

* refactor: remove unused chainId parameter from clear methods

Remove chainId parameter from clearBalances and clearCollectibles methods
in IBalancesApi interface and implementations. After the refactoring to
per-address cache keys, chainId is no longer needed by the API methods:

- ZerionBalancesApi uses address-only cache keys
- SafeBalancesApi uses its instance chainId variable

The repositories still accept chainId (needed to select the correct API)
but no longer pass it to the clear methods.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix tests: Separate unit tests from integration/e2e tests

- Renamed tests requiring real infrastructure (.spec.ts -> .integration.spec.ts or .e2e-spec.ts)
- Updated package.json Jest config to exclude integration and e2e tests from default 'yarn test' run
- Tests requiring Redis, Postgres, or full app bootstrap are now properly categorized
- All 308 unit test suites now pass without requiring external services

Renamed files:
- Redis cache service test -> integration test
- Postgres database module tests (v1 & v2) -> integration tests
- Postgres database service test -> integration test
- Auth decorator test -> e2e test
- Spaces-related controller tests -> e2e tests
- ... (continued)

2835 of 3551 branches covered (79.84%)

Branch coverage included in aggregate %.

4 of 8 new or added lines in 5 files covered. (50.0%)

199 existing lines in 34 files now uncovered.

13616 of 14927 relevant lines covered (91.22%)

564.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.44
/src/datasources/cache/cache.router.ts
1
import crypto from 'crypto';
200✔
2
import { CacheDir } from '@/datasources/cache/entities/cache-dir.entity';
200✔
3
import type { Address, Hash } from 'viem';
4
import type { TransactionInfo } from '@/modules/transactions/routes/entities/transaction-info.entity';
5

6
export class CacheRouter {
200✔
7
  private static readonly ACCOUNT_DATA_SETTINGS_KEY = 'account_data_settings';
200✔
8
  private static readonly ACCOUNT_DATA_TYPES_KEY = 'account_data_types';
200✔
9
  private static readonly ACCOUNT_KEY = 'account';
200✔
10
  private static readonly ALL_TRANSACTIONS_KEY = 'all_transactions';
200✔
11
  private static readonly AUTH_NONCE_KEY = 'auth_nonce';
200✔
12
  private static readonly BACKBONE_KEY = 'backbone';
200✔
13
  private static readonly BRIDGE_CHAINS_KEY = 'bridge_chains';
200✔
14
  private static readonly CHAIN_KEY = 'chain';
200✔
15
  private static readonly CHAINS_KEY = 'chains';
200✔
16
  private static readonly CONTRACTS_KEY = 'contracts';
200✔
17
  private static readonly COUNTERFACTUAL_SAFE_KEY = 'counterfactual_safe';
200✔
18
  private static readonly COUNTERFACTUAL_SAFES_KEY = 'counterfactual_safes';
200✔
19
  private static readonly CREATION_TRANSACTION_KEY = 'creation_transaction';
200✔
20
  private static readonly DECODED_DATA_KEY = 'decoded_data';
200✔
21
  private static readonly DELEGATES_KEY = 'delegates';
200✔
22
  private static readonly FIREBASE_OAUTH2_TOKEN_KEY = 'firebase_oauth2_token';
200✔
23
  private static readonly INCOMING_TRANSFERS_KEY = 'incoming_transfers';
200✔
24
  private static readonly INDEXING_KEY = 'indexing';
200✔
25
  private static readonly MESSAGE_KEY = 'message';
200✔
26
  private static readonly MESSAGES_KEY = 'messages';
200✔
27
  private static readonly MODULE_TRANSACTION_KEY = 'module_transaction';
200✔
28
  private static readonly MODULE_TRANSACTIONS_KEY = 'module_transactions';
200✔
29
  private static readonly MULTISIG_TRANSACTION_KEY = 'multisig_transaction';
200✔
30
  private static readonly MULTISIG_TRANSACTIONS_KEY = 'multisig_transactions';
200✔
31
  private static readonly NATIVE_COIN_PRICE_KEY = 'native_coin_price';
200✔
32
  private static readonly OWNERS_SAFE_KEY = 'owner_safes';
200✔
33
  private static readonly RATE_LIMIT_KEY = 'rate_limit';
200✔
34
  private static readonly RELAY_KEY = 'relay';
200✔
35
  private static readonly RPC_REQUESTS_KEY = 'rpc_requests';
200✔
36
  private static readonly SAFE_APPS_KEY = 'safe_apps';
200✔
37
  private static readonly SAFE_BALANCES_KEY = 'safe_balances';
200✔
38
  private static readonly SAFE_COLLECTIBLES_KEY = 'safe_collectibles';
200✔
39
  private static readonly SAFE_EXISTS_KEY = 'safe_exists';
200✔
40
  private static readonly SAFE_FIAT_CODES_KEY = 'safe_fiat_codes';
200✔
41
  private static readonly SAFE_KEY = 'safe';
200✔
42
  private static readonly SINGLETONS_KEY = 'singletons';
200✔
43
  private static readonly STAKING_DEDICATED_STAKING_STATS_KEY =
44
    'staking_dedicated_staking_stats';
200✔
45
  private static readonly STAKING_DEFI_VAULT_STATS_KEY =
46
    'staking_defi_vault_stats';
200✔
47
  private static readonly STAKING_DEFI_VAULT_STAKES_KEY =
48
    'staking_defi_vault_stakes';
200✔
49
  private static readonly STAKING_DEFI_MORPHO_EXTRA_REWARDS_KEY =
50
    'staking_defi_morpho_extra_rewards';
200✔
51
  private static readonly STAKING_DEPLOYMENTS_KEY = 'staking_deployments';
200✔
52
  private static readonly STAKING_REWARDS_FEE_KEY = 'staking_rewards_fee';
200✔
53
  private static readonly STAKING_NETWORK_STATS_KEY = 'staking_network_stats';
200✔
54
  private static readonly STAKING_POOLED_STAKING_STATS_KEY =
55
    'staking_pooled_staking_stats';
200✔
56
  private static readonly STAKING_STAKES_KEY = 'staking_stakes';
200✔
57
  private static readonly STAKING_TRANSACTION_STATUS_KEY =
58
    'staking_transaction_status';
200✔
59
  private static readonly TARGETED_MESSAGING_OUTREACHES =
60
    'targeted_messaging_outreaches';
200✔
61
  private static readonly TARGETED_MESSAGING_OUTREACH_FILE_PROCESSOR_LOCK =
62
    'targeted_messaging_outreach_file_processor_lock';
200✔
63
  private static readonly TARGETED_MESSAGING_SUBMISSION_KEY =
64
    'targeted_messaging_submission';
200✔
65
  private static readonly TARGETED_MESSAGING_TARGETED_SAFE_KEY =
66
    'targeted_messaging_targeted_safe';
200✔
67
  private static readonly TOKEN_KEY = 'token';
200✔
68
  private static readonly TOKEN_PRICE_KEY = 'token_price';
200✔
69
  private static readonly TOKENS_KEY = 'tokens';
200✔
70
  private static readonly TRANSFER_KEY = 'transfer';
200✔
71
  private static readonly TRANSFERS_KEY = 'transfers';
200✔
72
  private static readonly TRUSTED_FOR_DELEGATE_CALL_CONTRACTS_KEY =
73
    'trusted_contracts';
200✔
74
  private static readonly UNSUPPORTED_CHAIN_EVENT = 'unsupported_chain_event';
200✔
75
  private static readonly ZERION_BALANCES_KEY = 'zerion_balances';
200✔
76
  private static readonly ZERION_COLLECTIBLES_KEY = 'zerion_collectibles';
200✔
77
  private static readonly ZERION_POSITIONS_KEY = 'zerion_positions';
200✔
78
  private static readonly ZERION_CHAINS_KEY = 'zerion_chains';
200✔
79
  private static readonly PORTFOLIO_KEY = 'portfolio';
200✔
80
  private static readonly ORM_QUERY_CACHE_KEY = 'orm_query_cache';
200✔
81
  private static readonly TRANSACTIONS_EXPORT_KEY = 'transactions_export';
200✔
82
  private static readonly CONTRACT_ANALYSIS_KEY = 'contract_analysis';
200✔
83
  private static readonly RECIPIENT_ANALYSIS_KEY = 'recipient_analysis';
200✔
84

85
  static getAuthNonceCacheKey(nonce: string): string {
86
    return `${CacheRouter.AUTH_NONCE_KEY}_${nonce}`;
56✔
87
  }
88

89
  static getAuthNonceCacheDir(nonce: string): CacheDir {
90
    return new CacheDir(CacheRouter.getAuthNonceCacheKey(nonce), '');
56✔
91
  }
92

93
  static getBridgeChainsCacheDir(): CacheDir {
94
    return new CacheDir(CacheRouter.BRIDGE_CHAINS_KEY, '');
4✔
95
  }
96

97
  static getBalancesCacheKey(args: {
98
    chainId: string;
99
    safeAddress: Address;
100
  }): string {
101
    return `${args.chainId}_${CacheRouter.SAFE_BALANCES_KEY}_${args.safeAddress}`;
100✔
102
  }
103

104
  static getBalancesCacheDir(args: {
105
    chainId: string;
106
    safeAddress: Address;
107
    trusted?: boolean;
108
    excludeSpam?: boolean;
109
  }): CacheDir {
110
    return new CacheDir(
60✔
111
      CacheRouter.getBalancesCacheKey(args),
112
      `${args.trusted}_${args.excludeSpam}`,
113
    );
114
  }
115

116
  static getZerionBalancesCacheKey(args: { safeAddress: Address }): string {
117
    return `${CacheRouter.ZERION_BALANCES_KEY}_${args.safeAddress}`;
26✔
118
  }
119

120
  static getZerionBalancesCacheDir(args: {
121
    safeAddress: Address;
122
    fiatCode: string;
123
  }): CacheDir {
124
    return new CacheDir(
26✔
125
      CacheRouter.getZerionBalancesCacheKey(args),
126
      args.fiatCode.toUpperCase(),
127
    );
128
  }
129

130
  static getZerionCollectiblesCacheKey(args: { safeAddress: Address }): string {
131
    return `${CacheRouter.ZERION_COLLECTIBLES_KEY}_${args.safeAddress}`;
8✔
132
  }
133

134
  static getZerionCollectiblesCacheDir(args: {
135
    safeAddress: Address;
136
    limit?: number;
137
    offset?: number;
138
  }): CacheDir {
139
    return new CacheDir(
8✔
140
      CacheRouter.getZerionCollectiblesCacheKey(args),
141
      `${args.limit}_${args.offset}`,
142
    );
143
  }
144

145
  static getZerionPositionsCacheKey(args: { safeAddress: Address }): string {
NEW
146
    return `${CacheRouter.ZERION_POSITIONS_KEY}_${args.safeAddress}`;
×
147
  }
148

149
  static getZerionPositionsCacheDir(args: {
150
    safeAddress: Address;
151
    fiatCode: string;
152
    refresh?: string;
153
  }): CacheDir {
154
    return new CacheDir(
×
155
      CacheRouter.getZerionPositionsCacheKey(args),
156
      `${args.fiatCode.toUpperCase()}_${args.refresh ?? ''}`,
×
157
    );
158
  }
159

160
  static getRateLimitCacheKey(prefix: string): string {
161
    return `${prefix}_${CacheRouter.RATE_LIMIT_KEY}`;
140✔
162
  }
163

164
  static getSafeCacheDir(args: {
165
    chainId: string;
166
    safeAddress: Address;
167
  }): CacheDir {
168
    return new CacheDir(CacheRouter.getSafeCacheKey(args), '');
1,024✔
169
  }
170

171
  static getSafeCacheKey(args: {
172
    chainId: string;
173
    safeAddress: Address;
174
  }): string {
175
    return `${args.chainId}_${CacheRouter.SAFE_KEY}_${args.safeAddress}`;
1,058✔
176
  }
177

178
  static getIsSafeCacheDir(args: {
179
    chainId: string;
180
    safeAddress: Address;
181
  }): CacheDir {
182
    return new CacheDir(CacheRouter.getIsSafeCacheKey(args), '');
68✔
183
  }
184

185
  static getIsSafeCacheKey(args: {
186
    chainId: string;
187
    safeAddress: Address;
188
  }): string {
189
    return `${args.chainId}_${CacheRouter.SAFE_EXISTS_KEY}_${args.safeAddress}`;
72✔
190
  }
191

192
  static getBackboneCacheDir(chainId: string): CacheDir {
193
    return new CacheDir(`${chainId}_${CacheRouter.BACKBONE_KEY}`, '');
16✔
194
  }
195

196
  static getSingletonsCacheDir(chainId: string): CacheDir {
197
    return new CacheDir(`${chainId}_${CacheRouter.SINGLETONS_KEY}`, '');
68✔
198
  }
199

200
  static getCollectiblesCacheDir(args: {
201
    chainId: string;
202
    safeAddress: Address;
203
    limit?: number;
204
    offset?: number;
205
    trusted?: boolean;
206
    excludeSpam?: boolean;
207
  }): CacheDir {
208
    return new CacheDir(
10✔
209
      CacheRouter.getCollectiblesKey(args),
210
      `${args.limit}_${args.offset}_${args.trusted}_${args.excludeSpam}`,
211
    );
212
  }
213

214
  static getCollectiblesKey(args: {
215
    chainId: string;
216
    safeAddress: Address;
217
  }): string {
218
    return `${args.chainId}_${CacheRouter.SAFE_COLLECTIBLES_KEY}_${args.safeAddress}`;
52✔
219
  }
220

221
  static getDelegatesCacheKey(args: {
222
    chainId: string;
223
    safeAddress?: Address;
224
  }): string {
225
    return `${args.chainId}_${CacheRouter.DELEGATES_KEY}_${args.safeAddress}`;
192✔
226
  }
227

228
  static getDelegatesCacheDir(args: {
229
    chainId: string;
230
    safeAddress?: Address;
231
    delegate?: Address;
232
    delegator?: Address;
233
    label?: string;
234
    limit?: number;
235
    offset?: number;
236
  }): CacheDir {
237
    return new CacheDir(
186✔
238
      CacheRouter.getDelegatesCacheKey(args),
239
      `${args.delegate}_${args.delegator}_${args.label}_${args.limit}_${args.offset}`,
240
    );
241
  }
242

243
  static getFirebaseOAuth2TokenCacheDir(): CacheDir {
244
    return new CacheDir(CacheRouter.FIREBASE_OAUTH2_TOKEN_KEY, '');
8✔
245
  }
246

247
  static getTransferCacheDir(args: {
248
    chainId: string;
249
    transferId: string;
250
  }): CacheDir {
251
    return new CacheDir(
10✔
252
      `${args.chainId}_${CacheRouter.TRANSFER_KEY}_${args.transferId}`,
253
      '',
254
    );
255
  }
256

257
  static getTransfersCacheDir(args: {
258
    chainId: string;
259
    safeAddress: string;
260
    onlyErc20: boolean;
261
    onlyErc721: boolean;
262
    limit?: number;
263
    offset?: number;
264
    to?: string;
265
  }): CacheDir {
266
    return new CacheDir(
120✔
267
      CacheRouter.getTransfersCacheKey(args),
268
      `${args.onlyErc20}_${args.onlyErc721}_${args.limit}_${args.offset}_${args.to}`,
269
    );
270
  }
271

272
  static getTransfersCacheKey(args: {
273
    chainId: string;
274
    safeAddress: string;
275
  }): string {
276
    return `${args.chainId}_${CacheRouter.TRANSFERS_KEY}_${args.safeAddress}`;
180✔
277
  }
278

279
  static getModuleTransactionCacheDir(args: {
280
    chainId: string;
281
    moduleTransactionId: string;
282
  }): CacheDir {
283
    return new CacheDir(
12✔
284
      `${args.chainId}_${CacheRouter.MODULE_TRANSACTION_KEY}_${args.moduleTransactionId}`,
285
      '',
286
    );
287
  }
288

289
  static getModuleTransactionsCacheDir(args: {
290
    chainId: string;
291
    safeAddress: Address;
292
    to?: string;
293
    txHash?: string;
294
    module?: string;
295
    limit?: number;
296
    offset?: number;
297
  }): CacheDir {
298
    return new CacheDir(
70✔
299
      CacheRouter.getModuleTransactionsCacheKey(args),
300
      `${args.to}_${args.module}_${args.txHash}_${args.limit}_${args.offset}`,
301
    );
302
  }
303

304
  static getModuleTransactionsCacheKey(args: {
305
    chainId: string;
306
    safeAddress: Address;
307
  }): string {
308
    return `${args.chainId}_${CacheRouter.MODULE_TRANSACTIONS_KEY}_${args.safeAddress}`;
86✔
309
  }
310

311
  static getIncomingTransfersCacheDir(args: {
312
    chainId: string;
313
    safeAddress: string;
314
    executionDateGte?: string;
315
    executionDateLte?: string;
316
    to?: string;
317
    value?: string;
318
    tokenAddress?: string;
319
    txHash?: string;
320
    limit?: number;
321
    offset?: number;
322
  }): CacheDir {
323
    return new CacheDir(
64✔
324
      CacheRouter.getIncomingTransfersCacheKey(args),
325
      `${args.executionDateGte}_${args.executionDateLte}_${args.to}_${args.value}_${args.tokenAddress}_${args.txHash}_${args.limit}_${args.offset}`,
326
    );
327
  }
328

329
  static getIncomingTransfersCacheKey(args: {
330
    chainId: string;
331
    safeAddress: string;
332
  }): string {
333
    return `${args.chainId}_${CacheRouter.INCOMING_TRANSFERS_KEY}_${args.safeAddress}`;
94✔
334
  }
335

336
  static getIndexingCacheDir(chainId: string): CacheDir {
337
    return new CacheDir(`${chainId}_${CacheRouter.INDEXING_KEY}`, '');
12✔
338
  }
339

340
  static getMultisigTransactionsCacheDir(args: {
341
    chainId: string;
342
    safeAddress: string;
343
    ordering?: string;
344
    executed?: boolean;
345
    trusted?: boolean;
346
    executionDateGte?: string;
347
    executionDateLte?: string;
348
    to?: string;
349
    value?: string;
350
    nonce?: string;
351
    nonceGte?: number;
352
    limit?: number;
353
    offset?: number;
354
  }): CacheDir {
355
    return new CacheDir(
242✔
356
      CacheRouter.getMultisigTransactionsCacheKey(args),
357
      `${args.ordering}_${args.executed}_${args.trusted}_${args.executionDateGte}_${args.executionDateLte}_${args.to}_${args.value}_${args.nonce}_${args.nonceGte}_${args.limit}_${args.offset}`,
358
    );
359
  }
360

361
  static getMultisigTransactionsCacheKey(args: {
362
    chainId: string;
363
    safeAddress: string;
364
  }): string {
365
    return `${args.chainId}_${CacheRouter.MULTISIG_TRANSACTIONS_KEY}_${args.safeAddress}`;
344✔
366
  }
367

368
  static getMultisigTransactionCacheDir(args: {
369
    chainId: string;
370
    safeTransactionHash: string;
371
  }): CacheDir {
372
    return new CacheDir(CacheRouter.getMultisigTransactionCacheKey(args), '');
144✔
373
  }
374

375
  static getMultisigTransactionCacheKey(args: {
376
    chainId: string;
377
    safeTransactionHash: string;
378
  }): string {
379
    return `${args.chainId}_${CacheRouter.MULTISIG_TRANSACTION_KEY}_${args.safeTransactionHash}`;
206✔
380
  }
381

382
  static getCreationTransactionCacheDir(args: {
383
    chainId: string;
384
    safeAddress: Address;
385
  }): CacheDir {
386
    return new CacheDir(
18✔
387
      `${args.chainId}_${CacheRouter.CREATION_TRANSACTION_KEY}_${args.safeAddress}`,
388
      '',
389
    );
390
  }
391

392
  static getDecodedDataCacheKey(args: {
393
    chainId: string;
394
    data: Address;
395
    to: Address;
396
  }): string {
397
    return `${args.chainId}_${CacheRouter.DECODED_DATA_KEY}_${args.data}_${args.to}`;
524✔
398
  }
399

400
  static getDecodedDataCacheDir(args: {
401
    chainId: string;
402
    data: Address;
403
    to: Address;
404
  }): CacheDir {
405
    return new CacheDir(CacheRouter.getDecodedDataCacheKey(args), '');
524✔
406
  }
407

408
  static getContractsCacheKey(args: {
409
    chainId: string;
410
    address: Address;
411
  }): string {
412
    return `${args.chainId}_${CacheRouter.CONTRACTS_KEY}_${args.address}`;
1,314✔
413
  }
414

415
  static getContractsCacheDir(args: {
416
    chainId: string;
417
    address: Address;
418
  }): CacheDir {
419
    return new CacheDir(CacheRouter.getContractsCacheKey(args), '');
1,314✔
420
  }
421

422
  static getTrustedForDelegateCallContractsCacheKey(chainId: string): string {
423
    return `${chainId}_${CacheRouter.TRUSTED_FOR_DELEGATE_CALL_CONTRACTS_KEY}`;
14✔
424
  }
425

426
  static getTrustedForDelegateCallContractsCacheDir(args: {
427
    chainId: string;
428
    limit?: number;
429
    offset?: number;
430
  }): CacheDir {
431
    return new CacheDir(
14✔
432
      CacheRouter.getTrustedForDelegateCallContractsCacheKey(args.chainId),
433
      `${args.limit}_${args.offset}`,
434
    );
435
  }
436

437
  static getAllTransactionsCacheDir(args: {
438
    chainId: string;
439
    safeAddress: Address;
440
    ordering?: string;
441
    executed?: boolean;
442
    queued?: boolean;
443
    limit?: number;
444
    offset?: number;
445
  }): CacheDir {
446
    return new CacheDir(
78✔
447
      CacheRouter.getAllTransactionsKey(args),
448
      `${args.ordering}_${args.executed}_${args.queued}_${args.limit}_${args.offset}`,
449
    );
450
  }
451

452
  static getAllTransactionsKey(args: {
453
    chainId: string;
454
    safeAddress: Address;
455
  }): string {
456
    return `${args.chainId}_${CacheRouter.ALL_TRANSACTIONS_KEY}_${args.safeAddress}`;
152✔
457
  }
458

459
  static getTokenCacheDir(args: {
460
    chainId: string;
461
    address: string;
462
  }): CacheDir {
463
    return new CacheDir(
1,438✔
464
      `${args.chainId}_${CacheRouter.TOKEN_KEY}_${args.address}`,
465
      '',
466
    );
467
  }
468

469
  static getTokensCacheKey(chainId: string): string {
470
    return `${chainId}_${CacheRouter.TOKENS_KEY}`;
8✔
471
  }
472

473
  static getTokensCacheDir(args: {
474
    chainId: string;
475
    limit?: number;
476
    offset?: number;
477
  }): CacheDir {
478
    return new CacheDir(
8✔
479
      CacheRouter.getTokensCacheKey(args.chainId),
480
      `${args.limit}_${args.offset}`,
481
    );
482
  }
483

484
  static getSafesByOwnerCacheDir(args: {
485
    chainId: string;
486
    ownerAddress: Address;
487
  }): CacheDir {
488
    return new CacheDir(
24✔
489
      `${args.chainId}_${CacheRouter.OWNERS_SAFE_KEY}_${args.ownerAddress}`,
490
      '',
491
    );
492
  }
493

494
  static getMessageByHashCacheKey(args: {
495
    chainId: string;
496
    messageHash: string;
497
  }): string {
498
    return `${args.chainId}_${CacheRouter.MESSAGE_KEY}_${args.messageHash}`;
88✔
499
  }
500

501
  static getMessageByHashCacheDir(args: {
502
    chainId: string;
503
    messageHash: string;
504
  }): CacheDir {
505
    return new CacheDir(this.getMessageByHashCacheKey(args), '');
84✔
506
  }
507

508
  static getMessagesBySafeCacheKey(args: {
509
    chainId: string;
510
    safeAddress: Address;
511
  }): string {
512
    return `${args.chainId}_${CacheRouter.MESSAGES_KEY}_${args.safeAddress}`;
98✔
513
  }
514

515
  static getMessagesBySafeCacheDir(args: {
516
    chainId: string;
517
    safeAddress: Address;
518
    limit?: number;
519
    offset?: number;
520
  }): CacheDir {
521
    return new CacheDir(
70✔
522
      this.getMessagesBySafeCacheKey(args),
523
      `${args.limit}_${args.offset}`,
524
    );
525
  }
526

527
  static getChainsCacheKey(): string {
528
    return CacheRouter.CHAINS_KEY;
38✔
529
  }
530

531
  static getChainsCacheDir(args: {
532
    limit?: number;
533
    offset?: number;
534
  }): CacheDir {
535
    return new CacheDir(
24✔
536
      CacheRouter.getChainsCacheKey(),
537
      `${args.limit}_${args.offset}`,
538
    );
539
  }
540

541
  static getChainCacheKey(chainId: string): string {
542
    return `${chainId}_${CacheRouter.CHAIN_KEY}`;
2,376✔
543
  }
544

545
  static getChainCacheDir(chainId: string): CacheDir {
546
    return new CacheDir(CacheRouter.getChainCacheKey(chainId), '');
2,362✔
547
  }
548

549
  static getRelayKey(args: { chainId: string; address: Address }): string {
550
    return `${args.chainId}_${CacheRouter.RELAY_KEY}_${args.address}`;
918✔
551
  }
552

553
  static getRelayCacheDir(args: {
554
    chainId: string;
555
    address: Address;
556
  }): CacheDir {
557
    return new CacheDir(CacheRouter.getRelayKey(args), '');
918✔
558
  }
559

560
  static getSafeAppsKey(chainId: string): string {
561
    return `${chainId}_${CacheRouter.SAFE_APPS_KEY}`;
4✔
562
  }
563

564
  static getSafeAppsCacheDir(args: {
565
    chainId?: string;
566
    clientUrl?: string;
567
    onlyListed?: boolean;
568
    url?: string;
569
  }): CacheDir {
570
    return new CacheDir(
90✔
571
      `${args.chainId}_${CacheRouter.SAFE_APPS_KEY}`,
572
      `${args.clientUrl}_${args.onlyListed}_${args.url}`,
573
    );
574
  }
575

576
  static getNativeCoinPriceCacheDir(args: {
577
    nativeCoinId: string;
578
    fiatCode: string;
579
  }): CacheDir {
580
    return new CacheDir(
42✔
581
      `${args.nativeCoinId}_${CacheRouter.NATIVE_COIN_PRICE_KEY}_${args.fiatCode.toUpperCase()}`,
582
      '',
583
    );
584
  }
585

586
  static getTokenPriceCacheDir(args: {
587
    chainName: string;
588
    fiatCode: string;
589
    tokenAddress: string;
590
  }): CacheDir {
591
    return new CacheDir(
214✔
592
      `${args.chainName}_${CacheRouter.TOKEN_PRICE_KEY}_${args.tokenAddress}_${args.fiatCode.toUpperCase()}`,
593
      '',
594
    );
595
  }
596

597
  static getPriceFiatCodesCacheDir(): CacheDir {
598
    return new CacheDir(CacheRouter.SAFE_FIAT_CODES_KEY, '');
10✔
599
  }
600

601
  static getAccountCacheDir(address: Address): CacheDir {
602
    return new CacheDir(`${CacheRouter.ACCOUNT_KEY}_${address}`, '');
78✔
603
  }
604

605
  static getAccountDataTypesCacheDir(): CacheDir {
606
    return new CacheDir(CacheRouter.ACCOUNT_DATA_TYPES_KEY, '');
20✔
607
  }
608

609
  static getAccountDataSettingsCacheDir(address: Address): CacheDir {
610
    return new CacheDir(
22✔
611
      `${CacheRouter.ACCOUNT_DATA_SETTINGS_KEY}_${address}`,
612
      '',
613
    );
614
  }
615

616
  static getCounterfactualSafeCacheDir(
617
    chainId: string,
618
    predictedAddress: Address,
619
  ): CacheDir {
620
    return new CacheDir(
24✔
621
      `${chainId}_${CacheRouter.COUNTERFACTUAL_SAFE_KEY}_${predictedAddress}`,
622
      '',
623
    );
624
  }
625

626
  static getCounterfactualSafesCacheDir(address: Address): CacheDir {
627
    return new CacheDir(
78✔
628
      `${CacheRouter.COUNTERFACTUAL_SAFES_KEY}_${address}`,
629
      '',
630
    );
631
  }
632

633
  static getRpcRequestsKey(chainId: string): string {
634
    return `${chainId}_${CacheRouter.RPC_REQUESTS_KEY}`;
16✔
635
  }
636

637
  static getRpcRequestsCacheDir(args: {
638
    chainId: string;
639
    method: string;
640
    params: string;
641
  }): CacheDir {
642
    return new CacheDir(
12✔
643
      CacheRouter.getRpcRequestsKey(args.chainId),
644
      `${args.method}_${args.params}`,
645
    );
646
  }
647

648
  static getStakingDeploymentsCacheDir(
649
    cacheType: 'earn' | 'staking',
650
  ): CacheDir {
651
    return new CacheDir(this.STAKING_DEPLOYMENTS_KEY, cacheType);
52✔
652
  }
653

654
  static getStakingRewardsFeeCacheDir(args: {
655
    cacheType: 'earn' | 'staking';
656
    chainId: string;
657
    contract: Address;
658
  }): CacheDir {
659
    return new CacheDir(
22✔
660
      `${args.chainId}_${this.STAKING_REWARDS_FEE_KEY}_${args.contract}`,
661
      args.cacheType,
662
    );
663
  }
664

665
  static getStakingNetworkStatsCacheDir(
666
    cacheType: 'earn' | 'staking',
667
  ): CacheDir {
668
    return new CacheDir(this.STAKING_NETWORK_STATS_KEY, cacheType);
22✔
669
  }
670

671
  static getStakingDedicatedStakingStatsCacheDir(
672
    cacheType: 'earn' | 'staking',
673
  ): CacheDir {
674
    return new CacheDir(this.STAKING_DEDICATED_STAKING_STATS_KEY, cacheType);
14✔
675
  }
676

677
  static getStakingPooledStakingStatsCacheDir(args: {
678
    cacheType: 'earn' | 'staking';
679
    pool: Address;
680
  }): CacheDir {
681
    return new CacheDir(
4✔
682
      `${this.STAKING_POOLED_STAKING_STATS_KEY}_${args.pool}`,
683
      args.cacheType,
684
    );
685
  }
686

687
  static getStakingDefiVaultStatsCacheDir(args: {
688
    cacheType: 'earn' | 'staking';
689
    chainId: string;
690
    vault: Address;
691
  }): CacheDir {
692
    return new CacheDir(
6✔
693
      `${args.chainId}_${this.STAKING_DEFI_VAULT_STATS_KEY}_${args.vault}`,
694
      args.cacheType,
695
    );
696
  }
697

698
  static getStakingDefiVaultStakesCacheDir(args: {
699
    cacheType: 'earn' | 'staking';
700
    chainId: string;
701
    safeAddress: Address;
702
    vault: Address;
703
  }): CacheDir {
704
    return new CacheDir(
6✔
705
      `${args.chainId}_${this.STAKING_DEFI_VAULT_STAKES_KEY}_${args.safeAddress}_${args.vault}`,
706
      args.cacheType,
707
    );
708
  }
709

710
  static getStakingDefiMorphoExtraRewardsCacheDir(args: {
711
    cacheType: 'earn' | 'staking';
712
    chainId: string;
713
    safeAddress: Address;
714
  }): CacheDir {
715
    return new CacheDir(
4✔
716
      `${args.chainId}_${this.STAKING_DEFI_MORPHO_EXTRA_REWARDS_KEY}_${args.safeAddress}`,
717
      args.cacheType,
718
    );
719
  }
720

721
  /**
722
   * Calculated the chain/Safe-specific cache key of {@link Stake}.
723
   *
724
   * @param {string} args.chainId - Chain ID
725
   * @param {string} args.safeAddress - Safe address
726
   * @returns {string} - Cache key
727
   */
728
  static getStakingStakesCacheKey(args: {
729
    chainId: string;
730
    safeAddress: Address;
731
  }): string {
732
    return `${args.chainId}_${CacheRouter.STAKING_STAKES_KEY}_${args.safeAddress}`;
88✔
733
  }
734

735
  /**
736
   * Calculate cache directory for staking stakes.
737
   *
738
   * Note: This function hashes the validators' public keys to keep the
739
   * cache field short and deterministic. Redis and other cache systems
740
   * may experience performance degradation with long fields.
741
   *
742
   * @param {string} args.cacheType - Cache type (earn or staking)
743
   * @param {string} args.chainId - Chain ID
744
   * @param {string} args.safeAddress - Safe address
745
   * @param {string} args.validatorsPublicKeys - Array of validators public keys
746
   * @returns {@link CacheDir} - Cache directory
747
   */
748
  static getStakingStakesCacheDir(args: {
749
    cacheType: 'earn' | 'staking';
750
    chainId: string;
751
    safeAddress: Address;
752
    validatorsPublicKeys: Array<Address>;
753
  }): CacheDir {
754
    const hash = crypto.createHash('sha256');
22✔
755
    hash.update(args.validatorsPublicKeys.join('_'));
22✔
756
    return new CacheDir(
22✔
757
      CacheRouter.getStakingStakesCacheKey(args),
758
      `${args.cacheType}_${hash.digest('hex')}`,
759
    );
760
  }
761

762
  static getUnsupportedChainEventCacheKey(chainId: string): string {
763
    return `${chainId}_${this.UNSUPPORTED_CHAIN_EVENT}`;
54✔
764
  }
765

766
  static getStakingTransactionStatusCacheDir(args: {
767
    cacheType: 'earn' | 'staking';
768
    chainId: string;
769
    txHash: Hash;
770
  }): CacheDir {
771
    return new CacheDir(
8✔
772
      `${args.chainId}_${CacheRouter.STAKING_TRANSACTION_STATUS_KEY}_${args.txHash}`,
773
      args.cacheType,
774
    );
775
  }
776

777
  static getTargetedSafeCacheKey(outreachId: number): string {
778
    return `${CacheRouter.TARGETED_MESSAGING_TARGETED_SAFE_KEY}_${outreachId}`;
96✔
779
  }
780

781
  static getTargetedSafeCacheDir(args: {
782
    outreachId: number;
783
    safeAddress: Address;
784
    chainId?: string;
785
  }): CacheDir {
786
    return new CacheDir(
36✔
787
      CacheRouter.getTargetedSafeCacheKey(args.outreachId),
788
      `${args.safeAddress}_${args.chainId ?? 'null'}`,
29✔
789
    );
790
  }
791

792
  static getSubmissionCacheKey(outreachId: number): string {
793
    return `${CacheRouter.TARGETED_MESSAGING_SUBMISSION_KEY}_${outreachId}`;
28✔
794
  }
795

796
  static getSubmissionCacheDir(args: {
797
    outreachId: number;
798
    safeAddress: Address;
799
    signerAddress: Address;
800
    chainId: string | null;
801
  }): CacheDir {
802
    return new CacheDir(
16✔
803
      CacheRouter.getSubmissionCacheKey(args.outreachId),
804
      `${args.safeAddress}_${args.signerAddress}_${args.chainId ?? 'null'}`,
14✔
805
    );
806
  }
807

808
  static getOutreachesCacheDir(): CacheDir {
809
    return new CacheDir(CacheRouter.TARGETED_MESSAGING_OUTREACHES, '');
×
810
  }
811

812
  static getOutreachFileProcessorCacheKey(): string {
813
    return CacheRouter.TARGETED_MESSAGING_OUTREACH_FILE_PROCESSOR_LOCK;
58✔
814
  }
815

816
  static getOutreachFileProcessorCacheDir(): CacheDir {
817
    return new CacheDir(CacheRouter.getOutreachFileProcessorCacheKey(), '');
38✔
818
  }
819

820
  static getTransactionsExportCacheKey(args: {
821
    chainId: string;
822
    safeAddress: Address;
823
  }): string {
824
    return `${args.chainId}_${CacheRouter.TRANSACTIONS_EXPORT_KEY}_${args.safeAddress}`;
10✔
825
  }
826

827
  static getTransactionsExportCacheDir(args: {
828
    chainId: string;
829
    safeAddress: Address;
830
    executionDateGte?: string;
831
    executionDateLte?: string;
832
    limit?: number;
833
    offset?: number;
834
  }): CacheDir {
835
    return new CacheDir(
10✔
836
      CacheRouter.getTransactionsExportCacheKey(args),
837
      `${args.executionDateGte}_${args.executionDateLte}_${args.limit}_${args.offset}`,
838
    );
839
  }
840

841
  /**
842
   * Gets the in-memory cache key for the given cacheDir.
843
   */
844
  static getMemoryKey(cacheDir: CacheDir): string {
845
    return `${cacheDir.key}:${cacheDir.field}`;
306✔
846
  }
847

848
  /**
849
   * Gets Redis cache key for the ORM query cache.
850
   *
851
   * @param {string} prefix - Prefix for the cache key
852
   * @param {string} chainId - Chain ID
853
   * @param {string} safeAddress - Safe address
854
   *
855
   * @returns {string} - Cache key
856
   */
857
  static getOrnCacheKey(
858
    prefix: string,
859
    chainId: string,
860
    safeAddress: Address,
861
  ): string {
862
    return `${CacheRouter.ORM_QUERY_CACHE_KEY}:${prefix}:${chainId}:${safeAddress}`;
356✔
863
  }
864

865
  /**
866
   * Gets cache directory for contract analysis results.
867
   *
868
   * @param {string} args.chainId - Chain ID
869
   * @param {[Address, boolean][]} args.contractPairs - Array of pairs: contract address and isDelegateCall flag
870
   * @returns {CacheDir} - Cache directory
871
   */
872
  static getContractAnalysisCacheDir(args: {
873
    chainId: string;
874
    contractPairs: Array<[Address, boolean]>;
875
  }): CacheDir {
876
    const contractsHash = crypto.createHash('sha256');
16✔
877
    contractsHash.update(
16✔
878
      args.contractPairs.sort((a, b) => a[0].localeCompare(b[0])).join(','),
6✔
879
    );
880
    return new CacheDir(
16✔
881
      `${args.chainId}_${CacheRouter.CONTRACT_ANALYSIS_KEY}`,
882
      contractsHash.digest('hex'),
883
    );
884
  }
885

886
  /**
887
   * Gets cache directory for recipient analysis results.
888
   *
889
   * @param {string} args.chainId - Chain ID
890
   * @param {string} args.safeAddress - Safe address
891
   * @param {Address[]} args.recipients - Array of recipient addresses
892
   * @param {TransactionInfo} args.txInfo - The transaction info
893
   * @returns {CacheDir} - Cache directory
894
   */
895
  static getRecipientAnalysisCacheDir(args: {
896
    chainId: string;
897
    safeAddress: Address;
898
    recipients: Array<Address>;
899
    txInfo?: TransactionInfo;
900
  }): CacheDir {
901
    const hash = crypto.createHash('sha256');
34✔
902
    hash.update(args.recipients.sort().join(','));
34✔
903
    if (args.txInfo) {
34✔
904
      try {
18✔
905
        hash.update(JSON.stringify(args.txInfo));
18✔
906
      } catch {
907
        // fallback: do not update hash if serialization fails
908
      }
909
    }
910
    return new CacheDir(
34✔
911
      `${args.chainId}_${CacheRouter.RECIPIENT_ANALYSIS_KEY}_${args.safeAddress}`,
912
      hash.digest('hex'),
913
    );
914
  }
915

916
  static getPortfolioCacheKey(args: { address: Address }): string {
917
    return `${CacheRouter.PORTFOLIO_KEY}_${args.address}_zerion`;
30✔
918
  }
919

920
  static getPortfolioCacheDir(args: {
921
    address: Address;
922
    fiatCode: string;
923
    trusted?: boolean;
924
    isTestnet?: boolean;
925
  }): CacheDir {
926
    const trustedSuffix = args.trusted ? '_trusted' : '';
26✔
927
    const testnetSuffix = args.isTestnet ? '_testnet' : '';
26!
928
    return new CacheDir(
26✔
929
      CacheRouter.getPortfolioCacheKey(args),
930
      `${args.fiatCode.toUpperCase()}${trustedSuffix}${testnetSuffix}`,
931
    );
932
  }
933

934
  static getZerionChainsCacheDir(isTestnet: boolean): CacheDir {
935
    const field = isTestnet ? 'mapping_testnet' : 'mapping';
×
936
    return new CacheDir(CacheRouter.ZERION_CHAINS_KEY, field);
×
937
  }
938
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc