chore: sync main with upstream babylonlabs-io/babylon-toolkit - #5
chore: sync main with upstream babylonlabs-io/babylon-toolkit#5apple-juice wants to merge 316 commits into
Conversation
…nlabs-io#1894) * feat(vault): wire reserve available liquidity and utilization Surface the reserve-level Available liquidity and Utilization figures in the borrow flow, read live from the Aave Hub. - Read available liquidity (getAssetLiquidity) and total borrowed (getAssetTotalOwed) per reserve from the Hub in a batched, fault- isolated multicall; utilization = borrowed / (available + borrowed). Reads go through the app-side eth-contract layer, not the shared SDK. - Show Available liquidity and Utilization in the borrow metrics card and the per-asset Available column in the asset picker, each falling back to the empty placeholder while the read is loading or unavailable. - Project Available liquidity as current -> post-borrow once an amount is entered, mirroring the health-factor row. - Format large amounts compactly (K/M/B) via formatCompactTokenAmount. * fix(vault): source projection arrow from copy and round compact boundary - Use COPY.common.valueTransitionArrow for the current → projected separators in the borrow metrics card instead of an inline glyph (and align the adjacent health-factor arrow to the same source). - Round formatCompactTokenAmount to two decimals before testing the compact threshold, so a value that rounds up to 1,000 (e.g. 999.995) renders as "1K" rather than "1,000" and the boundary stays consistent. * feat(vault): cap borrow amount by available reserve liquidity The borrow Max button and validation used only the collateral-based max, so a user could enter or select more than the reserve holds — the metrics row would project to 0 while the button stayed enabled and the on-chain draw would revert. - Cap the effective max at the reserve's available liquidity when the Hub read succeeds; fall back to the collateral max while it is loading or unavailable, so a best-effort display read never blocks an otherwise-fundable borrow. - Drive the Max button, slider range, "Available" field, and validation off the capped max. - Show a distinct "exceeds available liquidity" message when liquidity is the binding constraint, rather than the generic "exceeds maximum". * fix(vault): margin the liquidity cap and clarify the Hub decimals scale - Cap borrowable liquidity slightly below the reserve's available amount so "Max" doesn't advertise a drain-to-zero figure that some pool configs revert on; the on-chain draw remains the backstop. - Document that the Hub reports liquidity and owed in underlying-token base units, so the token decimals are the correct conversion scale.
* feat(vault): redesign disconnected entry screen Replace the Protocol Cap + hero + 3-step explainer disconnected view with a self-contained entry screen: hero, a Cap / Max LTV / Loan process time stat row, Connect CTA, and a list of five feature cards. The rates card always shows live USDT/USDC/WBTC borrow APRs; Self-custodial and Trustless are expandable (single-open, collapsed bodies truncate to one line). Hide Protocol Cap / Collateral / Loans on the disconnected view via an early return in DashboardPage; OverviewSection is now connected-only. Cap is wired from useApplicationCap and APRs from useLandingBorrowAprs. Max LTV and loan process time are placeholders pending data integration (TODOs in copy.ts). * chore(vault): drop explanatory comments from entry screen Keep only the placeholder TODOs and concise file docblocks. * refactor(vault): split entry feature cards and address review feedback - Split DisconnectedFeatureCards into one component per file - Use useId() for the partial-liquidation mask to avoid duplicate DOM ids - Memoize aprStats and featureCards in DisconnectedOverview - Correct the docstring: the rates card shows live APRs statically, only the last two cards expand
…abylonlabs-io#1897) * feat(vault): show projected post-borrow rate in borrow metrics card * fix(vault): pass live deficit to rate strategy and withhold stale projection * docs(vault): note current-rate leg equals getAssetDrawnRate
* feat(vault): wire Max CF on entry page from reserve collateral factor Replace the hardcoded Max LTV placeholder on the disconnected entry page with the live collateral factor read from the Core Spoke contract via useVaultSplitParams, rendered as a percentage with an empty-value fallback while loading. Rename the stat to Max CF to match the on-chain value. Loan process time stays a placeholder. * feat(vault): promote entry-page loan process time to a fixed value There is no on-chain source for the end-to-end loan process time, so it stays a hardcoded "~3 hours". Drop the placeholder naming and TODO so the stat is no longer flagged as pending contract integration.
* fix(wallet): harden wallet links and stop ETH disconnect re-entrancy * feat(wallet-connector): add copy button and middle-truncate connected address
…abylonlabs-io#1862) * feat(vault): add subresource integrity hashes to built script assets * fix(vault): strip duplicate crossorigin and fail loud on unprotected scripts
…ylonlabs-io#1860) * fix(vault): validate ETH wallet addresses at the wallet boundary * fix(vault): return undefined from toCheckedAddress instead of throwing
… assets …" (babylonlabs-io#1925) This reverts commit 08e47f4.
…bs-io#1919) * feat(vault): redesign connected dashboard Overview section * fix(vault): suppress stale BTC price and address overview review feedback * fix(ci): keep full history in critical-path-check to fix merge-base error * feat(packages): revisions
* feat(wallet-connector): render new pill design * feat(packages): revision
…me (babylonlabs-io#1933) The footer's inner Container was capped at max-w-[1400px] to match the navbar but kept core-ui Container's default px-4 sm:px-0, dropping its horizontal inset at the sm breakpoint. Navbar and page content use PAGE_CONTENT_CLASS (px-5 at all breakpoints), so on wide viewports the footer content sat flush against the 1400px edge while everything above was inset 20px. Add [&>div]:!px-5 to realign the footer with the rest of the page chrome.
babylonlabs-io#1932) * feat(vault): in-page disabled-deposit state and oversized-amount guard Behind the DISABLE_DEPOSIT flag, replace the bare "Depositing Unavailable" CTA with an in-page disabled experience: an orange top banner, disabled Deposit buttons (header and Collateral section), and a grayed-out BTC logo empty state with explanatory copy. Also guard useOptimalSplit against implausibly large amounts so an oversized input returns the empty split result instead of tripping the ts-sdk precision assertion and crashing the deposit form. * fix(vault): close kill-switch leak and surface oversized-amount error Restore a central FeatureFlags.isDepositDisabled guard so every deposit entry point is blocked: getDepositCtaState returns a disabled CTA and handleDeposit bails before starting the flow, covering paths that bypass the disabled buttons (Activity empty-state CTA, urgent Add Collateral banner). In useOptimalSplit, return an explicit RangeError for amounts above the BTC supply cap instead of a silent zeroed result that downstream split planning cannot distinguish from a normal no-split state. * fix(vault): align disabled-deposit banner copy with staking phrasing * fix(vault): make oversized-amount guard defensive-only and tidy grayscale class Drop the unconsumed amountError plumbing from useOptimalSplit — the oversized amount is already rejected for the user by the form's balance/max validation, so the supply-cap check is a documented defensive crash-guard and its test now verifies the no-throw/empty behavior production relies on. Use twJoin for the empty-state logo grayscale toggle instead of duplicating the base class string. * chore(vault): simplify oversized-amount guard comment
* feat(wallet-connector): adds new activity page * fix(vault): correct activity expired tooltip to say Deposit expired * feat(wallet-connector): ui audit fix (babylonlabs-io#1922) * refactor(vault): rename isRefunded to isExpired, extract PENDING_DEPOSIT_TYPE Addresses PR review notes: align the activity red-dot field name with its 'Deposit expired' label, and centralize the 'Pending Deposit' sentinel.
…abylonlabs-io#1926) * feat(vault): show compact deposit summary as initial signing screen The deposit signing modal previously auto-started the flow on mount and rendered the fully-expanded step list immediately. Replace that initial state with a compact summary card (heading, explanation, the four step groups collapsed with their signature counts, single Sign CTA); the flow now starts only when the depositor clicks Sign, after which the existing live stepper renders unchanged. Counts derive from STEP_GROUPS, so the Activate vault group reads 0/3 (the real step count); the live stepper, resume flows, and deposit logic are untouched. * refactor(vault): hoist summary groups and share deposit-view width Address PR review: hoist the DepositSummaryCard group array to a module constant so it is not reallocated per render, and extract the shared max-w-[520px] into DEPOSIT_VIEW_MAX_WIDTH_CLASS used by both the summary card and the live progress view. * refactor(vault): extract reusable DepositCardShell for all deposit states Make the deposit card the shared shell for the whole progress flow, not just the pre-sign summary. DepositCardShell owns the chrome — border, heading + estimate, explanation, optional progress bar, full-bleed divider, body slot, and footer (CTA + optional footnote). DepositSummaryCard now supplies only the collapsed group rows and the Sign CTA. This readies the card for the in-flight states: the live stepper can render its expanded GroupedProgress (with per-step detail panels) as the body, pass a progress bar, and swap the footer to close-and-continue + the do-not-spend footnote — all inside the same card. No behavior change yet; the live stepper migration is follow-up work. * fix(vault): guard deposit Sign against double-start broadcast Restore the exactly-once guarantee that useRunOnce previously provided. The Sign button now starts the deposit flow at most once via a hasStartedRef guard in handleSign: executeDeposit broadcasts BTC and has no internal re-entrancy guard, so a fast double-click could otherwise start (and broadcast) the deposit twice before the started re-render unmounts the button. Adds a regression test that fires onSign twice synchronously and asserts executeDeposit runs once.
* feat(vault): show optimistic activating collateral * chore(pr): comments
…ase (babylonlabs-io#1938) * ci(vault-publish): add env scoping + correlation id for orchestrated release Add ENVIRONMENT and CORRELATION_ID workflow_dispatch inputs. ENVIRONMENT scopes the S3 publish to a single environment (the unified redeploy pipeline targets one env); empty/all and push/tag runs publish all three as before, via a dynamic matrix. CORRELATION_ID is echoed into run-name so an orchestrating workflow can locate this run. * ci(vault-publish): derive valid-env list in dispatch error from matrix source * ci(vault-publish): fail loud on a scoped prod publish continue-on-error tolerated a prod (vault-testnet) failure unconditionally; with env scoping a single ENVIRONMENT=vault-testnet dispatch would go green even if OIDC failed and nothing published. Scope it to the multi-env run only (setup.outputs.multi), so a single requested prod env fails loud. --------- Co-authored-by: toroviej0x0 <toroviej0x0@users.noreply.github.com>
* feat(core-ui): add Notification severity primitive Add a dashboard-level Notification component: left severity accent bar, icon chip, title/description, optional suggestion sub-box, optional dismiss control, and severity-aware action pills. Variants: error (with subtle red tint), warning, info, success, paused (teal), and suggestion (gold). Actions are passed as a structured NotificationAction[] and rendered as primary (accent-filled) or secondary (outlined) pills, so consumers never hand-style severity colors. Storybook stories cover every severity and slot combination. Leaves Callout untouched. Vault PositionNotificationBanner migration is deferred; this primitive unblocks the notification cards. * refactor(core-ui): address review on Notification primitive - Move the action pill into its own NotificationActionButton file to satisfy the one-component-per-file convention. - Require onClick on NotificationAction so an action pill always has a handler (no inert, focusable buttons). - Gate the title, description, and suggestion slots on presence rather than truthiness so numeric content (e.g. 0) is not dropped.
babylonlabs-io#2269) * feat(ledger-vault-signer): wire provider signPsbt/signPsbts onto signVaultPsbt * fix(ledger-vault-signer): address babylonlabs-io#2269 review * chore(ledger-sdk): trim comments * fix(ledger-vault-signer): address babylonlabs-io#2269 review round 2
…bylonlabs-io#2242) * perf(vault): cache the IRM curve read and stop per-minute polling The interest-rate-model curve is a pure function of governance-set strategy parameters (calculateInterestRate ignores its deficit argument), so per-minute polling re-read a shape that cannot move between governance actions. The curve query now caches for an hour (refetching hourly while mounted, retrying every 60s while errored, single attempt per cycle) and withholds retained data on error. The live Current marker derives entirely from the page's existing 60s reserve reads: its callout APR is read off the cached curve at the live utilization, so the dot and label share one source. Per-viewer RPC cost drops from 3 eth_calls per minute to 3 per hour. * feat(vault): read the IRM curve from the indexer endpoint The curve query now fetches the indexer's Redis-cached /api/aave/reserves/:id/irm route instead of issuing three multicalls from every browser, so total RPC load for the chart is bounded by the server's cache TTL instead of scaling with the audience. The endpoint returns 200 only with a complete curve — every degraded state is a non-200 — which is what lets the hour-long client cache treat any 200 as complete. The browser-side sampling client is deleted; hub helpers it exported become internal to aaveHub. Depends on the indexer deploying the /irm route first. * fix(vault): carry IRM curve failures as data and harden the client Review follow-ups on the curve query. A thrown queryFn error took the global QueryCache.onError path into Sentry once per 60s error cycle, and one failed hourly refetch blanked a curve that is hour-stable by construction. Failures now ride in the query data (functional staleTime zeroes the cache window while errored; the functional refetchInterval keeps the 60s-retry/1h-refresh cadence) and a failed refetch retains the last-good curve while the retry heals silently. The indexer client now also validates the curve's semantic invariants at parse (bounds, strict ascending order, exact kink sample, apr ceiling), aprAtUtilization documents and enforces its sorted-input precondition, and the accepted governance-window drift between the stats bar and the hour-cached curve is written down in the hook doc. Cadence and interpolation are pinned by tests. * fix(vault): close the gaps the error-as-data path left open Resolving IRM failures as query data keeps the 60s retry from billing a Sentry issue per cycle, but it also took the query out of QueryCache.onError entirely and let the retained curve chain forward unbounded. Three consequences, all addressed here. Report the failure from the queryFn itself, once, on the ok -> error edge. The global handler cannot see a queryFn that resolves, so a broken endpoint was raising nothing at all; billing every retry would recreate the alert storm queryClient.ts documents. Cap the retained curve by the age of the last SUCCESSFUL read. React Query's dataUpdatedAt cannot serve — the queryFn always resolves, so a failed refetch refreshes it forever. The ceiling is deliberately wider than the cache window: the healthy refetch lands exactly on that boundary, so a ceiling equal to it would drop the shape on the first failed refetch and leave no retention at all. Past the ceiling the curve is dropped and the card's existing unavailable state makes the outage visible. Degrade instead of throwing when a curve carries no exact kink sample. parseIrmPayload already rejects that at the fetch boundary, so this lookup is the second line of defence — and the nearest boundary above it is the app-wide one, so throwing traded the whole app for the global error screen over a single missing marker. Stop requiring aprRay. Nothing reads it, so demanding it let the indexer blank every chart by trimming a field no client consumes. Also covers the request timeout across response.text(), which bounded a stalled body with no test behind it.
…-io#2275) The rail column was pinned at 460px with shrink-0 while the stat labels give the left column a ~470px min-content floor. The xl breakpoint reads the viewport, but the 242px sidebar plus the page and card padding leave the card ~370px narrower, so between roughly 1280px and 1330px the row needs more width than the card has and the rail runs past its edge. Dropping shrink-0 lets the rail column absorb the difference. The rail is already width-driven (w-full plus a ResizeObserver), so ticks, markers and the gap arrow follow the new width.
babylonlabs-io#2279) * fix(ci): give the integrity-manifest publish a real propagation window Every vault-publish / simple-staking-publish failure on main since babylonlabs-io#253 landed is the same step: `gh release create` succeeds, then the public download URL is probed for only 30s (6 x 5s) before the job is declared broken. GitHub's release-asset CDN regularly takes 30-60s to serve a freshly created immutable release - successful runs already sat at 25-28s - and every one of those "failed" releases exists and serves 200 minutes later. Re-running only mints a wasted -rN slot for the same sha. Probe on a wall-clock deadline of 10 minutes at 10s intervals, with a per-probe curl --max-time so a stalled request cannot stretch the window past the bound. The already-published and concurrent-run branches are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): drive the integrity-manifest publish off the Releases API The action decided slot occupancy, concurrent-run races and publish success by probing the public release download URL. That URL is served by a CDN that lags a fresh immutable release by 30-60s, so ~85% of runs went red with "manifest not retrievable" even though every release existed, and the re-runs minted byte-identical -rN slots. Also fix a latent bug: `gh release create` without --target tags the default-branch HEAD at creation time, not the commit the manifests describe - 9 of 81 existing fm-* tags point at the wrong commit. - Slot occupancy, race detection and verification go through the Releases API (strongly consistent): GET /releases/tags/<tag> 200 = taken, 404 = free, anything else fails loudly; both assets must be state=uploaded. An existing release is only skipped if complete - an incomplete one is a dead slot and fails closed, after a short grace for a concurrent run still attaching. - `--target "$GITHUB_SHA"`, explicit `--repo`. - The public URL is probed as a bounded soft check (warning, not failure) with a per-probe curl timeout. - service/environment inputs validated against [a-z0-9-]; gh stderr flattened before it reaches a workflow command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): keep the CDN probe inside its window Cap the sleep between probes to the time left, so the soft check ends at CDN_PROBE_TIMEOUT_SECONDS instead of overrunning by up to one interval (review feedback). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…babylonlabs-io#2278) * feat(ts-sdk): reconstruct peg-in parameters from a stranded pre-pegin * chore(pr): comments * chore(pr): comments * chore(pr): comment
babylonlabs-io#2274) * feat(vault): rebuild DepositTerms on presign resume; map typed deposit errors * fix(vault): type presign rebuild refusals and gate them before the chain reads
…abs-io#2291) * feat(ts-sdk, vault): reach the refund from a reconstruction * chore(pr): additions * chore(pr): additions * chore(pr): comments * chore(pr): comments
babylonlabs-io#2272) * fix(ci): show the before and after pictures even when the set is large The publish step caps what one pull request may add to the images branch at 512KB and refuses the whole set past it. Nothing on the composing side knew that ceiling was there, so on babylonlabs-io#2241 five vault composites came to 678KB, the publish was refused, and the comment fell back to the list of filenames the pictures exist to replace. visual-embed.mjs now owns a byte budget below that ceiling and fits the set to it: the pictures are published at the first width in 1280/960/800/640 the whole set fits under, so a run that moves one screen still publishes it at the capture's own width and a run that moves five publishes all five, smaller. Full resolution stays in the visual-report artifact, which the comment already points at. Only when the smallest width still busts the budget is a picture given up, from the bottom of the ranking, and the comment already says how many it is not showing. Also: publishes truecolour rather than RGBA, since every source is an opaque screenshot and the alpha channel was around a tenth of the bytes; reduces to an arbitrary width instead of a whole factor, so the rung under 1280 is 960 and not 640; and pins the order of the two caps in a test, which is the check that was missing when they drifted apart. * fix(ci): keep the picture set honest when it has to shrink Averaging dropped the last source column and row - the right edge of the rightmost panel and the bottom of the bottom panel, which is the hairline the averaging exists to catch. The byte budget gave up pictures from the tail of a global ranking, and the vault screens sit at that tail, so a token repaint published a Storybook-only comment. It now gives up from whichever surface is holding the most, and a picture too large to fit on its own goes first rather than evicting every picture that would have fit on the way to being dropped itself. A picture that cannot be encoded costs that picture instead of the whole comment, and anything given up is named in the comment rather than only in the run log. Also corrects three claims the code made about itself: the reason the budget sits under the workflow ceiling, what keeps the alpha channel constant, and that the ladder rung is a cap rather than a scale factor. * fix(ci): correct the comments the width ladder made wrong `panelLayout` still argued from the integer reduction this branch deleted: two 1280px screens side by side were said to publish at 428px, a third of life size. Under `reduceToWidth` they publish at 639px, half life size - the conclusion survives, the arithmetic did not. Same figure was repeated in the test. `EMBED_MAX_WIDTH` is now rung 0 of the ladder rather than the width every composite is reduced to, and `MAX_EMBEDDED_GROUPS` still promised there was no third cap on the total when the byte budget is exactly that. `screenCaption` reported its trim in capture rows without saying so, so a reduced run read "trimmed to 1800px of 3000px" over a 900px-tall picture. It now says which screen the number describes. The ceiling guard also accepts a plain byte count, and is anchored to an assignment line so the prose above the constant cannot be read as it. * fix(ci): correct the panel width and two claims the last commit missed The reduced side-by-side panel is 638px, not 639: at a factor of 2.00625 the 639th column straddles the gutter, and 639 + gutter + 639 does not fit 1280. Measured against `reduceToWidth` rather than derived. `panelLayout` was credited with keeping every composite inside the top rung. It only bounds the side-by-side case; stacked and single composites are as wide as their capture and stay inside the rung only because nothing captured here is wider than it. The byte-budget docblock still said anything given up is named in the comment, twelve lines below the paragraph correcting that to a count in the comment and names in the run log. The anchor rationale in the test cited prose that is not in the workflow; the real reason is the bare-literal branch making a run of digits enough to match.
…onlabs-io#2273) * fix(release): publish the real version of each sibling package Packages that depend on another package in this repo were published with the wrong version of that dependency. wallet-connector's latest release cannot be installed at all, and ts-sdk resolves a WASM build missing 14 of the 20 functions it calls. The release now writes each sibling's real version into the manifest before publishing, refuses to publish when any of those versions is stale, a placeholder, or missing from npm, and creates git tags only after a successful publish so a failed one can be retried. * fix(release): address review on the sibling version gate Unblocks the first release after merge: core-ui 1.109.0 is a git tag that was never published, so pinning it would have aborted every run. All six on-disk versions are now set to their latest release tag, which also makes each package co-release and satisfy the gate. Fixes a regression where releaseChangelog was filtered but releasePublish was not, which would publish a dependent with no tag. Detects nx's on-disk version fallback by checking the tag list rather than the version string, keeps the range a workspace caret asked for, rejects any local protocol that survives into a published manifest, and rethrows a failed tag push on the prerelease path where nothing else creates the tag. Adds a typecheck target and a dry-run rehearsal to CI, and takes the release driver from 13 tests to 61. * fix(release): tell a skipped publish apart from an unpublishable package The new dry-run rehearsal in CI caught this on its first run. A project missing from the publish results was treated as a package nx had no publish target for, but nx also omits a project it skipped because one of its dependencies failed - which is what every dry run does. Publishability is now checked before publishing, where a private package can be named directly, and a missing result afterwards is reported as the failed publish it is. * fix(release): only demand a release tag where the version becomes a pin A package under the release globs that has never been released has no tag, so checking every versioned project would block the whole release over a package nothing depends on. The check now runs where the version is used as a dependency pin, which is the only place a version read off disk can do damage. Also runs the release-driver typecheck through its nx target. * fix(release): stop a release candidate publishing a stable dependent nx bumps a dependent dragged in by `updateDependents` with a hardcoded `patch` that ignores the preid. Dispatching an RC for the WASM package therefore also versioned ts-sdk at a stable number, and the driver published and stable-tagged it - pinned to the release-candidate engine. A `next` dist-tag does not keep it away from consumers, because dist-tags do not constrain semver range resolution, and the stable tag means no later run ever republishes that number. A prerelease run now publishes prereleases or nothing. * docs(release): say what to do when a release stops The runbook told you to hand-create a tag for a new package, which under the sibling gate blocks every dependent's release with no forward path. It also had nothing on the failure modes the gate introduces. Adds the missing caveat to the new-package section and a table mapping each message the driver can stop on to its recovery step. * refactor(release): keep file-local helpers out of the module surface `readJsonFile`, `isLocalDependencySpec`, `isPlaceholderVersion` and `LOCAL_DEPENDENCY_PROTOCOLS` are only ever called a few lines below their own definitions, while comparable helpers in the same files are already private. Also drops the `'(unnamed package)'` fallback on the consumer name. It is unreachable - `readReleasePackages` throws on a nameless manifest and keys the map by that name - and the only call site already holds the authoritative name, so it is threaded in instead. * docs(release): say which commit to put a restored tag on "Tag the version that is actually on NPM" left out the part that matters. Tagging `HEAD` makes nx read every unreleased commit before it as part of the already-published version, so those changes drop out of the next release entirely. The tag has to go on the commit that produced the published version. Also labels the output block, which is release output rather than a command. * fix(release): check a sibling the prerelease gate dropped from the run `publishedByThisRun` is what suppresses both registry guards for a sibling, so it has to mean "this run will actually publish it". It was derived from `newVersion !== null`, which was the same predicate as the publish set until the prerelease gate started dropping stable-numbered dependents from it. A dropped project left marked published would have its version pinned into a sibling with no check at all - the phantom pin this module exists to stop. Derives the flag from the publish set instead, so a dropped sibling falls through to the release-tag and registry checks and the run stops loudly. * fix(release): fail a prerelease run the gate leaves empty "No project release needed" was covering two different outcomes. It is true when nx bumped nothing; it is false when nx bumped projects and every one of them was gated out for not being a prerelease - that run published nothing and exited 0, which is how a preid that never reached the version step would look like success. * docs(release): correct two rows of the failure table `latestStableVersion` filters prereleases out before that version is reported, so "a range or a prerelease" named a cause the error cannot have. `readReleasePackages` rejects any `release.projects` entry that is not `<directory>/*`, so narrowing that config is not a recovery - following it trades one stopped release for another.
* feat(wasm): add tx-graph v3 support * chore(pr): regenerate docs * chore(pr): comment
* feat(ledger-vault-signer): read master fingerprint and verbatim account xpub The default wallet policy and the PoP input's TAP_BIP32_DERIVATION both need the device master fingerprint, and the policy key info must embed the device's own xpub serialization — the firmware byte-compares it. * feat(ledger-vault-signer, wallet-connector, ts-sdk): ledger BIP-322 proof of possession * feat(ledger-vault-signer, wallet-connector, ts-sdk, vault): ledger Pre-PegIn wallet-policy path * fix(ts-sdk, ledger-vault-signer): regenerate API docs; bound hardened policy indices * docs(ts-sdk): align supportsDepositApproval docs; test coinType overflow * fix(wallet-connector): mark bitcoinjs-lib as ESM for bundled CJS interop * feat(ledger-vault): skip the redundant re-ceremony at Pre-PegIn broadcast * feat(ledger-vault): complete the depositor deposit → payout flow * fix(ledger-sdk): fix review comments * fix(ledger-vault): reject a device whose xpub and depositor key disagree * fix(ledger-vault): broken build
* feat(vault): align the fee breakdown rows with the v3 row system * feat(vault): restyle the withdraw and refund flows * feat(vault): add the disabled-deposit states to Overview and Vaults
babylonlabs-io#2296) * fix(vault): stop the collateral stat column collapsing under its label * fix(vault): compress the stat columns in the 1280-1439 band instead of clipping
…iew (babylonlabs-io#2280) * feat(vault): chart the live position on the embedded liquidation preview * feat(vault): retire the liquidation analysis chart feature flag * fix(vault): exclude activating vaults, guard empty cascades, recompute at the simulated price * test(vault): assert the selected cascade result identity in the preview tests * style(vault): drop a stray blank line left by the rebase
…y page (babylonlabs-io#2286) * feat(vault): show the per-row USD value on activity rows The Figma "after deposit" Activity frame carries a USD sub-line under each row's amount; the v3 restyle deferred it because ActivityLog only had a preformatted token amount. ActivityAmount now also carries the amount as a plain number, set wherever the source knows it unambiguously, and the row derives the sub-line from that number times the CURRENT price out of usePrices — the same Chainlink source the dashboard and deposit form read. There is no historical price plumbing in the activity projection, so an old row's USD value reflects today's price, not the price when the transaction happened. A row that cannot be priced — unknown symbol, no numeric amount, or a non-positive product — renders no sub-line rather than "$0 USD". * fix(vault): derive the deposit row status from live vault state A settled Deposit row read "In use" purely because of its row type, so a deposit that had since been withdrawn kept claiming its collateral was locked. The row now correlates the deposit with current vault state. The join is `ActivityLog.vaultId` against `Vault.id` — the only correlation the row type documents as safe, since an indexed row's `id` is its event, not its vault. A vault still at `ContractStatus.ACTIVE` reads "In use"; one that has left ACTIVE — redeemed, withdrawn, liquidated, expired — reads "Done". The vault ids come from `useVaults(ethAddress)` in ActivityListWithRefund, which shares its query key with the deposit lifecycle already mounted there, so this adds no fetch. The set is undefined until that read resolves, and an undefined set (or a row with no vault id) keeps the type-derived label rather than flashing a status the data has not confirmed. Pending and expired keep taking precedence.
babylonlabs-io#2298) * feat(ledger-vault, vault): typed device errors, signing cancel, no version pin * fix(vault): bind signing cancel to the originating provider
…bs-io#2299) * feat(ts-sdk): verify P2WPKH depositor signatures host-side * fix(ts-sdk): derive and enforce MockBitcoinWallet key consistency * docs(ts-sdk): regenerate API docs for the P2WPKH verifiers
babylonlabs-io#2300) * fix(vault, ledger-vault): stop the payout loop on cancel; honest cancel copy * test(vault): split the cancellation tests into one-behavior cases
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Critical path PRThis PR touches files defined as critical in CLAUDE.md. Changes here handle irreversible value movement - silent bugs produce wrong BTC amounts. Paths touched:
Required before merge:
|
Visual regression🖼️ 204 of 349 screens render differently than the merge-base. vault - 20 of 20 screens changedShowing 2 of 10 changed groups. The rest are in the full list at the end.
|










!!!!!!!! Ignore CI errors. !!!!!!!!!
Syncs our
mainwithbabylonlabs-io/babylon-toolkit:main.Why a PR instead of the GitHub "Sync fork" button
The org ruleset
default-ruleset-sf-engineeringapplies to~DEFAULT_BRANCHand requires changes to go through a pull request (2 approvals + code owner review, no bypass actors). The Sync fork button pushes directly tomain, so it was silently rejected — that's why it appeared to do nothing whilemainsat 316 commits behind.Contents
fd6dd422→4ccda5a9(fix(vault, ledger-vault): stop the payout loop on cancelfix(vault, ledger-vault): stop the payout loop on cancel; honest canc… babylonlabs-io/babylon-toolkit#2300).main— our customizations live onstaging/production.mainwas a strict ancestor ofupstream/main, so this is a pure fast-forward with no conflicts and nothing of ours to lose.Please merge with "Create a merge commit"
Do not squash or rebase. Rebasing rewrites all 316 upstream SHAs, which is what caused the existing duplicate-commit history on
staging(commits such as babylonlabs-io#1624 appear there with different SHAs than upstream's own copies). Preserving upstream SHAs keeps future syncs clean fast-forwards.🤖 Generated with Claude Code