Skip to content
11 changes: 11 additions & 0 deletions src/adapters/keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ export class KeypairWalletAdapter implements WalletAdapter {
return parsedTx.toXDR();
}

if (
_opts?.networkPassphrase &&
tx.networkPassphrase !== undefined &&
tx.networkPassphrase !== _opts.networkPassphrase
) {
throw new Error(
`networkPassphrase mismatch: the transaction was built for "${tx.networkPassphrase}" ` +
`but opts.networkPassphrase is "${_opts.networkPassphrase}".`,
);
}

tx.sign(this.keypair);
return tx;
}
Expand Down
16 changes: 15 additions & 1 deletion src/dashboard/transaction-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,21 @@ function toIso8601(value: string): string {
function asTimestamp(value: unknown): number {
if (typeof value === 'number' && Number.isFinite(value)) {
// Indexers emit seconds for `createdAt`; normalise to milliseconds.
return value < 1e12 ? Math.trunc(value) * 1000 : Math.trunc(value);
// Use digit count to disambiguate: 10 digits ⇒ seconds, 13 digits ⇒ ms.
// This avoids the pre-2001 ms misclassification of the old < 1e12 check.
const abs = Math.abs(value);
if (abs >= 1e9 && abs < 1e10) {
// 10 digits: seconds epoch (1970–2033 range)
return Math.trunc(value) * 1000;
}
if (abs >= 1e12 && abs < 1e13) {
// 13 digits: milliseconds epoch
return Math.trunc(value);
}
// Ambiguous digit count (e.g. 11-12 digits): fall through to existing
// callers — they already handle numeric timestamps in a context-dependent
// way, and the ambiguous band is small relative to the pre-2001 breakage.
return Math.trunc(value);
}
if (typeof value === 'string' && value.trim() !== '') {
const trimmed = value.trim();
Expand Down
2 changes: 1 addition & 1 deletion src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export function dispatchEvent(
const topicName = topics[0]?.sym()?.toString() ?? '';

const actor = addressField(topics[1]);
const sequence = topics[2] ? scValToU64(topics[2]) : 0n;
const sequence = topics[2] ? scValToU64(topics[2]) : undefined;

switch (topicName) {
case TOPIC.WITHDRAWN: {
Expand Down
18 changes: 4 additions & 14 deletions src/module49.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { StreamInfo } from './types/index.js';
import { withdrawableLocal } from './utils.js';
import { withdrawableLocal, streamProgress, normalizeProgress } from './utils.js';
import { LruMemoCache } from './lru-memo-cache.js';

export interface Module49Config {
Expand Down Expand Up @@ -102,19 +102,9 @@ export class Module49 {
}

this.cacheMisses++;
this.totalProcessed++;
const withdrawable = withdrawableLocal(item.stream, nowSec);
let progress = 0;
const { startTime, endTime } = item.stream;
if (nowSec >= startTime) {
if (endTime === 0) {
progress = 0.5; // open-ended active
} else if (nowSec >= endTime) {
progress = 1.0;
} else {
progress = (nowSec - startTime) / (endTime - startTime);
}
}
const withdrawable = withdrawableLocal(item.stream, nowSec);

const progress = normalizeProgress(streamProgress(item.stream, nowSec));

const computedAt = nowSec;

Expand Down
2 changes: 1 addition & 1 deletion src/tests/events-subscribe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ describe('subscribeToStream', () => {
onClawback: (e) => { received = e; },
});
await vi.waitFor(() => expect(received).toBeDefined());
expect(received).toEqual({ sender, amount: 5_000n, sequence: 0n });
expect(received).toEqual({ sender, amount: 5_000n, sequence: undefined });
sub.unsubscribe();
});

Expand Down
12 changes: 6 additions & 6 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,12 @@ export interface GovernorConfig {

// ── Events ──────────────────────────────────────────────────────────────────

export interface WithdrawEvent { amount: bigint; recipient: string; totalWithdrawn: bigint; remaining: bigint; sequence: bigint; }
export interface CancelEvent { refundAmount: bigint; withdrawnSoFar: bigint; sender: string; sequence: bigint; }
export interface PauseEvent { pausedAt: number; withdrawable: bigint; sender: string; sequence: bigint; }
export interface ResumeEvent { resumedAt: number; sender: string; sequence: bigint; }
export interface TopUpEvent { amount: bigint; newBalance: bigint; sender: string; sequence: bigint; }
export interface ClawbackEvent { amount: bigint; sender: string; sequence: bigint; }
export interface WithdrawEvent { amount: bigint; recipient: string; totalWithdrawn: bigint; remaining: bigint; sequence?: bigint; }
export interface CancelEvent { refundAmount: bigint; withdrawnSoFar: bigint; sender: string; sequence?: bigint; }
export interface PauseEvent { pausedAt: number; withdrawable: bigint; sender: string; sequence?: bigint; }
export interface ResumeEvent { resumedAt: number; sender: string; sequence?: bigint; }
export interface TopUpEvent { amount: bigint; newBalance: bigint; sender: string; sequence?: bigint; }
export interface ClawbackEvent { amount: bigint; sender: string; sequence?: bigint; }

/** Published once when the factory deploys a new DripStream (`created` topic). */
export interface CreatedEvent {
Expand Down