feat: blokli safe channel stake and redemptions - #737
Merged
Conversation
…tickets
First real use of the per node blokli url. Adds 2 figures the connected node
cannot report on its own:
- wxHOPR staked in the open outgoing channels of every node registered to the
safe, not just this one
- wxHOPR this node has redeemed on chain, which unlike the node's own ticket
statistics is not reset when the node database is wiped
src/blokli/ is the client module, playing the role @hoprnet/hopr-sdk plays for
the node slice. Blokli is GraphQL only, at <blokliUrl>/graphql, no auth and open
CORS. 2 things about it are easy to get wrong and are handled here:
- every root field returns a union, and error members come back with HTTP 200,
so success is decided by __typename, not by the status code
- TokenValueString is neither wei nor a number, it is a formatted decimal with
the symbol appended ("2084.003187159723402252 wxHOPR"). parseTokenValue splits
it and converts through viem parseUnits, never through Number, which would
lose precision at 18 decimals
The url is joined by appending /graphql rather than with new URL(), because the
base can carry a path and new URL would drop its last segment.
src/store/slices/blokli/ mirrors the node slice file for file, and moves the url
override out of the node slice, so all blokli state lives in one place. Its
reducers guard on nodeAddress the way the node reducers guard on apiEndpoint, so
results arriving after a node switch are dropped. Local storage key moves from
node/blokliUrl/<addr> to blokli/url/<addr>.
channelStats(safeAddress, status: OPEN) sums server side and is source side
only, so it is outgoing only by definition and needs no node enumeration.
wxHOPR: Channels OUT on the info page now shows the safe wide figure. That value
also feeds Total Staked and the sidebar, which duplicated the same calculation
verbatim, so all 3 read one memoized selector and cannot disagree. It falls back
to this node's own channels when blokli is unreachable, so nothing regresses.
The tickets page gains Redeemed value and Redemptions, replacing the commented
out row that read the DB-reset-scoped redeemedValue.
Verified against https://blokli.jura.hoprnet.link/ with safe
0x5CBEf561923e9C45BD5e5Af3Cb3a961B236CEd56 and node
0x2bD383d4ab1379ef58E22BBc3E12d2475D5C9b29: 45 open outgoing channels holding
2136.503187161128055072 wxHOPR, 560.000000014983072483 wxHOPR redeemed over 125
redemptions. Bad address, unreachable host and wrong path all surface as a typed
blokliApiError.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
selectChannelsOutBalance silently substituted the connected node's own channel
total when blokli had no data, so wxHOPR: Channels OUT could show a node-only
number under a label and tooltip that said safe-wide, with nothing on screen to
tell the 2 apart. A blokli figure that quietly becomes a different figure is
worse than no figure, so every blokli backed field now shows "-" when blokli has
no data.
With the fallback gone the selector only returned store.blokli.channelStats.data
unchanged, so it, its memoization and its fromBlokli flag are deleted and the
consumers read the slice directly, like they already do for ticketRedemption.
selectBlokliUrl stays: that combines the override with the url the node reports,
which is a config default, not a data fallback.
Total Staked now adds the safe wide channels, making it the safe's true total
stake, and shows "-" without blokli rather than falling back.
The info page's Channels section gains the 2 amounts side by side, so the node
only figure is visible again next to the safe wide one instead of being implied:
Outgoing (this node) from the node api, already summed into
balances.data.channels by the getChannels reducer
Outgoing (safe nodes) from blokli
Unredeemed wxHOPR and Outgoing (this node) stay on node data by design - blokli
indexes only on chain redemptions and has no unredeemed figure. Those are node
sourced fields, not blokli fields with a fallback.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TableExtended (and DERP) declared styled components inside the render body, creating a new component type on every render and remounting the whole subtree. First visible symptom: the Blokli URL field lost focus after every keystroke. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nges resetData drops channelStats and ticketRedemption and clears isFetching so the immediate refetch is not swallowed by the thunks' in-flight guards. urlInUse records the effective url the figures were fetched with, and the fulfilled reducers drop late responses from a previous url, mirroring the existing nodeAddress guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mjadach-iv
enabled auto-merge (squash)
August 17, 2026 13:00
|
Important Review available on request
Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔎 Trivy Security Report
|
mjadach-iv
disabled auto-merge
August 17, 2026 13:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.