Skip to content

feat(widget): migrate data flows to Yield API#516

Open
petar-omni wants to merge 61 commits into
mainfrom
feat/yield-balances-migration
Open

feat(widget): migrate data flows to Yield API#516
petar-omni wants to merge 61 commits into
mainfrom
feat/yield-balances-migration

Conversation

@petar-omni
Copy link
Copy Markdown
Contributor

@petar-omni petar-omni commented Apr 28, 2026

Overview

This PR migrates the widget to use the new Yield API for balances, validators, and action management. It introduces a dedicated YieldApiClientProvider backed by openapi-fetch, decouples domain types from @stakekit/api-hooks, and updates the full data flow from yield discovery through staking, unstaking, and pending actions.


Key Changes

New: Yield API Client Provider

  • Introduces YieldApiClientProvider with createYieldApiFetchClient (openapi-fetch-based) and a react-query useYieldApiClient wrapper.
  • Registers onResponse hooks for geo-block detection, rich error handling, and delayed request gating.
  • Exposes useYieldApiFetchClient and useYieldApiClient hooks; throws if used outside the provider.
  • Added VITE_YIELDS_API_URL env variable (falls back to https://api.yield.xyz).

Yield Balances Migration

  • Replaces yieldYieldBalancesScan (POST /v1/yields/balances/scan) with the new POST /v1/yields/balances endpoint via the Yield API client.
  • Query invalidation now uses a static key (["post", "/v1/yields/balances"]).
  • Drops additionalAddresses from the balances scan request.

Domain Type Decoupling

  • Introduces a new Yield domain type (wrapping the Yield API schema yield + a __fallback__ for legacy compatibility) that replaces YieldDto from @stakekit/api-hooks across the codebase.
  • New domain type modules: action, addresses, errors, fees, pending-action, positions, price, reward-rate, settings, stake, token-balance, tokens, transaction, tron, validators, yield-api, yields.
  • TokenDto is now a union of the legacy DTO and the new Yield API TokenDto.

Yield Opportunity & Validators

  • getYieldOpportunity now concurrently fetches from both GET /v1/yields/{yieldId} (new) and the legacy endpoint, merging results with __fallback__.
  • New useYieldValidators hook: fetches all validators via paginated GET /v1/yields/{yieldId}/validators (limit 100), with filterValidators support.
  • Replaces validatorsConfig with yieldApiFetchClient throughout wagmi config, init hooks, and opportunity hooks.

Action Preview & Steps

  • Stake, unstake, and pending-action review hooks now use createEnterAction / createExitAction / createManageAction for action previews, replacing previous gas estimation hooks.
  • Gas totals are derived by decoding per-transaction gasEstimate from the preview payload.
  • useStepsMachine now uses yieldId (was integrationId) and submits via submitTransaction / submitTransactionHash.
  • Request DTOs migrated: integrationId/args to yieldId/arguments; addresses field added explicitly.

Image Component Refactor

  • Image component simplified: always renders an <img> with an inline SVG monogram data-URL as fallback (via fallbackName prop).
  • ImageFallback component deleted; all usages updated to use fallbackName.
  • Image prop names updated: containerProps to wrapperProps, imageProps to imgProps.

Reward Rate Breakdown

  • New RewardRateBreakdown component renders APY composition (native / protocol incentive / campaign) from YieldRewardRateDto.
  • New domain/types/reward-rate.ts with helpers: getRewardRateBreakdown, getYieldRewardRateDetails, getRewardTypeFromRateType.
  • Position details page now shows personalized APY and APY composition breakdown.

Test Infrastructure

  • New test fixtures: yieldApiYieldFixture, yieldBalanceFixture, yieldValidatorsFixture, yieldApiActionFixture, yieldApiTransactionFixture.
  • New legacyApiRoute / yieldApiRoute helpers for MSW mocks.
  • All use-case test setups updated to use Yield API-shaped responses and new mock endpoints.
  • New test suite: trust-incentive-apy covering APY composition and personalized reward rate UI.
  • New unit tests for Image and TokenIconImage components.
  • Added packages/widget/.env.test with test environment variables.

Tooling & CI

  • mise.toml: Node pinned to 24.15.0, pnpm to 10.33.2 (via npm:pnpm).
  • CI and release workflows: replaced jdx/mise-action@v2 with inline curl-based mise install.
  • package.json: @biomejs/biome bumped to ^2.4.8, packageManager updated to pnpm@10.33.2.
  • Added AGENTS.md documenting monorepo conventions, i18n guidelines, and agent tooling.
  • Added opensrc/ to .gitignore.
  • Added gen:yield-api script to generate TypeScript from the Yield API OpenAPI spec.

Other Notable Changes

  • New prepareLedgerLiveTransaction utility for EVM, Polkadot, Tron, Near, Tezos, Ton, and Cosmos Ledger Live transaction encoding.
  • handleGeoBlockResponse and handleRichErrorResponse extracted as testable exports.
  • VirtualList / GroupedVirtualList: removed measureElement ref from row containers.
  • waitForDelayedApiRequests exported from delay-api-requests.ts.
  • i18n: new keys for APY composition, personalized APY, balance states (active, entering, exiting, withdrawable, claimable), and auto-sweep reward actions (EN + FR).
  • APToPercentage now formats via shared formatNumber utility.
  • getPositionTotalAmount updated to aggregate amountUsd directly from balance items.

Changeset

@stakekit/widget — patch: feat: migrate to yield api balances

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 28, 2026

⚠️ No Changeset found

Latest commit: 0df1959

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Large migration to a Yield API client and domain model: adds a YieldApi client/provider and request helpers, replaces many external DTOs with in-repo domain types, rewires providers/hooks/pages/state/machines to use the new client and endpoints, updates UI components (Image/token handling), extends MSW/fixtures/tests for the Yield API, and bumps toolchain (Node 24, pnpm 10.33.2).

Changes

Build, CI, and Tooling

Layer / File(s) Summary
CI and toolchain pins
.github/workflows/ci.yml, .github/workflows/release.yml, mise.toml, package.json
Pin Node to 24.15.0 and pnpm to 10.33.2; replace jdx/mise-action with an inline mise installer (curl
Test env / vite
packages/widget/.env.test, packages/widget/vite/vite.config.base.ts
Add VITE test env vars (API/keys) and disable Playwright screenshotFailures and Vite compressed-size reporting.
Packaging metadata
.changeset/lemon-drinks-prove.md, .gitignore
Add changeset for release message and update .gitignore to include opensrc/ and newline fixes.

Yield API client & request helpers

Layer / File(s) Summary
OpenAPI fetch client & provider
packages/widget/src/providers/yield-api-client-provider/index.tsx, types.ts
Add createYieldApiFetchClient, YieldApiClientProvider, contexts, and hooks useYieldApiClient / useYieldApiFetchClient; memoize fetch/react-query clients and surface typed client.
Actions & request helpers
packages/widget/src/providers/yield-api-client-provider/actions.ts, request-helpers.ts
Add wrappers for createEnter/Exit/Manage actions, listActions, getTransaction, submitTransaction/Hash; add withAdditionalAddresses and getResponseData helpers.
Response handling
yield-api-client-provider (onResponse hooks)
Register response hooks to await delayed requests, parse non-OK responses, and run geo-block / rich-error handlers (i18n-aware) before returning results.

Domain model & types

Layer / File(s) Summary
Domain types surface
packages/widget/src/domain/types/*
Introduce and reorganize many domain types and helpers (Yield, YieldTokenDto, YieldBalanceDto, RewardTypes, action/transaction types, pending-action utilities, positions, prices, reward-rate breakdown, etc.) to replace prior @stakekit/api-hooks DTO usage.
Type adapters / re-exports
packages/widget/src/domain/types/{addresses,errors,fees,tron,validators,yield-api}.ts
Add small re-export/adaptor modules to surface external schema types via local paths.

Providers, wiring, and global composition

Layer / File(s) Summary
Provider composition
packages/widget/src/providers/index.tsx, providers/wagmi/index.ts
Insert YieldApiClientProvider into app provider tree; create and memoize yieldApiFetchClient in wagmi builder and thread it into enabled-networks and platform configs.
Enabled networks & platform plumbing
providers/*/config.ts, providers/api/get-enabled-networks.ts
Refactor network discovery and platform config generation to accept and use yieldApiFetchClient for GET /v1/networks and related queries.

Private API surface & hook migration

Layer / File(s) Summary
Private API reshaping
packages/widget/src/common/private-api.ts
Remove old yield-scan/validator endpoints and add token/prices/balances/rewards/verification wrappers mapped to new API routes.
Hooks → Yield API client
src/hooks/api/*, use-yield-balances-scan.ts, use-yield-validators.ts, use-prices.ts, use-tokens-prices.ts
Migrate many hooks from @stakekit/api-hooks to local private-api / yield API fetch client calls, update query keys and types, add paginated useYieldValidators, and adjust selects/returns to domain types.
Deleted helpers
use-base-token.ts, use-gas-fee-token.ts, get-gas-mode-value.ts
Remove deprecated helpers and replace callers with domain helpers (e.g., getYieldActionArg, mechanics.gasFeeToken).

Components — images, token icons, and UI changes

Layer / File(s) Summary
Image component & fallback
packages/widget/src/components/atoms/image/index.tsx, tests packages/widget/tests/components/atoms/image.test.tsx
Refactor Image to always render an <img> and fall back to an inline SVG data-URL monogram via fallbackName; rename props to wrapperProps/imgProps; add tests; remove ImageFallback component and related styles.
Token icon wiring
packages/widget/src/components/atoms/token-icon/**
Broaden token prop types to `TokenDto
New UI component
packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx
Add RewardRateBreakdown component to render reward-rate composition (native/protocol/campaign) and optional "up to" campaign wording.

Pages, flows, machines, and state

Layer / File(s) Summary
Pages & contexts
src/pages/** (earn, review, position-details, steps, activity, complete)
Migrate pages and contexts to use Yield domain type, pass yieldApiFetchClient where needed, adapt request DTO shapes (yieldId/arguments/addresses), and update provider/validator handling.
Steps machine & signing/submission
src/pages/steps/hooks/use-steps-machine.hook.ts, use-steps.hook.ts
Rename integrationIdyieldId; remove internal transaction construction path; sign from tx.unsignedTransaction; replace legacy submit/status endpoints with yield-client getTransaction, submitTransaction, submitTransactionHash; adjust initial tx detection and explorer URL handling.
Stores & init DTOs
enter-stake-store, exit-stake-store, pending-action-store
Switch init/request DTOs to YieldCreateAction/Manage shapes, add addresses into init data, and widen token types to include yield tokens.

Formatting, utils, and minor behavior shifts

Layer / File(s) Summary
Formatters & mappers
src/utils/*
APToPercentage now uses shared formatNumber; gas/fee formatters read mechanics.gasFeeToken; price types updated to PriceRequest/Response shapes.
MSW worker style
packages/widget/public/mockServiceWorker.js
JavaScript style modernization (double quotes, arrow functions, semicolons) without behavior changes.

MSW, tests, and fixtures

Layer / File(s) Summary
MSW routes & helpers
packages/widget/src/worker.ts, packages/widget/tests/mocks/api-routes.ts
Extend MSW to serve Yield API endpoints (yields, validators, balances, actions, transactions); add legacyApiRoute and yieldApiRoute helpers for tests.
Fixtures & test setups
packages/widget/tests/fixtures/*, tests/use-cases/*
Add yield-api fixtures and converters from legacy fixtures; update many test setups and mocks to yield-api shapes; add trust-incentive APY test suite and other test updates to reflect new API shapes.
New tests
packages/widget/tests/components/atoms/image.test.tsx, tests/use-cases/trust-incentive-apy/*
Add Image unit tests and new E2E-like trust-incentive APY test suite with deterministic fixtures.

Removals & cleanup

Layer / File(s) Summary
Deleted legacy artifacts
packages/widget/script.ts, src/common/get-gas-mode-value.ts, src/components/atoms/image-fallback/*
Remove obsolete network-scan script, older gas-mode helper, and ImageFallback component and styles.

Sequence Diagram(s)

sequenceDiagram
    participant Widget as Widget App
    participant Provider as YieldApiClientProvider
    participant FetchClient as OpenAPI Fetch Client
    participant YieldAPI as Yield API Server
    participant DB as Backend

    rect rgba(200,150,255,0.5)
    Note over Widget,Provider: Client initialization
    Widget->>Provider: useYieldApiFetchClient()
    Provider->>Provider: createYieldApiFetchClient(apiKey, url, hooks)
    Provider-->>Widget: YieldApiFetchClient & OpenAPI client
    end

    rect rgba(150,200,255,0.5)
    Note over Widget,YieldAPI: Fetch yield opportunity
    Widget->>FetchClient: GET /v1/yields/{yieldId}
    FetchClient->>YieldAPI: HTTP GET
    YieldAPI->>DB: Resolve yield + mechanics
    DB-->>YieldAPI: YieldApiYieldDto
    YieldAPI-->>FetchClient: HTTP 200 + body
    FetchClient->>Widget: parsed data (geo-block / rich-error handled)
    end

    rect rgba(150,255,200,0.5)
    Note over Widget,YieldAPI: Create action (preview/submit)
    Widget->>FetchClient: POST /v1/actions/{enter|exit|manage} (YieldCreateActionDto)
    FetchClient->>YieldAPI: HTTP POST
    YieldAPI->>DB: Create action + build txs
    DB-->>YieldAPI: ActionDto + Transactions
    YieldAPI-->>FetchClient: Action response
    FetchClient-->>Widget: ActionDto for preview/submit
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through types and endpoints wide,
A Yield client tucked by my side,
SVG whiskers for tokens bright,
Legacy and new schemas now unite,
Tests hum—deployments in sight!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/yield-balances-migration

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts (1)

43-45: ⚠️ Potential issue | 🟠 Major

String placeholder path is ignored in final return.

On Line 43, when perReward is "- %", the function still returns estimatedRewards.extract()?.percentage, so the new placeholder may never be shown (and can become undefined after DTO migration).

💡 Proposed fix
-  return typeof perReward === "string" || config.env.isTestMode
-    ? estimatedRewards.extract()?.percentage
-    : transformedMotionValue;
+  return typeof perReward === "string"
+    ? perReward
+    : config.env.isTestMode
+      ? estimatedRewards.extract()?.percentage
+      : transformedMotionValue;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts`
around lines 43 - 45, The current return expression returns
estimatedRewards.extract()?.percentage when perReward is a string placeholder
like "- %", hiding the placeholder and possibly yielding undefined; change the
logic in use-animate-yield-percent so that if typeof perReward === "string" you
return perReward directly (not estimatedRewards.extract()?.percentage),
otherwise preserve the existing test-mode and motion-value behavior; also guard
the estimatedRewards.extract()?.percentage with a nullish fallback (e.g., ??
perReward or a safe default) to avoid returning undefined after DTO migration.
packages/widget/tests/use-cases/select-opportunity.test.tsx (1)

117-129: ⚠️ Potential issue | 🟡 Minor

Replace wildcard matcher with legacyApiRoute() for /v1/tokens endpoint.

This endpoint is the legacy API version used for token retrieval (per use-default-tokens.ts). Using the route helper maintains consistency with other endpoint mocking in this test (lines 65, 135-141) and prevents potential host/base-URL wiring issues.

Suggested change
-      http.get("*/v1/tokens", async () => {
+      http.get(legacyApiRoute("/v1/tokens"), async () => {
         await delay();
         return HttpResponse.json([
           {
             token,
             availableYields: [
               "ethereum-eth-lido-staking",
               "ethereum-eth-stakewise-staking",
               "ethereum-eth-reth-staking",
             ],
           },
         ]);
       }),

Note: This pattern appears in multiple test setup files (external-provider, deep-links-flow, staking-flow, gas-warning-flow, etc.). Consider applying the same fix across the test suite for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/tests/use-cases/select-opportunity.test.tsx` around lines 117
- 129, Replace the wildcard http.get matcher for the "/v1/tokens" endpoint with
the helper legacyApiRoute() so the test uses the same legacy API route as
use-default-tokens.ts; locate the http.get(...) call that returns
HttpResponse.json([...]) and change its first argument to
legacyApiRoute("/v1/tokens") to avoid host/base-URL wiring issues and match
other mocks in this suite.
packages/widget/src/providers/sk-wallet/errors.ts (1)

30-30: ⚠️ Potential issue | 🟡 Minor

Remove dead code: unused SendTransactionError instantiation.

This line creates an error instance that's never used or assigned. It appears to be leftover debug/test code.

🗑️ Proposed fix
-
-new SendTransactionError();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/providers/sk-wallet/errors.ts` at line 30, The line
instantiating SendTransactionError is dead code and should be removed: delete
the standalone `new SendTransactionError();` expression in the errors module so
the class is only defined/used where intended; ensure no other code relies on
that unused instantiation and run tests/typechecks to confirm no references to
the unused instance remain (look for the SendTransactionError symbol in the same
file or nearby exports).
packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts (1)

334-337: ⚠️ Potential issue | 🟠 Major

assign call has no effect in error handler.

On line 335, assign(({ context }) => ({ ...context, error })) is called but its return value is not used. In XState, assign returns an action object that needs to be executed as part of the machine's actions array, not called imperatively. The error won't be stored in context.

🐛 Proposed fix

The error assignment should be handled via the __SUBMIT_ERROR__ event's actions in the machine definition (around line 265), or the error should be passed with the event:

               .caseOf({
                   Right() {
                     self.send({ type: "__SUBMIT_SUCCESS__" });
                   },
                   Left(error) {
-                    assign(({ context }) => ({ ...context, error }));
-                    self.send({ type: "__SUBMIT_ERROR__" });
+                    self.send({ type: "__SUBMIT_ERROR__", error });
                   },
                 }),

Then update the event type and add an action handler:

-        | { type: "__SUBMIT_ERROR__" },
+        | { type: "__SUBMIT_ERROR__"; error?: Error },

And in the on handler for __SUBMIT_ERROR__:

           __SUBMIT_ERROR__: {
             target: ".error",
+            actions: assign(({ event }) => ({
+              error: Maybe.fromNullable(event.error),
+            })),
           },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts`
around lines 334 - 337, The assign call inside Left is not applied because
assign returns an action object instead of mutating context; update Left to
include the error in the event payload (send an event with type
"__SUBMIT_ERROR__" and the error) and then move the assign logic into the
machine's event handler for "__SUBMIT_ERROR__" (add an action that assigns error
into context via assign in the machine definition near the existing
__SUBMIT_ERROR__ handling), or alternatively replace the send call with
executing the assign action directly as part of the actions array; reference:
Left, assign, self.send, __SUBMIT_ERROR__, and the machine definition where
__SUBMIT_ERROR__ actions are declared.
🟡 Minor comments (10)
packages/widget/src/components/molecules/amount-toggle/index.tsx-62-62 (1)

62-62: ⚠️ Potential issue | 🟡 Minor

Remove or justify this unrelated export reordering.

The exports were reordered to alphabetical order (Amount before Root), but no ESLint or Prettier configuration enforces this convention. This change appears unrelated to the PR's stated objective of yield balances migration and lacks explanation. Either revert this change or clarify its purpose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/components/molecules/amount-toggle/index.tsx` at line 62,
The change reordered the named exports to "Amount, Root" which is unrelated to
the PR; revert the export ordering to the original sequence (export { Root,
Amount }) in the amount-toggle module or add a short inline comment explaining
why alphabetical order is required and reference a style rule, or add a
lint/prettier rule that enforces alphabetical exports; update the export
statement for Amount and Root accordingly (symbols: Amount, Root) so the diff
only contains intentional changes.
packages/widget/.env.test-1-4 (1)

1-4: ⚠️ Potential issue | 🟡 Minor

Reorder keys to clear dotenv-linter warnings.

Current ordering triggers static analysis warnings; reordering keeps CI/lint output clean.

Suggested reorder
-VITE_API_URL=https://api.stakek.it/
-VITE_YIELDS_API_URL=https://api.yield.xyz/
-VITE_API_KEY=vitest-api-key
 MODE=test
+VITE_API_KEY=vitest-api-key
+VITE_API_URL=https://api.stakek.it/
+VITE_YIELDS_API_URL=https://api.yield.xyz/
Based on learnings: Confirm nothing is broken with lint command which checks lint/type errors after making changes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/.env.test` around lines 1 - 4, Reorder the environment
variable keys in the packages/widget/.env.test file to satisfy dotenv-linter
(e.g., sort keys alphabetically or follow the project's expected key order such
that VITE_API_KEY, VITE_API_URL, VITE_YIELDS_API_URL, MODE are in the corrected
order); update the .env.test file accordingly and then run the project's lint
command (the same lint/type-check step used in CI) to confirm no lint or type
errors were introduced.
packages/widget/src/pages-dashboard/activity/activity-details.page.tsx-89-89 (1)

89-89: ⚠️ Potential issue | 🟡 Minor

Confirm that integrationId prop receives and semantically represents yield identifiers.

The change is functionally correct: selectedAction.yieldId is passed to integrationId, which is then used as a yield identifier in isEthenaUsdeStaking() (compares against "ethena-usde-staking") and useYieldOpportunity(). However, the prop naming creates a semantic inconsistency—the parameter is named integrationId but receives and is treated as a yield ID throughout downstream code. This works because both represent the same string identifier in this codebase, but the naming should be clarified or unified for maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/pages-dashboard/activity/activity-details.page.tsx` at
line 89, The prop integrationId is being passed selectedAction.yieldId but is
treated as a yield identifier downstream (see isEthenaUsdeStaking() and
useYieldOpportunity()), causing a semantic mismatch; update the component API so
the prop name matches its meaning: either rename the prop integrationId to
yieldId where it’s declared/consumed or change the call site to pass
selectedAction.yieldId into a yieldId prop, and update all usages of
integrationId inside the component and in calls to isEthenaUsdeStaking() and
useYieldOpportunity() to use yieldId so naming is unified and self-explanatory.
packages/widget/src/translation/French/translations.json-163-163 (1)

163-163: ⚠️ Potential issue | 🟡 Minor

Fix French typography in APY composition label.

Line 163 should use Jusqu’à {{value}} (with apostrophe/accent), not Jusqu'a {{value}}.

✏️ Proposed fix
-      "up_to": "Jusqu'a {{value}}"
+      "up_to": "Jusqu’à {{value}}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/translation/French/translations.json` at line 163, Update
the French translation value for the "up_to" key in translations.json to use the
correct typographic apostrophe and accented "à": replace "Jusqu'a {{value}}"
with "Jusqu’à {{value}}" (use the proper right single quotation mark + "à") so
the label reads correctly in French.
packages/widget/src/translation/French/translations.json-407-407 (1)

407-407: ⚠️ Potential issue | 🟡 Minor

Fix accent in personalized APY label.

Line 407 should be APY personnalisé.

✏️ Proposed fix
-    "personalized_apy": "APY personnalise",
+    "personalized_apy": "APY personnalisé",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/translation/French/translations.json` at line 407, Update
the French translation value for the key "personalized_apy" in translations.json
to use the correct accent: change the string "APY personnalise" to "APY
personnalisé" so the label displays the proper French accent.
packages/widget/src/hooks/api/use-tokens-prices.ts-18-27 (1)

18-27: ⚠️ Potential issue | 🟡 Minor

Fix duplicate token in tokenList: second entry should reference yield's base token.

The comment on line 9 documents "Requested Token + Yield base token + Yield gas fee token", but line 23 incorrectly uses val.token twice:

tokenList: [val.token, val.token, val.yieldDto.mechanics.gasFeeToken]

The second entry should be the yield's base token, not the requested token:

tokenList: [val.token, val.yieldDto.token, val.yieldDto.mechanics.gasFeeToken]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/hooks/api/use-tokens-prices.ts` around lines 18 - 27, The
tokenList currently contains a duplicate requested token in the priceRequestDto
creation; update the mapping inside the useMemo (where Maybe.fromRecord({
yieldDto, token }) is mapped to the DTO) so tokenList becomes [val.token,
val.yieldDto.token, val.yieldDto.mechanics.gasFeeToken] (i.e., replace the
second val.token with the yield's base token val.yieldDto.token) to match the
documented "Requested Token + Yield base token + Yield gas fee token".
packages/widget/src/hooks/use-positions-data.ts-76-80 (1)

76-80: ⚠️ Potential issue | 🟡 Minor

Fix validator-address fallback when validators is an empty array.

Current ?? fallback skips balance.validator whenever balance.validators is present but empty. That can drop addresses unexpectedly.

Suggested fix
-const getBalanceValidatorAddresses = (balance: YieldBalanceDto) =>
-  (
-    balance.validators?.map((validator) => validator.address) ??
-    (balance.validator?.address ? [balance.validator.address] : [])
-  ).filter(Boolean);
+const getBalanceValidatorAddresses = (balance: YieldBalanceDto) => {
+  const fromValidators = (
+    balance.validators?.map((validator) => validator.address) ?? []
+  ).filter(Boolean);
+
+  if (fromValidators.length > 0) return fromValidators;
+  return balance.validator?.address ? [balance.validator.address] : [];
+};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/hooks/use-positions-data.ts` around lines 76 - 80,
getBalanceValidatorAddresses uses the nullish coalescing operator (??) which
treats an empty validators array as present and therefore skips the fallback to
balance.validator; change the expression to check for a non-empty validators
array (e.g., use a length check or logical OR) so that when balance.validators
is an empty array it falls back to balance.validator.address; update the
function getBalanceValidatorAddresses to use something like (balance.validators
&& balance.validators.length ? balance.validators.map(v => v.address) :
(balance.validator?.address ? [balance.validator.address] : [])) and keep the
final .filter(Boolean) intact.
packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts-46-53 (1)

46-53: ⚠️ Potential issue | 🟡 Minor

Potential issue: providerId may be included when undefined.

When b.validator?.providerId is truthy (line 48), the returned object includes both providerId and validatorAddress. However, if b.validator.address is undefined, you'll be including validatorAddress: undefined in the DTO, which may cause issues downstream.

🛡️ Suggested defensive fix
             return List.find(
               (b) => !!b.validator?.providerId,
               val.stakedOrLiquidBalances
             ).map((b) => ({
               providerId: b.validator?.providerId,
-              validatorAddress: b.validator?.address,
+              ...(b.validator?.address && { validatorAddress: b.validator.address }),
             }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts`
around lines 46 - 53, The code in use-stake-exit-request-dto uses List.find on
val.stakedOrLiquidBalances when
isYieldIntegrationAggregator(val.integrationData) is true but may return an
object with validatorAddress undefined; change the mapping logic so you only
include providerId/validatorAddress when validator.address is defined (e.g.,
check b.validator?.address before returning the DTO), or return undefined/null
instead of an object with validatorAddress: undefined; update the mapping in the
List.find result to guard on b.validator?.address so downstream consumers never
receive validatorAddress set to undefined.
packages/widget/src/providers/yield-api-client-provider/index.tsx-84-91 (1)

84-91: ⚠️ Potential issue | 🟡 Minor

Potential excessive client recreation due to i18n dependency.

The i18n object from useTranslation() may change reference on every render or language change. Since createYieldApiFetchClient is called inside useMemo with i18n as a dependency, this could cause the fetch client to be recreated more often than necessary. Consider using a ref or extracting only the stable parts of i18n needed for error handling.

♻️ Suggested fix using a ref for stable i18n reference
 export const YieldApiClientProvider = ({ children }: PropsWithChildren) => {
   const { apiKey, yieldsApiUrl } = useSettings();
   const { i18n } = useTranslation();
   const url = yieldsApiUrl ?? config.env.yieldsApiUrl;
+  const i18nRef = useRef(i18n);
+  i18nRef.current = i18n;
 
   const clients = useMemo(() => {
-    const fetchClient = createYieldApiFetchClient({ apiKey, i18n, url });
+    const fetchClient = createYieldApiFetchClient({ 
+      apiKey, 
+      i18n: i18nRef.current, 
+      url 
+    });
 
     return {
       fetchClient,
       queryClient: createClient(fetchClient),
     };
-  }, [apiKey, i18n, url]);
+  }, [apiKey, url]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/providers/yield-api-client-provider/index.tsx` around
lines 84 - 91, The useMemo block recreates fetchClient whenever the entire i18n
object reference changes; update createYieldApiFetchClient usage in the useMemo
so it depends only on stable values (e.g., i18n.language or a stable translator
function) or keep i18n in a ref and read from that ref inside error
handlers—specifically, modify the useMemo that calls createYieldApiFetchClient
and createClient so its dependency array uses apiKey, url and a stable i18n
token (or remove i18n and use an i18nRef where i18nRef.current = i18n) to avoid
recreating fetchClient unnecessarily.
packages/widget/src/pages/position-details/hooks/use-position-details.ts-192-200 (1)

192-200: ⚠️ Potential issue | 🟡 Minor

Potential division by zero in share conversion calculation.

While there's a filter for yb.shareAmount && yb.amount (line 192), if yb.amount is "0" (a truthy string), the division on line 197-199 would result in Infinity. Consider adding a numeric check.

🛡️ Proposed fix to guard against zero division
             .filter((yb) => yb.shareAmount && yb.amount && !yb.token.isPoints)
+            .filter((yb) => new BigNumber(yb.amount ?? 0).isGreaterThan(0))
             .forEach((yb) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/pages/position-details/hooks/use-position-details.ts`
around lines 192 - 200, The division can produce Infinity when yb.amount is a
non-empty "0" string; update the conversion logic in use-position-details (the
block that filters over yb and calls acc.set with defaultFormattedNumber(new
BigNumber(yb.shareAmount).dividedBy(new BigNumber(yb.amount)))) to coerce
yb.amount to a numeric value and guard against zero before dividing (e.g.,
parse/convert yb.amount to a Number or BigNumber and skip or set a safe fallback
when it equals 0), ensuring the value passed to defaultFormattedNumber is valid;
keep references to yb.shareAmount, yb.amount, defaultFormattedNumber, BigNumber,
acc.set, and yb.token.symbol/yb.shareToken?.symbol when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ae3c0135-c881-4a62-96ea-2a41e0fe728e

📥 Commits

Reviewing files that changed from the base of the PR and between 0b60e3e and dfbc459.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (223)
  • .changeset/lemon-drinks-prove.md
  • .github/workflows/ci.yml
  • .gitignore
  • AGENTS.md
  • mise.toml
  • package.json
  • packages/widget/.env.test
  • packages/widget/package.json
  • packages/widget/public/mockServiceWorker.js
  • packages/widget/script.ts
  • packages/widget/src/common/check-gas-amount.ts
  • packages/widget/src/common/delay-api-requests.ts
  • packages/widget/src/common/get-gas-mode-value.ts
  • packages/widget/src/common/private-api.ts
  • packages/widget/src/components/atoms/image-fallback/index.tsx
  • packages/widget/src/components/atoms/image-fallback/styles.css.ts
  • packages/widget/src/components/atoms/image/index.tsx
  • packages/widget/src/components/atoms/token-icon/index.tsx
  • packages/widget/src/components/atoms/token-icon/network-icon-image/index.tsx
  • packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx
  • packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts
  • packages/widget/src/components/atoms/token-icon/token-icon-container/index.tsx
  • packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx
  • packages/widget/src/components/atoms/virtual-list/index.tsx
  • packages/widget/src/components/molecules/amount-toggle/index.tsx
  • packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx
  • packages/widget/src/components/molecules/reward-token-details/index.tsx
  • packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx
  • packages/widget/src/components/molecules/select-validator/index.tsx
  • packages/widget/src/components/molecules/select-validator/meta-info.tsx
  • packages/widget/src/components/molecules/select-validator/select-validator-list.tsx
  • packages/widget/src/components/molecules/select-yield/index.tsx
  • packages/widget/src/config/index.ts
  • packages/widget/src/domain/index.ts
  • packages/widget/src/domain/types/action.ts
  • packages/widget/src/domain/types/addresses.ts
  • packages/widget/src/domain/types/errors.ts
  • packages/widget/src/domain/types/fees.ts
  • packages/widget/src/domain/types/init-params.ts
  • packages/widget/src/domain/types/pending-action.ts
  • packages/widget/src/domain/types/positions.ts
  • packages/widget/src/domain/types/price.ts
  • packages/widget/src/domain/types/reward-rate.ts
  • packages/widget/src/domain/types/rewards.ts
  • packages/widget/src/domain/types/settings.ts
  • packages/widget/src/domain/types/stake.ts
  • packages/widget/src/domain/types/token-balance.ts
  • packages/widget/src/domain/types/tokens.ts
  • packages/widget/src/domain/types/transaction.ts
  • packages/widget/src/domain/types/tron.ts
  • packages/widget/src/domain/types/validators.ts
  • packages/widget/src/domain/types/wallets/generic-wallet.ts
  • packages/widget/src/domain/types/yield-api.ts
  • packages/widget/src/domain/types/yields.ts
  • packages/widget/src/hooks/api/use-activity-actions.ts
  • packages/widget/src/hooks/api/use-default-tokens.ts
  • packages/widget/src/hooks/api/use-multi-yields.ts
  • packages/widget/src/hooks/api/use-prices.ts
  • packages/widget/src/hooks/api/use-token-balances-scan.ts
  • packages/widget/src/hooks/api/use-tokens-prices.ts
  • packages/widget/src/hooks/api/use-yield-balances-scan.ts
  • packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts
  • packages/widget/src/hooks/api/use-yield-opportunity/index.ts
  • packages/widget/src/hooks/api/use-yield-validators.ts
  • packages/widget/src/hooks/navigation/use-dashboard-tabs-page-match.ts
  • packages/widget/src/hooks/navigation/use-unstake-or-pending-action-params.ts
  • packages/widget/src/hooks/use-base-token.ts
  • packages/widget/src/hooks/use-estimated-rewards.ts
  • packages/widget/src/hooks/use-force-max-amount.ts
  • packages/widget/src/hooks/use-gas-fee-token.ts
  • packages/widget/src/hooks/use-gas-warning-check.ts
  • packages/widget/src/hooks/use-geo-block.ts
  • packages/widget/src/hooks/use-handle-deep-links.ts
  • packages/widget/src/hooks/use-init-params.ts
  • packages/widget/src/hooks/use-init-query-params.ts
  • packages/widget/src/hooks/use-max-min-yield-amount.ts
  • packages/widget/src/hooks/use-position-balance-by-type.ts
  • packages/widget/src/hooks/use-position-balances.ts
  • packages/widget/src/hooks/use-positions-data.ts
  • packages/widget/src/hooks/use-provider-details.ts
  • packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx
  • packages/widget/src/hooks/use-reward-token-details/index.ts
  • packages/widget/src/hooks/use-rewards-summary.ts
  • packages/widget/src/hooks/use-rich-errors.ts
  • packages/widget/src/hooks/use-staked-or-liquid-balance.ts
  • packages/widget/src/hooks/use-summary.tsx
  • packages/widget/src/hooks/use-under-maintenance.ts
  • packages/widget/src/hooks/use-yield-meta-info.tsx
  • packages/widget/src/hooks/use-yield-type.ts
  • packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx
  • packages/widget/src/pages-dashboard/activity/activity-details.page.tsx
  • packages/widget/src/pages-dashboard/activity/activity.page.tsx
  • packages/widget/src/pages-dashboard/activity/position-balances.tsx
  • packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx
  • packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-trigger.tsx
  • packages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsx
  • packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts
  • packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx
  • packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx
  • packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx
  • packages/widget/src/pages-dashboard/position-details/components/top-header.tsx
  • packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx
  • packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts
  • packages/widget/src/pages/complete/hooks/use-complete.hook.ts
  • packages/widget/src/pages/complete/pages/activity-complete.page.tsx
  • packages/widget/src/pages/complete/pages/common.page.tsx
  • packages/widget/src/pages/complete/pages/pending-complete.page.tsx
  • packages/widget/src/pages/complete/pages/stake-complete.page.tsx
  • packages/widget/src/pages/complete/pages/unstake-complete.page.tsx
  • packages/widget/src/pages/complete/state/index.tsx
  • packages/widget/src/pages/components/meta-info/index.tsx
  • packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx
  • packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx
  • packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts
  • packages/widget/src/pages/details/activity-page/hooks/use-activity-page.tsx
  • packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx
  • packages/widget/src/pages/details/activity-page/types.ts
  • packages/widget/src/pages/details/earn-page/components/extra-args-selection/index.tsx
  • packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx
  • packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx
  • packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx
  • packages/widget/src/pages/details/earn-page/components/select-validator-section/index.tsx
  • packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx
  • packages/widget/src/pages/details/earn-page/components/select-validator-section/use-select-validator.ts
  • packages/widget/src/pages/details/earn-page/components/select-yield-section/index.tsx
  • packages/widget/src/pages/details/earn-page/components/select-yield-section/select-opportunity.tsx
  • packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx
  • packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx
  • packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts
  • packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx
  • packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx
  • packages/widget/src/pages/details/earn-page/state/types.ts
  • packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts
  • packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts
  • packages/widget/src/pages/details/earn-page/state/use-init-token.ts
  • packages/widget/src/pages/details/earn-page/state/use-init-yield.ts
  • packages/widget/src/pages/details/earn-page/state/use-pending-action-deep-link.ts
  • packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts
  • packages/widget/src/pages/details/earn-page/state/use-token-balance.ts
  • packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts
  • packages/widget/src/pages/details/earn-page/state/utils.ts
  • packages/widget/src/pages/details/earn-page/types.ts
  • packages/widget/src/pages/details/positions-page/components/positions-list-item.tsx
  • packages/widget/src/pages/details/positions-page/hooks/use-position-list-item.ts
  • packages/widget/src/pages/details/positions-page/hooks/use-positions.ts
  • packages/widget/src/pages/position-details/components/amount-block.tsx
  • packages/widget/src/pages/position-details/components/position-balances.tsx
  • packages/widget/src/pages/position-details/components/provider-details.tsx
  • packages/widget/src/pages/position-details/components/static-action-block.tsx
  • packages/widget/src/pages/position-details/hooks/use-pending-actions.ts
  • packages/widget/src/pages/position-details/hooks/use-position-details.ts
  • packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts
  • packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts
  • packages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.ts
  • packages/widget/src/pages/position-details/hooks/utils.ts
  • packages/widget/src/pages/position-details/position-details.page.tsx
  • packages/widget/src/pages/position-details/state/index.tsx
  • packages/widget/src/pages/position-details/state/types.ts
  • packages/widget/src/pages/position-details/state/utils.ts
  • packages/widget/src/pages/review/hooks/use-action-review.hook.ts
  • packages/widget/src/pages/review/hooks/use-fees.ts
  • packages/widget/src/pages/review/hooks/use-pending-review.hook.ts
  • packages/widget/src/pages/review/hooks/use-stake-review.hook.ts
  • packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts
  • packages/widget/src/pages/review/pages/action-review.page.tsx
  • packages/widget/src/pages/review/pages/common-page/common.page.tsx
  • packages/widget/src/pages/review/pages/common-page/components/review-top-section.tsx
  • packages/widget/src/pages/review/pages/pending-review.page.tsx
  • packages/widget/src/pages/review/pages/unstake-review.page.tsx
  • packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts
  • packages/widget/src/pages/steps/hooks/use-steps.hook.ts
  • packages/widget/src/pages/steps/pages/activity-steps.page.tsx
  • packages/widget/src/pages/steps/pages/common.page.tsx
  • packages/widget/src/pages/steps/pages/pending-steps.page.tsx
  • packages/widget/src/pages/steps/pages/stake-steps.page.tsx
  • packages/widget/src/pages/steps/pages/tx-state.tsx
  • packages/widget/src/pages/steps/pages/unstake-steps.page.tsx
  • packages/widget/src/providers/activity-provider/index.tsx
  • packages/widget/src/providers/api/get-enabled-networks.ts
  • packages/widget/src/providers/cosmos/config.ts
  • packages/widget/src/providers/enter-stake-store/index.tsx
  • packages/widget/src/providers/ethereum/config.ts
  • packages/widget/src/providers/exit-stake-store/index.tsx
  • packages/widget/src/providers/index.tsx
  • packages/widget/src/providers/misc/solana-connector.ts
  • packages/widget/src/providers/pending-action-store/index.tsx
  • packages/widget/src/providers/settings/types.ts
  • packages/widget/src/providers/sk-wallet/errors.ts
  • packages/widget/src/providers/sk-wallet/index.tsx
  • packages/widget/src/providers/sk-wallet/use-additional-addresses.ts
  • packages/widget/src/providers/substrate/config.ts
  • packages/widget/src/providers/wagmi/index.ts
  • packages/widget/src/providers/yield-api-client-provider/actions.ts
  • packages/widget/src/providers/yield-api-client-provider/index.tsx
  • packages/widget/src/providers/yield-api-client-provider/request-helpers.ts
  • packages/widget/src/providers/yield-api-client-provider/types.ts
  • packages/widget/src/translation/English/translations.json
  • packages/widget/src/translation/French/translations.json
  • packages/widget/src/types/yield-api-schema.d.ts
  • packages/widget/src/utils/formatters.ts
  • packages/widget/src/utils/index.ts
  • packages/widget/src/utils/mappers.ts
  • packages/widget/src/utils/region-iso-3166-codes.ts
  • packages/widget/src/worker.ts
  • packages/widget/tests/components/atoms/image.test.tsx
  • packages/widget/tests/components/atoms/token-icon-image.test.tsx
  • packages/widget/tests/fixtures/index.ts
  • packages/widget/tests/mocks/api-routes.ts
  • packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx
  • packages/widget/tests/use-cases/deep-links-flow/setup.ts
  • packages/widget/tests/use-cases/external-provider/setup.ts
  • packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx
  • packages/widget/tests/use-cases/gas-warning-flow/setup.ts
  • packages/widget/tests/use-cases/geo-block.test.tsx
  • packages/widget/tests/use-cases/renders-initial-page.test.tsx
  • packages/widget/tests/use-cases/select-opportunity.test.tsx
  • packages/widget/tests/use-cases/sk-wallet.test.tsx
  • packages/widget/tests/use-cases/staking-flow/setup.ts
  • packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx
  • packages/widget/tests/use-cases/trust-incentive-apy/setup.ts
  • packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx
  • packages/widget/tests/use-cases/under-maintenance.test.tsx
  • packages/widget/vite/vite.config.base.ts
💤 Files with no reviewable changes (7)
  • packages/widget/src/hooks/navigation/use-dashboard-tabs-page-match.ts
  • packages/widget/src/components/atoms/image-fallback/styles.css.ts
  • packages/widget/src/hooks/use-base-token.ts
  • packages/widget/src/components/atoms/image-fallback/index.tsx
  • packages/widget/src/common/get-gas-mode-value.ts
  • packages/widget/src/hooks/use-gas-fee-token.ts
  • packages/widget/script.ts

Comment thread packages/widget/src/components/atoms/image/index.tsx
Comment on lines 39 to 43
const tokenMappingResult = Maybe.fromNullable(tokenIconMapping)
.chainNullable((mapping) => {
if (typeof mapping === "function") {
return mapping(token);
return mapping(token as Parameters<typeof mapping>[0]);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tokenIconMapping type and settings contract =="
rg -n -C4 '\btokenIconMapping\b' packages/widget/src

echo
echo "== TokenDto / YieldTokenDto definitions =="
fd 'tokens.ts' packages/widget/src/domain/types -x sed -n '1,240p' {}

echo
echo "== Hook implementation =="
fd 'use-variant-token-urls.ts' packages/widget/src -x sed -n '1,140p' {}

Repository: stakekit/widget

Length of output: 7550


Update the settings contract to reflect that tokenIconMapping callbacks must handle both TokenDto and YieldTokenDto.

The cast at Line 42 (mapping(token as Parameters<typeof mapping>[0])) hides a type mismatch: the settings type defines tokenIconMapping callbacks to accept only TokenDto, but the hook now accepts TokenDto | YieldTokenDto. Integrators' callbacks written before YieldTokenDto support may fail at runtime when passed a yield token. Either widen the callback type in the settings contract to (token: TokenDto | YieldTokenDto) => string, or normalize the token shape before calling the callback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts`
around lines 39 - 43, The tokenIconMapping callback is currently typed to accept
only TokenDto but use-variant-token-urls now passes TokenDto | YieldTokenDto
(see Maybe.fromNullable(...).chainNullable and the mapping(token as
Parameters<typeof mapping>[0]) cast); remove this unsafe cast by either widening
the settings contract so tokenIconMapping is declared as (token: TokenDto |
YieldTokenDto) => string, or normalize/convert a YieldTokenDto to the TokenDto
shape inside useVariantTokenUrls before calling mapping; update the
tokenIconMapping type in the settings contract and any call sites (or implement
a small normalizer in useVariantTokenUrls) so callbacks always receive the
declared token shape without runtime type mismatches.

Comment on lines 17 to +19
<Image
data-rk="token-logo"
containerProps={{ hw: tokenLogoHw, "data-rk": "token-logo" }}
wrapperProps={{ hw: tokenLogoHw, "data-rk": "token-logo" }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find packages/widget/src/components/atoms -name "*.tsx" -o -name "*.ts" | grep -E "(token-icon-image|image)" | head -20

Repository: stakekit/widget

Length of output: 344


🏁 Script executed:

cat -n packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx

Repository: stakekit/widget

Length of output: 757


🏁 Script executed:

cat -n packages/widget/src/components/atoms/image/index.tsx

Repository: stakekit/widget

Length of output: 2347


🏁 Script executed:

rg "Image" packages/widget/src/components/atoms -A 5 -B 1 | grep -E "(data-rk|Image|wrapperProps)" | head -40

Repository: stakekit/widget

Length of output: 3604


🏁 Script executed:

fd "tsconfig" packages/widget | head -5

Repository: stakekit/widget

Length of output: 125


🏁 Script executed:

cat packages/widget/tsconfig.json | head -30

Repository: stakekit/widget

Length of output: 276


🏁 Script executed:

cat /dev/null | head -0 && cat ../../tsconfig.json | grep -E "(strict|jsx)" | head -10

Repository: stakekit/widget

Length of output: 111


🏁 Script executed:

rg "data-rk" packages/widget/src/components/atoms/token-icon-image -A 3 -B 3

Repository: stakekit/widget

Length of output: 153


🏁 Script executed:

cat tsconfig.json | grep -E "(strict|jsx)"

Repository: stakekit/widget

Length of output: 103


🏁 Script executed:

rg "data-rk" packages/widget/src/components/atoms/token-icon -A 2 -B 2

Repository: stakekit/widget

Length of output: 1247


🏁 Script executed:

rg "export const Image" packages/widget/src/components/atoms/image -A 20 | head -30

Repository: stakekit/widget

Length of output: 1613


🏁 Script executed:

# Check if there are any component usages with extraneous props on Image
rg "<Image" packages/widget/src -A 5 | grep -E "(<Image|data-rk)" | head -20

Repository: stakekit/widget

Length of output: 1570


Remove the extraneous data-rk prop from <Image>.

The prop on line 18 is not part of ImageProps and will fail strict TypeScript type-checking. The same attribute is already defined in wrapperProps on line 19.

Suggested fix
   <Image
-    data-rk="token-logo"
     wrapperProps={{ hw: tokenLogoHw, "data-rk": "token-logo" }}
     imgProps={{ hw: tokenLogoHw }}
     src={mainUrl ?? fallbackUrl}
     fallbackName={name}
   />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx`
around lines 17 - 19, Remove the extraneous top-level prop named `data-rk`
passed to the `Image` component (the `Image` JSX in token-icon-image/index.tsx);
it is not part of `ImageProps` and duplicates the same `data-rk` already present
in `wrapperProps`. Edit the `Image` element to drop the `data-rk` attribute so
only `wrapperProps={{ hw: tokenLogoHw, "data-rk": "token-logo" }}` remains,
ensuring TypeScript strict props validation passes.

Comment thread packages/widget/src/components/atoms/virtual-list/index.tsx Outdated
Comment on lines +10 to +11
const getTokenGetTokensQueryKey = (params?: TokenGetTokensParams) =>
["/v1/tokens", ...(params ? [params] : [])] as const;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'getTokenGetTokensQueryKey|enabledYieldsOnly|queryKey:' packages/widget/src/hooks/api/use-default-tokens.ts -C2

Repository: stakekit/widget

Length of output: 1040


🏁 Script executed:

cat -n packages/widget/src/hooks/api/use-default-tokens.ts

Repository: stakekit/widget

Length of output: 2290


Include enabledYieldsOnly in the query key to avoid stale cache on setting changes.

The queryFn depends on enabledYieldsOnly (line 23), but the query key only varies by network. When toggling the tokensForEnabledYieldsOnly setting, the cache key remains identical, causing stale data to be returned. This affects both useDefaultTokens (line 18) and getDefaultTokens (line 35–37).

Proposed fix
-const getTokenGetTokensQueryKey = (params?: TokenGetTokensParams) =>
+const getTokenGetTokensQueryKey = (
+  params?: Pick<TokenGetTokensParams, "network" | "enabledYieldsOnly">
+) =>
   ["/v1/tokens", ...(params ? [params] : [])] as const;

   return useQuery({
-    queryKey: getTokenGetTokensQueryKey({ network: network ?? undefined }),
+    queryKey: getTokenGetTokensQueryKey({
+      network: network ?? undefined,
+      enabledYieldsOnly: !!tokensForEnabledYieldsOnly || undefined,
+    }),
     queryFn: async () =>
       (
         await queryFn({
           network: network ?? undefined,
           enabledYieldsOnly: !!tokensForEnabledYieldsOnly,
         })
       ).unsafeCoerce(),
@@
       queryKey: getTokenGetTokensQueryKey({
         network: params.network ?? undefined,
+        enabledYieldsOnly: params.enabledYieldsOnly || undefined,
       }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/hooks/api/use-default-tokens.ts` around lines 10 - 11,
The query key generator getTokenGetTokensQueryKey currently only includes
TokenGetTokensParams (network) so toggling tokensForEnabledYieldsOnly
(enabledYieldsOnly) doesn't change the key and returns stale cache; update
getTokenGetTokensQueryKey to include enabledYieldsOnly (or accept a params shape
that contains enabledYieldsOnly) so the key becomes ["/v1/tokens", params?,
enabledYieldsOnly] (or equivalent), and update both callers useDefaultTokens and
getDefaultTokens to pass the enabledYieldsOnly value into the key generation so
the query key varies when the setting changes.

}) => {
const { t } = useTranslation();

const nameOrAddress = providerDetails.name ?? providerDetails ?? "";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: providerDetails is an object, not a string.

The fallback providerDetails.name ?? providerDetails will result in [object Object] being used as nameOrAddress when name is undefined, since providerDetails is the destructured props object.

This should likely be:

🐛 Proposed fix
-  const nameOrAddress = providerDetails.name ?? providerDetails ?? "";
+  const nameOrAddress = providerDetails.name ?? providerDetails.address ?? "";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const nameOrAddress = providerDetails.name ?? providerDetails ?? "";
const nameOrAddress = providerDetails.name ?? providerDetails.address ?? "";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/pages/position-details/components/provider-details.tsx`
at line 42, The current assignment to nameOrAddress uses providerDetails
directly (const nameOrAddress = providerDetails.name ?? providerDetails ?? "";),
but providerDetails is an object so that falls back to "[object Object]"; change
the fallback to a scalar property (e.g., use providerDetails.address or
providerDetails.id) instead of the whole object — update the expression to
something like providerDetails.name ?? providerDetails.address ?? "" so
nameOrAddress is always a string; locate the constant nameOrAddress in
provider-details.tsx and replace the fallback accordingly.

Comment on lines +36 to +45
const actionPreviewQuery = useQuery({
enabled: !!exitRequest,
queryKey: ["unstake-review-action-preview", exitRequest.requestDto],
retry: false,
queryFn: () =>
createExitAction({
fetchClient: yieldApiFetchClient,
requestDto: exitRequest.requestDto,
}),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle action-preview failures explicitly.

This new preview query drives both stakeExitTxGas and the gas warning state, but its error path is ignored. If createExitAction fails, stakeExitTxGas becomes empty, gasCheckLoading flips to false, and the review can proceed with no fee/warning data instead of surfacing the failure.

Also applies to: 47-59, 185-188

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts` around
lines 36 - 45, The action-preview query (actionPreviewQuery) that calls
createExitAction currently ignores errors, which leaves stakeExitTxGas empty and
gasCheckLoading false so the review can proceed without fee/warning data; update
the queryFn/error handling to explicitly handle failures by capturing the error
(query's onError or error state), set a distinct error flag/state (e.g.,
actionPreviewError) and ensure stakeExitTxGas and gasCheckLoading reflect the
failed state (prevent proceeding or show an explicit error UI/message), and
propagate that error into any dependent logic that reads stakeExitTxGas or
gasCheckLoading (also apply the same explicit error handling pattern to the
other similar queries/usages referenced by the comment).

Comment on lines +111 to +116
if (
recentConnectorId &&
recentConnectorId === solanaWallet.adapter.name
) {
await solanaWallet.adapter.autoConnect();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In @solana/wallet-adapter(the version used in this repo), canautoConnect()reject, and is catching errors recommended for non-throwing auth checks likeisAuthorized?

💡 Result:

Yes, autoConnect can reject with a Promise that may throw WalletError subclasses (e.g., WalletNotConnectedError, WalletNotSelectedError, WalletConnectionError) based on wallet state and availability. Catching errors is recommended even for non-throwing auth checks like wallet connection status, as useWallet provides connected and publicKey booleans/states that are synchronous and reliable for authorization without async calls; however, any manual connect or autoConnect calls (e.g., in custom logic) should be wrapped in try-catch to handle uncaught promise rejections, as seen in GitHub issues where errors like WalletNotSelectedError occur without handling.

Citations:


🏁 Script executed:

cat -n packages/widget/src/providers/misc/solana-connector.ts | head -150 | tail -60

Repository: stakekit/widget

Length of output: 2131


Guard autoConnect() errors in isAuthorized

autoConnect() can reject with WalletError subclasses. Unhandled rejection causes isAuthorized to throw instead of returning false, breaking the reconnect/auth probe flow. Wrap the call in try-catch and fail closed.

Suggested fix
       if (
         recentConnectorId &&
         recentConnectorId === solanaWallet.adapter.name
       ) {
-        await solanaWallet.adapter.autoConnect();
+        if (!solanaWallet.adapter.connected) {
+          try {
+            await solanaWallet.adapter.autoConnect();
+          } catch {
+            return false;
+          }
+        }
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
recentConnectorId &&
recentConnectorId === solanaWallet.adapter.name
) {
await solanaWallet.adapter.autoConnect();
}
if (
recentConnectorId &&
recentConnectorId === solanaWallet.adapter.name
) {
if (!solanaWallet.adapter.connected) {
try {
await solanaWallet.adapter.autoConnect();
} catch {
return false;
}
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/widget/src/providers/misc/solana-connector.ts` around lines 111 -
116, In isAuthorized, guard the call to solanaWallet.adapter.autoConnect() so
any rejection (WalletError subclasses) is caught and handled: wrap the await
solanaWallet.adapter.autoConnect() invocation in a try/catch, return false (fail
closed) when an error is caught and optionally log the error; keep the existing
logic that only attempts autoConnect when recentConnectorId && recentConnectorId
=== solanaWallet.adapter.name.

Comment thread packages/widget/tests/fixtures/index.ts Outdated
Comment thread packages/widget/tests/use-cases/trust-incentive-apy/setup.ts
@petar-omni petar-omni force-pushed the feat/yield-balances-migration branch from dfbc459 to bf2f956 Compare April 28, 2026 17:09
Simplify the widget Vite config by dropping the virtual worker_threads
stub and the build-only plugin that rewrote dynamic imports for React
Router warning suppression.
Surface API risk ratings with tone-coded badges in the opportunity list,
a summary block on the earn page, and EN/FR copy for sources and tooltips.
@petar-omni petar-omni force-pushed the feat/yield-balances-migration branch from b3bd8a9 to 9498eb1 Compare May 18, 2026 13:59
@petar-omni petar-omni requested a review from Philippoes May 18, 2026 22:32
@petar-omni petar-omni requested review from dnehl and jdomingos May 19, 2026 11:10
@petar-omni
Copy link
Copy Markdown
Contributor Author

@coderabbitai update PR description

@petar-omni petar-omni changed the title Feat: yield balances migration feat(widget): migrate data flows to Yield API May 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@petar-omni I've updated the PR description with a comprehensive summary of all the changes in this PR. It covers the Yield API migration, domain type decoupling, new YieldApiClientProvider, image component refactoring, reward rate breakdown, test infrastructure updates, tooling changes, and more.


🏁 Script executed:

#!/bin/bash
gh pr edit 516 --repo stakekit/widget --body "## Overview

This PR migrates the widget to use the new Yield API for balances, validators, and action management. It introduces a dedicated \`YieldApiClientProvider\` backed by \`openapi-fetch\`, decouples domain types from \`@stakekit/api-hooks\`, and updates the full data flow from yield discovery through staking, unstaking, and pending actions.

---

## Key Changes

### 🆕 Yield API Client Provider
- Introduces \`YieldApiClientProvider\` with \`createYieldApiFetchClient\` (openapi-fetch-based) and a react-query \`useYieldApiClient\` wrapper.
- Registers \`onResponse\` hooks for geo-block detection, rich error handling, and delayed request gating.
- Exposes \`useYieldApiFetchClient\` and \`useYieldApiClient\` hooks; throws if used outside the provider.
- Added \`VITE_YIELDS_API_URL\` env variable (falls back to \`https://api.yield.xyz\`).

### 🔄 Yield Balances Migration
- Replaces \`yieldYieldBalancesScan\` (POST \`/v1/yields/balances/scan\`) with the new \`POST /v1/yields/balances\` endpoint via the Yield API client.
- Query invalidation now uses a static key (\`["post", "/v1/yields/balances"]\`).
- Drops \`additionalAddresses\` from the balances scan request.

### 🗂️ Domain Type Decoupling
- Introduces a new \`Yield\` domain type (wrapping the Yield API schema yield + a \`__fallback__\` for legacy compatibility) that replaces \`YieldDto\` from \`@stakekit/api-hooks\` across the codebase.
- New domain type modules added: \`action\`, \`addresses\`, \`errors\`, \`fees\`, \`pending-action\`, \`positions\`, \`price\`, \`reward-rate\`, \`settings\`, \`stake\`, \`token-balance\`, \`tokens\`, \`transaction\`, \`tron\`, \`validators\`, \`yield-api\`, \`yields\`.
- \`TokenDto\` is now a union of the legacy DTO and the new Yield API \`TokenDto\`.

### 🔁 Yield Opportunity & Validators
- \`getYieldOpportunity\` now concurrently fetches from both \`GET /v1/yields/{yieldId}\` (new) and the legacy endpoint, merging results with \`__fallback__\`.
- New \`useYieldValidators\` hook: fetches all validators via paginated \`GET /v1/yields/{yieldId}/validators\` (limit 100), with \`filterValidators\` support.
- Replaces \`validatorsConfig\` with \`yieldApiFetchClient\` throughout wagmi config, init hooks, and opportunity hooks.

### ⚡ Action Preview & Steps
- Stake, unstake, and pending-action review hooks now use \`createEnterAction\` / \`createExitAction\` / \`createManageAction\` (Yield API fetch client) for action previews, replacing previous gas estimation hooks.
- Gas totals are derived by decoding per-transaction \`gasEstimate\` from the preview payload.
- \`useStepsMachine\` now uses \`yieldId\` (was \`integrationId\`) and submits transactions via \`submitTransaction\` / \`submitTransactionHash\` from the Yield API client.
- Request DTOs migrated: \`integrationId\`/\`args\` → \`yieldId\`/\`arguments\`; \`addresses\` field added explicitly.

### 🖼️ Image Component Refactor
- \`Image\` component simplified: always renders an \`<img>\` with an inline SVG monogram data-URL as fallback (via \`fallbackName\` prop), removing the previous timeout-based fallback and \`ImageFallback\` component.
- \`ImageFallback\` component deleted; all usages across validators, providers, and token icons updated to use \`fallbackName\`.
- \`Image\` prop names updated: \`containerProps\`→\`wrapperProps\`, \`imageProps\`→\`imgProps\`.

### 📊 Reward Rate Breakdown
- New \`RewardRateBreakdown\` component renders APY composition (native / protocol incentive / campaign) from \`YieldRewardRateDto\`.
- New \`domain/types/reward-rate.ts\` with helpers: \`getRewardRateBreakdown\`, \`getYieldRewardRateDetails\`, \`getRewardTypeFromRateType\`.
- Position details page now shows personalized APY and APY composition breakdown.

### 🧪 Test Infrastructure
- New test fixtures: \`yieldApiYieldFixture\`, \`yieldBalanceFixture\`, \`yieldValidatorsFixture\`, \`yieldApiActionFixture\`, \`yieldApiTransactionFixture\`, etc.
- New \`legacyApiRoute\` / \`yieldApiRoute\` route builder helpers for MSW mocks.
- All use-case test setups updated to use Yield API-shaped responses and new mock endpoints (\`/v1/networks\`, \`/v1/yields/:id/validators\`, \`/v1/yields/balances\`, \`/v1/transactions/:id\`).
- New test suite: \`trust-incentive-apy\` covering APY composition and personalized reward rate UI.
- New unit tests for \`Image\` and \`TokenIconImage\` components.
- Added \`packages/widget/.env.test\` with \`VITE_API_URL\`, \`VITE_YIELDS_API_URL\`, and \`VITE_API_KEY\`.

### 🛠️ Tooling & CI
- \`mise.toml\`: Node pinned to \`24.15.0\`, pnpm to \`10.33.2\` (via \`npm:pnpm\`).
- CI and release workflows: replaced \`jdx/mise-action@v2\` with inline \`curl | sh\` mise install.
- \`package.json\`: \`@biomejs/biome\` bumped to \`^2.4.8\`, \`packageManager\` updated to \`pnpm@10.33.2\`.
- Added \`AGENTS.md\` documenting monorepo conventions, i18n guidelines, and agent tooling.
- Added \`opensrc/\` to \`.gitignore\`.
- Added \`gen:yield-api\` script to download and generate TypeScript from the Yield API OpenAPI spec.

### 🔑 Other Notable Changes
- New \`prepareLedgerLiveTransaction\` utility for EVM, Polkadot, Tron, Near, Tezos, Ton, and Cosmos Ledger Live transaction encoding.
- \`handleGeoBlockResponse\` and \`handleRichErrorResponse\` extracted as testable exports.
- \`VirtualList\` / \`GroupedVirtualList\`: removed \`measureElement\` ref from virtualizer row containers.
- \`waitForDelayedApiRequests\` exported from \`delay-api-requests.ts\`.
- i18n: new translation keys for APY composition, personalized APY, new balance states (\`active\`, \`entering\`, \`exiting\`, \`withdrawable\`, \`claimable\`), and auto-sweep reward actions (EN + FR).
- \`APToPercentage\` now formats via shared \`formatNumber\` utility.
- \`getPositionTotalAmount\` updated to aggregate \`amountUsd\` directly from balance items instead of using \`pricePerShare\`-based conversion.

---

## Changeset

\`\`\`
`@stakekit/widget` — patch: feat: migrate to yield api balances
\`\`\`"

Length of output: 573


@stakekit/widget — patch: feat: migrate to yield api balances


payload = json.dumps({"body": body})

result = subprocess.run(
    ["gh", "api", "--method", "PATCH", "/repos/stakekit/widget/pulls/516",
     "--input", "-"],
    input=payload,
    capture_output=True,
    text=True
)
print("STDOUT:", result.stdout[:500] if result.stdout else "(empty)")
print("STDERR:", result.stderr[:500] if result.stderr else "(empty)")
print("Return code:", result.returncode)
PYEOF

🏁 Script executed:

#!/bin/bash
python3 << 'PYEOF'
import subprocess, json

body = """## Overview

This PR migrates the widget to use the new Yield API for balances, validators, and action management. It introduces a dedicated `YieldApiClientProvider` backed by `openapi-fetch`, decouples domain types from `@stakekit/api-hooks`, and updates the full data flow from yield discovery through staking, unstaking, and pending actions.

---

## Key Changes

### 🆕 Yield API Client Provider
- Introduces `YieldApiClientProvider` with `createYieldApiFetchClient` (openapi-fetch-based) and a react-query `useYieldApiClient` wrapper.
- Registers `onResponse` hooks for geo-block detection, rich error handling, and delayed request gating.
- Exposes `useYieldApiFetchClient` and `useYieldApiClient` hooks; throws if used outside the provider.
- Added `VITE_YIELDS_API_URL` env variable (falls back to `https://api.yield.xyz`).

### 🔄 Yield Balances Migration
- Replaces `yieldYieldBalancesScan` (`POST /v1/yields/balances/scan`) with the new `POST /v1/yields/balances` endpoint via the Yield API client.
- Query invalidation now uses a static key (`["post", "/v1/yields/balances"]`).
- Drops `additionalAddresses` from the balances scan request.

### 🗂️ Domain Type Decoupling
- Introduces a new `Yield` domain type (wrapping the Yield API schema yield + a `__fallback__` for legacy compatibility) that replaces `YieldDto` from `@stakekit/api-hooks` across the codebase.
- New domain type modules added: `action`, `addresses`, `errors`, `fees`, `pending-action`, `positions`, `price`, `reward-rate`, `settings`, `stake`, `token-balance`, `tokens`, `transaction`, `tron`, `validators`, `yield-api`, `yields`.
- `TokenDto` is now a union of the legacy DTO and the new Yield API `TokenDto`.

### 🔁 Yield Opportunity & Validators
- `getYieldOpportunity` now concurrently fetches from both `GET /v1/yields/{yieldId}` (new) and the legacy endpoint, merging results with `__fallback__`.
- New `useYieldValidators` hook: fetches all validators via paginated `GET /v1/yields/{yieldId}/validators` (limit 100), with `filterValidators` support.
- Replaces `validatorsConfig` with `yieldApiFetchClient` throughout wagmi config, init hooks, and opportunity hooks.

### ⚡ Action Preview & Steps
- Stake, unstake, and pending-action review hooks now use `createEnterAction` / `createExitAction` / `createManageAction` (Yield API fetch client) for action previews, replacing previous gas estimation hooks.
- Gas totals are derived by decoding per-transaction `gasEstimate` from the preview payload.
- `useStepsMachine` now uses `yieldId` (was `integrationId`) and submits transactions via `submitTransaction` / `submitTransactionHash` from the Yield API client.
- Request DTOs migrated: `integrationId`/`args` → `yieldId`/`arguments`; `addresses` field added explicitly.

### 🖼️ Image Component Refactor
- `Image` component simplified: always renders an `<img>` with an inline SVG monogram data-URL as fallback (via `fallbackName` prop), removing the previous timeout-based fallback and `ImageFallback` component.
- `ImageFallback` component deleted; all usages across validators, providers, and token icons updated to use `fallbackName`.
- `Image` prop names updated: `containerProps` → `wrapperProps`, `imageProps` → `imgProps`.

### 📊 Reward Rate Breakdown
- New `RewardRateBreakdown` component renders APY composition (native / protocol incentive / campaign) from `YieldRewardRateDto`.
- New `domain/types/reward-rate.ts` with helpers: `getRewardRateBreakdown`, `getYieldRewardRateDetails`, `getRewardTypeFromRateType`.
- Position details page now shows personalized APY and APY composition breakdown.

### 🧪 Test Infrastructure
- New test fixtures: `yieldApiYieldFixture`, `yieldBalanceFixture`, `yieldValidatorsFixture`, `yieldApiActionFixture`, `yieldApiTransactionFixture`, etc.
- New `legacyApiRoute` / `yieldApiRoute` route builder helpers for MSW mocks.
- All use-case test setups updated to use Yield API-shaped responses and new mock endpoints (`/v1/networks`, `/v1/yields/:id/validators`, `/v1/yields/balances`, `/v1/transactions/:id`).
- New test suite: `trust-incentive-apy` covering APY composition and personalized reward rate UI.
- New unit tests for `Image` and `TokenIconImage` components.
- Added `packages/widget/.env.test` with `VITE_API_URL`, `VITE_YIELDS_API_URL`, and `VITE_API_KEY`.

### 🛠️ Tooling & CI
- `mise.toml`: Node pinned to `24.15.0`, pnpm to `10.33.2` (via `npm:pnpm`).
- CI and release workflows: replaced `jdx/mise-action@v2` with inline `curl | sh` mise install.
- `package.json`: `@biomejs/biome` bumped to `^2.4.8`, `packageManager` updated to `pnpm@10.33.2`.
- Added `AGENTS.md` documenting monorepo conventions, i18n guidelines, and agent tooling.
- Added `opensrc/` to `.gitignore`.
- Added `gen:yield-api` script to download and generate TypeScript from the Yield API OpenAPI spec.

### 🔑 Other Notable Changes
- New `prepareLedgerLiveTransaction` utility for EVM, Polkadot, Tron, Near, Tezos, Ton, and Cosmos Ledger Live transaction encoding.
- `handleGeoBlockResponse` and `handleRichErrorResponse` extracted as testable exports.
- `VirtualList` / `GroupedVirtualList`: removed `measureElement` ref from virtualizer row containers.
- `waitForDelayedApiRequests` exported from `delay-api-requests.ts`.
- i18n: new translation keys for APY composition, personalized APY, new balance states (`active`, `entering`, `exiting`, `withdrawable`, `claimable`), and auto-sweep reward actions (EN + FR).
- `APToPercentage` now formats via shared `formatNumber` utility.
- `getPositionTotalAmount` updated to aggregate `amountUsd` directly from balance items instead of using `pricePerShare`-based conversion.

---

## Changeset

Length of output: 236


🏁 Script executed:

#!/bin/bash
python3 -c "
import json

body = '## Overview\n\nThis PR migrates the widget to use the new Yield API for balances, validators, and action management. It introduces a dedicated \`YieldApiClientProvider\` backed by \`openapi-fetch\`, decouples domain types from \`@stakekit/api-hooks\`, and updates the full data flow from yield discovery through staking, unstaking, and pending actions.\n\n---\n\n## Key Changes\n\n### New: Yield API Client Provider\n- Introduces \`YieldApiClientProvider\` with \`createYieldApiFetchClient\` (openapi-fetch-based) and a react-query \`useYieldApiClient\` wrapper.\n- Registers \`onResponse\` hooks for geo-block detection, rich error handling, and delayed request gating.\n- Exposes \`useYieldApiFetchClient\` and \`useYieldApiClient\` hooks; throws if used outside the provider.\n- Added \`VITE_YIELDS_API_URL\` env variable (falls back to \`https://api.yield.xyz\`).\n\n### Yield Balances Migration\n- Replaces \`yieldYieldBalancesScan\` (\`POST /v1/yields/balances/scan\`) with the new \`POST /v1/yields/balances\` endpoint via the Yield API client.\n- Query invalidation now uses a static key (\`[\"post\", \"/v1/yields/balances\"]\`).\n- Drops \`additionalAddresses\` from the balances scan request.\n\n### Domain Type Decoupling\n- Introduces a new \`Yield\` domain type (wrapping the Yield API schema yield + a \`__fallback__\` for legacy compatibility) that replaces \`YieldDto\` from \`@stakekit/api-hooks\` across the codebase.\n- New domain type modules: \`action\`, \`addresses\`, \`errors\`, \`fees\`, \`pending-action\`, \`positions\`, \`price\`, \`reward-rate\`, \`settings\`, \`stake\`, \`token-balance\`, \`tokens\`, \`transaction\`, \`tron\`, \`validators\`, \`yield-api\`, \`yields\`.\n- \`TokenDto\` is now a union of the legacy DTO and the new Yield API \`TokenDto\`.\n\n### Yield Opportunity & Validators\n- \`getYieldOpportunity\` now concurrently fetches from both \`GET /v1/yields/{yieldId}\` (new) and the legacy endpoint, merging results with \`__fallback__\`.\n- New \`useYieldValidators\` hook: fetches all validators via paginated \`GET /v1/yields/{yieldId}/validators\` (limit 100), with \`filterValidators\` support.\n- Replaces \`validatorsConfig\` with \`yieldApiFetchClient\` throughout wagmi config, init hooks, and opportunity hooks.\n\n### Action Preview & Steps\n- Stake, unstake, and pending-action review hooks now use \`createEnterAction\` / \`createExitAction\` / \`createManageAction\` for action previews, replacing previous gas estimation hooks.\n- Gas totals are derived by decoding per-transaction \`gasEstimate\` from the preview payload.\n- \`useStepsMachine\` now uses \`yieldId\` (was \`integrationId\`) and submits via \`submitTransaction\` / \`submitTransactionHash\`.\n- Request DTOs migrated: \`integrationId\`/\`args\` to \`yieldId\`/\`arguments\`; \`addresses\` field added explicitly.\n\n### Image Component Refactor\n- \`Image\` component simplified: always renders an \`<img>\` with an inline SVG monogram data-URL as fallback (via \`fallbackName\` prop).\n- \`ImageFallback\` component deleted; all usages updated to use \`fallbackName\`.\n- \`Image\` prop names updated: \`containerProps\` to \`wrapperProps\`, \`imageProps\` to \`imgProps\`.\n\n### Reward Rate Breakdown\n- New \`RewardRateBreakdown\` component renders APY composition (native / protocol incentive / campaign) from \`YieldRewardRateDto\`.\n- New \`domain/types/reward-rate.ts\` with helpers: \`getRewardRateBreakdown\`, \`getYieldRewardRateDetails\`, \`getRewardTypeFromRateType\`.\n- Position details page now shows personalized APY and APY composition breakdown.\n\n### Test Infrastructure\n- New test fixtures: \`yieldApiYieldFixture\`, \`yieldBalanceFixture\`, \`yieldValidatorsFixture\`, \`yieldApiActionFixture\`, \`yieldApiTransactionFixture\`.\n- New \`legacyApiRoute\` / \`yieldApiRoute\` helpers for MSW mocks.\n- All use-case test setups updated to use Yield API-shaped responses and new mock endpoints.\n- New test suite: \`trust-incentive-apy\` covering APY composition and personalized reward rate UI.\n- New unit tests for \`Image\` and \`TokenIconImage\` components.\n- Added \`packages/widget/.env.test\` with test environment variables.\n\n### Tooling & CI\n- \`mise.toml\`: Node pinned to \`24.15.0\`, pnpm to \`10.33.2\` (via \`npm:pnpm\`).\n- CI and release workflows: replaced \`jdx/mise-action@v2\` with inline curl-based mise install.\n- \`package.json\`: \`@biomejs/biome\` bumped to \`^2.4.8\`, \`packageManager\` updated to \`pnpm@10.33.2\`.\n- Added \`AGENTS.md\` documenting monorepo conventions, i18n guidelines, and agent tooling.\n- Added \`opensrc/\` to \`.gitignore\`.\n- Added \`gen:yield-api\` script to generate TypeScript from the Yield API OpenAPI spec.\n\n### Other Notable Changes\n- New \`prepareLedgerLiveTransaction\` utility for EVM, Polkadot, Tron, Near, Tezos, Ton, and Cosmos Ledger Live transaction encoding.\n- \`handleGeoBlockResponse\` and \`handleRichErrorResponse\` extracted as testable exports.\n- \`VirtualList\` / \`GroupedVirtualList\`: removed \`measureElement\` ref from row containers.\n- \`waitForDelayedApiRequests\` exported from \`delay-api-requests.ts\`.\n- i18n: new keys for APY composition, personalized APY, balance states (\`active\`, \`entering\`, \`exiting\`, \`withdrawable\`, \`claimable\`), and auto-sweep reward actions (EN + FR).\n- \`APToPercentage\` now formats via shared \`formatNumber\` utility.\n- \`getPositionTotalAmount\` updated to aggregate \`amountUsd\` directly from balance items.\n\n---\n\n## Changeset\n\n\`@stakekit/widget\` — patch: feat: migrate to yield api balances'

with open('/tmp/pr_body.json', 'w') as f:
    json.dump({'body': body}, f)
print('Written to /tmp/pr_body.json')
"

gh api --method PATCH /repos/stakekit/widget/pulls/516 --input /tmp/pr_body.json | python3 -c "import sys,json; d=json.load(sys.stdin); print('Updated PR:', d.get('number'), '-', d.get('title'))"

Length of output: 78


@petar-omni The PR description has been successfully updated! Here's a summary of what was added:

  • Overview — high-level summary of the migration goals
  • Yield API Client Provider — new YieldApiClientProvider, createYieldApiFetchClient, and related hooks
  • Yield Balances Migration — endpoint change from /v1/yields/balances/scan/v1/yields/balances
  • Domain Type Decoupling — new Yield type, TokenDto union, and all new domain type modules
  • Yield Opportunity & Validators — concurrent fetching, new useYieldValidators hook, validatorsConfigyieldApiFetchClient
  • Action Preview & Steps — preview-based gas estimation, yieldId migration, DTO shape changes
  • Image Component RefactorfallbackName-based SVG monogram, removal of ImageFallback
  • Reward Rate Breakdown — new RewardRateBreakdown component and APY composition display
  • Test Infrastructure — new fixtures, route helpers, trust-incentive-apy test suite
  • Tooling & CI — Node/pnpm version pins, mise install, AGENTS.md, gen:yield-api script
  • Other Notable Changes — Ledger Live tx preparation, i18n keys, getPositionTotalAmount update, etc.

Comment thread repos/effect/packages/effect/src/unstable/httpapi/HttpApiScalar.ts Fixed
Comment thread repos/effect/packages/effect/src/unstable/httpapi/HttpApiScalar.ts Fixed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants