From 6776f4b006c1f12777f6cf784630d9862ea40baf Mon Sep 17 00:00:00 2001 From: The-Big-Danny Date: Thu, 28 May 2026 09:52:07 +0100 Subject: [PATCH 1/2] fix: eliminate invalid trailing whitespace path for Windows file systems --- src/{metrics => metrics}/index.js | 0 src/{metrics => metrics}/metricsRouter.js | 0 src/{metrics => metrics}/oracleMetrics.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/{metrics => metrics}/index.js (100%) rename src/{metrics => metrics}/metricsRouter.js (100%) rename src/{metrics => metrics}/oracleMetrics.js (100%) diff --git a/src/metrics /index.js b/src/metrics/index.js similarity index 100% rename from src/metrics /index.js rename to src/metrics/index.js diff --git a/src/metrics /metricsRouter.js b/src/metrics/metricsRouter.js similarity index 100% rename from src/metrics /metricsRouter.js rename to src/metrics/metricsRouter.js diff --git a/src/metrics /oracleMetrics.js b/src/metrics/oracleMetrics.js similarity index 100% rename from src/metrics /oracleMetrics.js rename to src/metrics/oracleMetrics.js From 08b8c5fb9214354704c9c913e74fecad56c75ae3 Mon Sep 17 00:00:00 2001 From: The-Big-Danny Date: Thu, 28 May 2026 10:13:32 +0100 Subject: [PATCH 2/2] feat(#282): add bracket-free StellarExpert testnet link telemetry sink --- src/services/stellarService.ts | 29 +++++++++----- test/stellarExpertTelemetry.jest.test.ts | 50 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 test/stellarExpertTelemetry.jest.test.ts diff --git a/src/services/stellarService.ts b/src/services/stellarService.ts index b48004e7..1423b949 100644 --- a/src/services/stellarService.ts +++ b/src/services/stellarService.ts @@ -10,6 +10,7 @@ import { xdr, Account, } from "@stellar/stellar-sdk"; +import logger from "../utils/logger"; import stellarProvider from "../lib/stellarProvider"; import { sequenceManager } from "./sequence-manager"; import { assertSigningAllowed } from "../state/appState"; @@ -81,7 +82,7 @@ export class StellarService { baseFee, ); - console.info(`✅ Price update for ${currency} confirmed. Hash: ${result.hash}`); + logger.info(`✅ Price update for ${currency} confirmed. Hash: ${result.hash}`); return result.hash; } @@ -123,7 +124,7 @@ export class StellarService { ); const currencies = updates.map((u) => u.currency).join(", "); - console.info(`✅ Batched price update for [${currencies}] confirmed. Hash: ${result.hash}`); + logger.info(`✅ Batched price update for [${currencies}] confirmed. Hash: ${result.hash}`); return result.hash; } @@ -161,7 +162,7 @@ export class StellarService { baseFee, ); - console.info(`✅ Multi-signed price update for ${currency} confirmed. Hash: ${result.hash}`); + logger.info(`✅ Multi-signed price update for ${currency} confirmed. Hash: ${result.hash}`); return result.hash; } @@ -207,12 +208,17 @@ export class StellarService { }) ); - return await this.server.submitTransaction(transaction); + const result = await this.server.submitTransaction(transaction); + + // Telemetry Sink: Clean point-and-click StellarExpert URL for Testnet + logger.info(`[StellarService] Transaction Broadcast Successful. View on StellarExpert: https://testnet.stellarexpert.org/tx/${result.hash}`); + + return result; } catch (error: any) { const resultCode = error.response?.data?.extras?.result_codes?.transaction; if (resultCode === "tx_bad_seq") { - console.warn("⚠️ SequenceManager: tx_bad_seq detected. Invalidating sequence and retrying..."); + logger.warn("⚠️ SequenceManager: tx_bad_seq detected. Invalidating sequence and retrying..."); sequenceManager.invalidate(await this.getPublicKey()); } @@ -220,7 +226,7 @@ export class StellarService { stellarProvider.reportFailure(error); if (this.isStuckError(error) && attempt <= maxRetries) { - console.warn(`⚠️ Transaction stuck or fee too low (Attempt ${attempt}). Bumping fee and retrying...`); + logger.warn(`⚠️ Transaction stuck or fee too low (Attempt ${attempt}). Bumping fee and retrying...`); await new Promise((resolve) => setTimeout(resolve, this.RETRY_DELAY_MS)); continue; } @@ -288,16 +294,21 @@ export class StellarService { transaction.signatures.push(decoratedSignature); } catch (error) { - console.error(`[StellarService] Failed to add signature for ${sig.signerPublicKey}:`, error); + logger.error(`[StellarService] Failed to add signature for ${sig.signerPublicKey}:`, error); } } - return await this.server.submitTransaction(transaction); + const result = await this.server.submitTransaction(transaction); + + // Telemetry Sink: Clean point-and-click StellarExpert URL for Testnet + logger.info(`[StellarService] Transaction Broadcast Successful. View on StellarExpert: https://testnet.stellarexpert.org/tx/${result.hash}`); + + return result; } catch (error: any) { const resultCode = error.response?.data?.extras?.result_codes?.transaction; if (resultCode === "tx_bad_seq") { - console.warn("⚠️ SequenceManager: tx_bad_seq detected in multi-sig. Invalidating sequence..."); + logger.warn("⚠️ SequenceManager: tx_bad_seq detected in multi-sig. Invalidating sequence..."); sequenceManager.invalidate(await this.getPublicKey()); } diff --git a/test/stellarExpertTelemetry.jest.test.ts b/test/stellarExpertTelemetry.jest.test.ts new file mode 100644 index 00000000..f24a420d --- /dev/null +++ b/test/stellarExpertTelemetry.jest.test.ts @@ -0,0 +1,50 @@ +import { StellarService } from '../src/services/stellarService'; +import { TransactionBuilder, Networks, Keypair } from '@stellar/stellar-sdk'; +import logger from '../src/utils/logger'; +import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals'; + +// Mock the native logger module +jest.mock('../src/utils/logger', () => ({ + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), +})); + +describe('StellarService Telemetry', () => { + let stellarService: StellarService; + const mockHash = 'abc123transactionhash'; + + beforeEach(() => { + stellarService = new StellarService(); + + // Mock the horizon server response + (stellarService as any).server = { + submitTransaction: jest.fn().mockResolvedValue({ + hash: mockHash, + successful: true + }) + }; + + // Mock signer to avoid real cryptography in test + jest.spyOn(stellarService as any, 'getPublicKey').mockResolvedValue(Keypair.random().publicKey()); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should log a clean StellarExpert URL at INFO level upon successful broadcast', async () => { + const mockSource = Keypair.random(); + const tx = new TransactionBuilder(mockSource, { fee: '100', networkPassphrase: Networks.TESTNET }) + .addOperation({} as any) + .setTimeout(30) + .build(); + + await stellarService.submitTransactionWithRetries(() => tx, 0, 100); + + const expectedUrl = `https://testnet.stellarexpert.org/tx/${mockHash}`; + expect(logger.info).toHaveBeenCalledWith( + expect.stringContaining(`[StellarService] Transaction Broadcast Successful. View on StellarExpert: ${expectedUrl}`) + ); + }); +}); \ No newline at end of file