From 44a2a5c7fb546ccda88fd3e97e38ffb9b966cc07 Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Tue, 19 May 2026 10:55:28 -0700 Subject: [PATCH 1/3] fix(protocol-dashboard): remove '1.0.0' fallback and make setCurrentVersion race-safe The getCurrentVersion selector returned '1.0.0' when the validator's currentVersion wasn't loaded yet. Because useCurrentVersion only dispatches fetchCurrentVersion when the selected value is undefined, the fallback string short-circuited the dispatch and the real version was never fetched. Also harden the setCurrentVersion reducer to initialize the validator slot if it doesn't exist yet, since fetchCurrentVersion may resolve before fetchServiceStakeValues populates services.validator. Co-Authored-By: Claude Opus 4.7 --- .../src/store/cache/protocol/hooks.ts | 2 +- .../src/store/cache/protocol/slice.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/protocol-dashboard/src/store/cache/protocol/hooks.ts b/packages/protocol-dashboard/src/store/cache/protocol/hooks.ts index 7d592ffeac6..eca1262649c 100644 --- a/packages/protocol-dashboard/src/store/cache/protocol/hooks.ts +++ b/packages/protocol-dashboard/src/store/cache/protocol/hooks.ts @@ -38,7 +38,7 @@ export const getCurrentVersion = ( case ServiceType.ContentNode: return state.cache.protocol.services.contentNode.currentVersion case ServiceType.Validator: - return state.cache.protocol.services.validator?.currentVersion ?? '1.0.0' + return state.cache.protocol.services.validator?.currentVersion } } diff --git a/packages/protocol-dashboard/src/store/cache/protocol/slice.ts b/packages/protocol-dashboard/src/store/cache/protocol/slice.ts index 7f00158e1eb..9a3f88246c9 100644 --- a/packages/protocol-dashboard/src/store/cache/protocol/slice.ts +++ b/packages/protocol-dashboard/src/store/cache/protocol/slice.ts @@ -82,8 +82,14 @@ const slice = createSlice({ action.payload.currentVersion break case ServiceType.Validator: - state.services.validator.currentVersion = - action.payload.currentVersion + state.services.validator = { + ...(state.services.validator ?? { + isValid: false, + minStake: new BN(0), + maxStake: new BN(0) + }), + currentVersion: action.payload.currentVersion + } break } } From 6ca12896028a2af67dfdf3f4bb12e375ebbefc61 Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Tue, 19 May 2026 11:08:57 -0700 Subject: [PATCH 2/3] fix(protocol-dashboard): gate version pill on currentVersion, not storageCommitment The version pill's loading spinner was keyed off storageCommitment == null, which usually resolves first (single HTTP call vs two RPC calls for currentVersion). When storage loaded first, the pill swapped from spinner to an empty while currentVersion was still undefined, then the version text flashed in once the contract calls resolved. Gate the pill on currentVersion's own loading state so it stays as a spinner until the value is actually available. Co-Authored-By: Claude Opus 4.7 --- .../src/components/RegisterNodeCard/RegisterNodeCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol-dashboard/src/components/RegisterNodeCard/RegisterNodeCard.tsx b/packages/protocol-dashboard/src/components/RegisterNodeCard/RegisterNodeCard.tsx index a968b281018..5017eaff4b3 100644 --- a/packages/protocol-dashboard/src/components/RegisterNodeCard/RegisterNodeCard.tsx +++ b/packages/protocol-dashboard/src/components/RegisterNodeCard/RegisterNodeCard.tsx @@ -84,7 +84,7 @@ export const RegisterNodeCard = ({ wallet }: { wallet: string }) => { - {storageCommitment == null ? ( + {currentVersion == null ? ( From 2dfe534fff87cbdff634c3b95026f00a8b3ed0db Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Tue, 19 May 2026 11:55:22 -0700 Subject: [PATCH 3/3] fix(protocol-dashboard): preserve currentVersion when setServiceTypeInfo runs setServiceTypeInfo overwrites state.services.validator wholesale with the {isValid, minStake, maxStake} payload from getServiceTypeInfo. If fetchCurrentVersion resolves first (it has fewer RPC calls), the already-stored currentVersion gets wiped, the selector returns undefined, useCurrentVersion re-dispatches, and the user sees a double load: spinner -> 1.1.0 -> spinner -> 1.1.0. Merge over the existing currentVersion when applying new service info so the version stays put once it's been fetched. Co-Authored-By: Claude Opus 4.7 --- .../src/store/cache/protocol/slice.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/protocol-dashboard/src/store/cache/protocol/slice.ts b/packages/protocol-dashboard/src/store/cache/protocol/slice.ts index 9a3f88246c9..7fb339e1430 100644 --- a/packages/protocol-dashboard/src/store/cache/protocol/slice.ts +++ b/packages/protocol-dashboard/src/store/cache/protocol/slice.ts @@ -55,9 +55,18 @@ const slice = createSlice({ state.delegator.maxDelegators = action.payload.maxDelegators }, setServiceTypeInfo: (state, action: PayloadAction) => { - state.services.discoveryProvider = action.payload.discoveryProvider - state.services.contentNode = action.payload.contentNode - state.services.validator = action.payload.validator + state.services.discoveryProvider = { + ...action.payload.discoveryProvider, + currentVersion: state.services.discoveryProvider?.currentVersion + } + state.services.contentNode = { + ...action.payload.contentNode, + currentVersion: state.services.contentNode?.currentVersion + } + state.services.validator = { + ...action.payload.validator, + currentVersion: state.services.validator?.currentVersion + } }, setEthBlockNumber: (state, action: PayloadAction) => { state.ethBlockNumber = action.payload