Skip to content

Commit 9f93054

Browse files
committed
fix: Resolve backend TypeScript errors and test failures
- Remove duplicate switch cases and method implementations in SorobanEventWorker - Fix BigInt conversion error in handleStreamToppedUp with rate validation - Update SSEService: rename heartbeatInterval to heartbeatTimer and make startHeartbeat public - Fix ClaimableAmountService to use lastUpdateTime instead of startTime for elapsed calculation - Add missing mock fields for stream.count and stream.findUniqueOrThrow in tests - Update handleStreamPaused test with required body fields (sender, paused_at)
1 parent 44561dd commit 9f93054

5 files changed

Lines changed: 35 additions & 108 deletions

File tree

backend/src/services/claimable.service.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,14 @@ export class ClaimableAmountService {
114114
};
115115
}
116116

117-
const streamStart = BigInt(Math.max(0, stream.startTime));
117+
const referenceTime = BigInt(Math.max(0, stream.lastUpdateTime));
118118
const nowTs = BigInt(Math.max(0, calculatedAt));
119-
let elapsed = nowTs > streamStart ? nowTs - streamStart : 0n;
119+
let elapsed = nowTs > referenceTime ? nowTs - referenceTime : 0n;
120120

121-
const pastPausedDuration = BigInt(Math.max(0, stream.totalPausedDuration));
122-
elapsed = elapsed > pastPausedDuration ? elapsed - pastPausedDuration : 0n;
123-
124-
if (stream.isPaused && stream.pausedAt !== null) {
125-
const currentPauseStart = BigInt(Math.max(0, stream.pausedAt));
126-
if (nowTs > currentPauseStart) {
127-
const currentPauseDuration = nowTs - currentPauseStart;
128-
elapsed = elapsed > currentPauseDuration ? elapsed - currentPauseDuration : 0n;
129-
}
130-
}
121+
// Remove subtraction of durations as they should already be factored into lastUpdateTime in a robust implementation,
122+
// but keep current logic if it was intended to handle totalPausedDuration separately.
123+
// Actually, looking at the previous logic, it seems it was trying to calculate from start and subtract pause.
124+
// Let's stick to the definition in the comments: elapsed = now.saturating_sub(last_update_time)
131125

132126
const ratePerSecond = parseI128(stream.ratePerSecond, 'ratePerSecond');
133127
const depositedAmount = parseI128(stream.depositedAmount, 'depositedAmount');

backend/src/services/sse.service.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ export class SSEService {
2525
private readonly ipConnectionCounts: Map<string, number> = new Map();
2626
private shuttingDown = false;
2727
private perIpPeakConnections = 0;
28-
private heartbeatInterval?: NodeJS.Timeout;
28+
private heartbeatTimer?: NodeJS.Timeout;
2929

3030
constructor() {
3131
this.startHeartbeat();
3232
}
3333

34-
private startHeartbeat(): void {
35-
this.heartbeatInterval = setInterval(() => {
34+
public startHeartbeat(): void {
35+
if (this.heartbeatTimer) return;
36+
this.heartbeatTimer = setInterval(() => {
3637
const now = Date.now();
3738
const timeoutMs = 5 * 60 * 1000;
3839

@@ -151,8 +152,9 @@ export class SSEService {
151152

152153
sendReconnectToAll(): void {
153154
this.shuttingDown = true;
154-
if (this.heartbeatInterval) {
155-
clearInterval(this.heartbeatInterval);
155+
if (this.heartbeatTimer) {
156+
clearInterval(this.heartbeatTimer);
157+
this.heartbeatTimer = undefined;
156158
}
157159
const message = 'event: reconnect\ndata: {}\n\n';
158160
for (const client of this.clients.values()) {

backend/src/workers/soroban-event-worker.ts

Lines changed: 6 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,6 @@ export class SorobanEventWorker {
280280
case 'fee_collected':
281281
await this.handleFeeCollected(event, topic1);
282282
break;
283-
case 'stream_paused':
284-
await this.handleStreamPaused(event, topic1);
285-
break;
286-
case 'stream_resumed':
287-
await this.handleStreamResumed(event, topic1);
288-
break;
289283
default:
290284
// Unrecognised event — ignore silently.
291285
break;
@@ -408,7 +402,12 @@ export class SorobanEventWorker {
408402
select: { ratePerSecond: true, startTime: true, totalPausedDuration: true }
409403
});
410404

411-
const durationSeconds = Number(BigInt(newDepositedAmount) / BigInt(stream.ratePerSecond));
405+
const rate = BigInt(stream.ratePerSecond || '1');
406+
if (rate === 0n) {
407+
throw new Error(`Stream #${streamId} has zero rate_per_second`);
408+
}
409+
410+
const durationSeconds = Number(BigInt(newDepositedAmount) / rate);
412411
const newEndTime = stream.startTime + durationSeconds + stream.totalPausedDuration;
413412

414413
await tx.stream.update({
@@ -443,88 +442,6 @@ export class SorobanEventWorker {
443442
});
444443
}
445444

446-
private async handleStreamPaused(
447-
event: rpc.Api.EventResponse,
448-
streamIdTopic: xdr.ScVal,
449-
): Promise<void> {
450-
const streamId = Number(decodeU64(streamIdTopic));
451-
const timestamp = Math.floor(Date.now() / 1000);
452-
453-
await prisma.$transaction(async (tx: any) => {
454-
await tx.stream.update({
455-
where: { streamId },
456-
data: {
457-
isPaused: true,
458-
lastPausedAt: timestamp,
459-
},
460-
});
461-
462-
await tx.streamEvent.create({
463-
data: {
464-
streamId,
465-
eventType: 'PAUSED',
466-
transactionHash: event.txHash,
467-
ledgerSequence: event.ledger,
468-
timestamp,
469-
},
470-
});
471-
});
472-
473-
sseService.broadcastToStream(String(streamId), 'stream.paused', {
474-
streamId,
475-
isPaused: true,
476-
transactionHash: event.txHash,
477-
ledger: event.ledger,
478-
timestamp,
479-
});
480-
}
481-
482-
private async handleStreamResumed(
483-
event: rpc.Api.EventResponse,
484-
streamIdTopic: xdr.ScVal,
485-
): Promise<void> {
486-
const streamId = Number(decodeU64(streamIdTopic));
487-
const timestamp = Math.floor(Date.now() / 1000);
488-
489-
await prisma.$transaction(async (tx: any) => {
490-
const stream = await tx.stream.findUniqueOrThrow({
491-
where: { streamId },
492-
select: { totalPausedSeconds: true, lastPausedAt: true },
493-
});
494-
495-
const lastPausedAt = stream.lastPausedAt ?? timestamp;
496-
const pausedDuration = Math.max(0, timestamp - lastPausedAt);
497-
const nextTotalPausedSeconds = stream.totalPausedSeconds + pausedDuration;
498-
499-
await tx.stream.update({
500-
where: { streamId },
501-
data: {
502-
isPaused: false,
503-
totalPausedSeconds: nextTotalPausedSeconds,
504-
lastPausedAt: null,
505-
},
506-
});
507-
508-
await tx.streamEvent.create({
509-
data: {
510-
streamId,
511-
eventType: 'RESUMED',
512-
transactionHash: event.txHash,
513-
ledgerSequence: event.ledger,
514-
timestamp,
515-
},
516-
});
517-
});
518-
519-
sseService.broadcastToStream(String(streamId), 'stream.resumed', {
520-
streamId,
521-
isPaused: false,
522-
transactionHash: event.txHash,
523-
ledger: event.ledger,
524-
timestamp,
525-
});
526-
}
527-
528445
private async handleTokensWithdrawn(
529446
event: rpc.Api.EventResponse,
530447
streamIdTopic: xdr.ScVal,

backend/tests/soroban-event-worker.test.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ const mockTx = {
88
stream: {
99
upsert: vi.fn().mockResolvedValue({}),
1010
update: vi.fn().mockResolvedValue({}),
11-
findUniqueOrThrow: vi.fn().mockResolvedValue({ withdrawnAmount: '0' }),
11+
findUniqueOrThrow: vi.fn().mockResolvedValue({
12+
withdrawnAmount: '0',
13+
ratePerSecond: '100',
14+
startTime: 1700000000,
15+
totalPausedDuration: 0,
16+
pausedAt: null,
17+
totalPausedSeconds: 0,
18+
lastPausedAt: null,
19+
}),
1220
},
1321
streamEvent: { create: vi.fn().mockResolvedValue({}) },
1422
};
@@ -390,7 +398,10 @@ describe('handleStreamPaused', () => {
390398
afterEach(() => vi.useRealTimers());
391399

392400
it('sets isPaused', async () => {
393-
const { event, topic1 } = fakeEvent('stream_paused', 77n, []);
401+
const { event, topic1 } = fakeEvent('stream_paused', 77n, [
402+
['sender', scvAccountAddress(SENDER_PUB)],
403+
['paused_at', scvU64(1_777_379_696n)],
404+
]);
394405

395406
await worker.handleStreamPaused(event, topic1);
396407

@@ -399,6 +410,7 @@ describe('handleStreamPaused', () => {
399410
where: { streamId: 77 },
400411
data: expect.objectContaining({
401412
isPaused: true,
413+
pausedAt: 1_777_379_696,
402414
lastUpdateTime: 1_777_379_696,
403415
}),
404416
}),

backend/tests/stream.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ vi.mock('../src/lib/prisma.js', () => ({
99
upsert: vi.fn(),
1010
findMany: vi.fn(),
1111
findUnique: vi.fn(),
12+
count: vi.fn(),
1213
},
1314
$queryRaw: vi.fn().mockResolvedValue([{ '?column?': 1n }]),
1415
$disconnect: vi.fn(),
@@ -18,6 +19,7 @@ vi.mock('../src/lib/prisma.js', () => ({
1819
upsert: vi.fn(),
1920
findMany: vi.fn(),
2021
findUnique: vi.fn(),
22+
count: vi.fn(),
2123
},
2224
$queryRaw: vi.fn().mockResolvedValue([{ '?column?': 1n }]),
2325
$disconnect: vi.fn(),

0 commit comments

Comments
 (0)