Skip to content

Commit 7d55e48

Browse files
asuzuki-jumptradingjherrera-jump
authored andcommitted
feat: shreds progression
1 parent d772f99 commit 7d55e48

File tree

17 files changed

+1781
-22
lines changed

17 files changed

+1781
-22
lines changed

package-lock.json

Lines changed: 670 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,19 @@
6868
"@eslint/js": "^9.11.1",
6969
"@tanstack/router-devtools": "^1.114.25",
7070
"@tanstack/router-vite-plugin": "^1.114.25",
71+
"@testing-library/dom": "^10.4.1",
72+
"@testing-library/react": "^16.3.0",
7173
"@types/node": "^22.7.0",
72-
"@types/react": "^18.3.9",
73-
"@types/react-dom": "^18.3.0",
74+
"@types/react": "^18.3.26",
75+
"@types/react-dom": "^18.3.7",
7476
"@vitejs/plugin-react": "^4.3.1",
7577
"eslint": "^9.11.1",
7678
"eslint-config-prettier": "^10.1.8",
7779
"eslint-plugin-react": "^7.36.1",
7880
"eslint-plugin-react-hooks": "^5.1.0-rc-04bd67a4-20240924",
7981
"eslint-plugin-react-refresh": "^0.4.12",
8082
"husky": "^9.1.7",
83+
"jsdom": "^27.1.0",
8184
"lint-staged": "^15.5.0",
8285
"optionator": "^0.9.4",
8386
"prettier": "^3.5.3",
@@ -87,9 +90,9 @@
8790
"typescript-plugin-css-modules": "^5.1.0",
8891
"vite": "^5.4.8",
8992
"vite-plugin-checker": "^0.8.0",
93+
"vite-plugin-svgr": "^4.5.0",
9094
"vite-plugin-top-level-await": "^1.5.0",
9195
"vite-plugin-wasm": "^3.4.1",
92-
"vite-plugin-svgr": "^4.5.0",
9396
"vitest": "^3.0.9"
9497
}
9598
}

src/api/entities.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ export const slotResponseSchema = z.object({
651651
});
652652

653653
export const slotSkippedHistorySchema = z.number().array();
654+
export const slotSkippedHistoryClusterSchema = z.number().array();
654655

655656
export const slotRankingsSchema = z.object({
656657
slots_largest_tips: z.number().array(),
@@ -679,11 +680,32 @@ export const slotRankingsSchema = z.object({
679680
vals_smallest_skipped: z.coerce.bigint().array(),
680681
});
681682

683+
export enum ShredEvent {
684+
shred_repair_request = 0,
685+
shred_received_turbine,
686+
shred_received_repair,
687+
shred_replayed,
688+
slot_complete,
689+
}
690+
691+
export const liveShredsSchema = z.object({
692+
reference_slot: z.number(),
693+
reference_ts: z.coerce.bigint(),
694+
slot_delta: z.number().array(),
695+
shred_idx: z.number().nullable().array(),
696+
event: z.nativeEnum(ShredEvent).array(),
697+
event_ts_delta: z.coerce.number().array(),
698+
});
699+
682700
export const slotSchema = z.discriminatedUnion("key", [
683701
slotTopicSchema.extend({
684702
key: z.literal("skipped_history"),
685703
value: slotSkippedHistorySchema,
686704
}),
705+
slotTopicSchema.extend({
706+
key: z.literal("skipped_history_cluster"),
707+
value: slotSkippedHistoryClusterSchema,
708+
}),
687709
slotTopicSchema.extend({
688710
key: z.literal("update"),
689711
value: slotResponseSchema,
@@ -696,6 +718,10 @@ export const slotSchema = z.discriminatedUnion("key", [
696718
key: z.literal("query_rankings"),
697719
value: slotRankingsSchema,
698720
}),
721+
slotTopicSchema.extend({
722+
key: z.literal("live_shreds"),
723+
value: liveShredsSchema,
724+
}),
699725
]);
700726

701727
export const blockEngineStatusSchema = z.enum([

src/api/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import type {
4747
catchUpHistorySchema,
4848
repairSlotSchema,
4949
turbineSlotSchema,
50+
liveShredsSchema,
5051
} from "./entities";
5152

5253
export type Client = z.infer<typeof clientSchema>;
@@ -150,3 +151,4 @@ export type BlockEngineUpdate = z.infer<typeof blockEngineUpdateSchema>;
150151
export type BlockEngineStatus = z.infer<typeof blockEngineStatusSchema>;
151152

152153
export type SlotRankings = z.infer<typeof slotRankingsSchema>;
154+
export type LiveShreds = z.infer<typeof liveShredsSchema>;

src/api/useSetAtomWsData.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ import {
4545
deleteSlotStatusBoundsAtom,
4646
deleteSlotResponseBoundsAtom,
4747
skipRateAtom,
48+
addSkippedClusterSlotsAtom,
49+
deleteSkippedClusterSlotAtom,
50+
deleteSkippedClusterSlotsRangeAtom,
4851
} from "../atoms";
4952
import type {
5053
EstimatedSlotDuration,
@@ -71,6 +74,11 @@ import {
7174
addTurbineSlotsAtom,
7275
addRepairSlotsAtom,
7376
} from "../features/StartupProgress/Firedancer/CatchingUp/atoms";
77+
import {
78+
addLiveShredsAtom,
79+
deleteLiveShredsAtom,
80+
} from "../features/Overview/ShredsProgression/atoms";
81+
import { xRangeMs } from "../features/Overview/ShredsProgression/const";
7482

7583
export function useSetAtomWsData() {
7684
const setVersion = useSetAtom(versionAtom);
@@ -158,9 +166,18 @@ export function useSetAtomWsData() {
158166

159167
const setCompletedSlot = useSetAtom(completedSlotAtom);
160168

169+
const addSkippedClusterSlots = useSetAtom(addSkippedClusterSlotsAtom);
170+
const deleteSkippedClusterSlot = useSetAtom(deleteSkippedClusterSlotAtom);
171+
161172
const handleSlotUpdate = (value: SlotResponse) => {
162173
setSlotStatus(value.publish.slot, value.publish.level);
163174

175+
if (value.publish.skipped) {
176+
addSkippedClusterSlots([value.publish.slot]);
177+
} else {
178+
deleteSkippedClusterSlot(value.publish.slot);
179+
}
180+
164181
if (value.publish.mine) {
165182
if (value.publish.skipped) {
166183
setSkippedSlots((prev) =>
@@ -193,6 +210,8 @@ export function useSetAtomWsData() {
193210
addRepairSlots([slot]);
194211
};
195212

213+
const addLiveShreds = useSetAtom(addLiveShredsAtom);
214+
196215
useServerMessages((msg) => {
197216
try {
198217
const { topic } = topicSchema.parse(msg);
@@ -329,6 +348,10 @@ export function useSetAtomWsData() {
329348
setSkippedSlots(value.sort());
330349
break;
331350
}
351+
case "skipped_history_cluster": {
352+
addSkippedClusterSlots(value);
353+
break;
354+
}
332355
case "update":
333356
case "query": {
334357
if (value) {
@@ -341,6 +364,10 @@ export function useSetAtomWsData() {
341364
setSlotRankings(value);
342365
break;
343366
}
367+
case "live_shreds": {
368+
addLiveShreds(value);
369+
break;
370+
}
344371
}
345372
} else if (topic === "block_engine") {
346373
const { key, value } = blockEngineSchema.parse(msg);
@@ -375,6 +402,9 @@ export function useSetAtomWsData() {
375402

376403
const deleteSlotStatusBounds = useSetAtom(deleteSlotStatusBoundsAtom);
377404
const deleteSlotResponseBounds = useSetAtom(deleteSlotResponseBoundsAtom);
405+
const deleteSkippedClusterSlotsRange = useSetAtom(
406+
deleteSkippedClusterSlotsRangeAtom,
407+
);
378408

379409
useInterval(() => {
380410
deleteSlotStatusBounds();
@@ -386,6 +416,14 @@ export function useSetAtomWsData() {
386416
(slot) => slot >= epoch.start_slot && slot <= epoch.end_slot,
387417
);
388418
});
419+
420+
deleteSkippedClusterSlotsRange(epoch.start_slot, epoch.end_slot);
389421
}
390422
}, 5_000);
423+
424+
const deleteLiveShreds = useSetAtom(deleteLiveShredsAtom);
425+
426+
useInterval(() => {
427+
deleteLiveShreds();
428+
}, xRangeMs / 4);
391429
}

src/atoms.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,3 +659,42 @@ export const statusAtom = atom<Status | null>((get) => {
659659

660660
return "Past";
661661
});
662+
663+
export const [
664+
skippedClusterSlotsAtom,
665+
addSkippedClusterSlotsAtom,
666+
deleteSkippedClusterSlotAtom,
667+
deleteSkippedClusterSlotsRangeAtom,
668+
] = (function getSkippedSlotsClusterAtom() {
669+
const _skippedClusterSlotsAtom = atomWithImmer(new Set<number>());
670+
return [
671+
atom((get) => get(_skippedClusterSlotsAtom)),
672+
atom(null, (_get, set, slots: number[]) => {
673+
set(_skippedClusterSlotsAtom, (prev) => {
674+
for (const slot of slots) {
675+
prev.add(slot);
676+
}
677+
return prev;
678+
});
679+
}),
680+
atom(null, (_get, set, slot: number) => {
681+
set(_skippedClusterSlotsAtom, (prev) => {
682+
prev.delete(slot);
683+
return prev;
684+
});
685+
}),
686+
687+
atom(null, (_get, set, startSlot: number, endSlot: number) => {
688+
set(_skippedClusterSlotsAtom, (prev) => {
689+
const toKeep = new Set<number>();
690+
691+
for (const slot of prev) {
692+
if (slot < startSlot || slot > endSlot) continue;
693+
toKeep.add(slot);
694+
}
695+
696+
return toKeep;
697+
});
698+
}),
699+
];
700+
})();

src/colors.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ export const tilePrimaryStatValueColor = "#DB8F38";
136136

137137
export const nextSlotValueColor = "#CACACA";
138138

139+
// shreds
140+
export const gridTicksColor = "#858585";
141+
export const gridLineColor = "#312D42";
142+
143+
export const shredRepairRequestedColor = "#792C2C";
144+
export const shredReceivedTurbineColor = "#163454";
145+
export const shredReceivedRepairColor = "#89603E";
146+
export const shredReplayedColor = "#408E7B";
147+
export const shredSkippedColor = "#FF5353";
148+
139149
// epoch bar
140150
export const epochTextColor = "#FAFAFA";
141151
export const epochNotLiveColor = "#3CB4FF";

0 commit comments

Comments
 (0)