Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 4ae72e9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughMigrates the widget from internal APIs to a new Yield API client provider, adds Yield API types and provider, expands token types to include YieldTokenDto, refactors balance keying and USD computations, updates pending-action handling, and adjusts providers, UI, translations, and tests. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Client/UI
participant Hook as useYieldBalancesScan hook
participant Provider as YieldApiClientProvider
participant QueryClient as OpenapiQueryClient
participant FetchClient as OpenapiFetchClient
UI->>Hook: call useYieldBalancesScan(opts)
Hook->>Provider: useYieldApiClient() / useYieldApiFetchClient()
Provider->>QueryClient: provide query client (memoized)
Provider->>FetchClient: provide fetch client (memoized)
Hook->>QueryClient: query POST /v1/yields/balances (request DTO)
QueryClient->>FetchClient: perform HTTP POST (yieldsApiUrl + apiKey)
FetchClient-->>QueryClient: return response (YieldBalancesByYieldDto[])
QueryClient-->>Hook: return cached/memoized data
Hook-->>UI: selected/normalized data (via select or default)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/widget/src/providers/sk-wallet/index.tsx (1)
331-334:⚠️ Potential issue | 🟠 MajorNon-string provider errors are being discarded instead of chained.
This branch now loses rich error context (
Error, RPC error objects) by converting toundefinedunless it is a string. Pass the original value ascause.Suggested patch
- .mapLeft( - (e) => - new SendTransactionError( - typeof e === "string" ? e : undefined - ) - ) + .mapLeft((e) => new SendTransactionError(e))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/widget/src/providers/sk-wallet/index.tsx` around lines 331 - 334, The error mapping currently loses non-string error context by converting any non-string `e` to undefined when constructing `SendTransactionError`; update the construction in the `SendTransactionError` call (the closure that receives `e`) to set the message to `typeof e === "string" ? e : undefined` but also pass the original `e` as the `cause` (or equivalent) so Error/RPC objects are preserved; reference the `SendTransactionError` constructor and the `e` parameter in your fix.packages/widget/src/pages/position-details/components/position-balances.tsx (1)
22-43:⚠️ Potential issue | 🟡 Minor
enteringcurrently renders unstaking copy.Line 25 now allows both
enteringandexiting, but Line 33/42 still useposition_details.unstaking_*messages. This can show incorrect status text for entering balances.💡 Suggested fix
- if ( - !yieldBalance.date || - (yieldBalance.type !== "entering" && yieldBalance.type !== "exiting") - ) { + if (!yieldBalance.date || yieldBalance.type !== "exiting") { return null; }If
enteringshould also show timing, it should use separate entering-specific translation keys.🤖 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/position-balances.tsx` around lines 22 - 43, The current durationUntilDate useMemo treats both yieldBalance.type === "entering" and "exiting" the same but always returns unstaking translation keys, causing wrong copy for entering balances; update the logic in durationUntilDate to branch on yieldBalance.type: keep the initial guard for missing date/type, then compute date and isPast as now, and return t("position_details.exiting_imminent") / t("position_details.exiting_duration", {duration}) when type === "exiting" but use entering-specific keys (e.g. t("position_details.entering_imminent") / t("position_details.entering_duration", {duration})) when type === "entering" (create those translation keys if they don't exist); keep dependencies [yieldBalance.date, yieldBalance.type, t].
🧹 Nitpick comments (2)
packages/widget/src/providers/sk-wallet/errors.ts (1)
23-24: Set a default decode message to avoid blank/ambiguous errors.When callers omit
message, the resulting error text can be unhelpful during debugging.Suggested patch
- constructor(message?: string, cause?: unknown) { - super(message, { cause }); + constructor(message = "Transaction decode failed", cause?: unknown) { + super(message, { cause });🤖 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` around lines 23 - 24, The error constructor currently allows an empty message when callers omit the message; update the constructor (the constructor(message?: string, cause?: unknown) that calls super(...)) to supply a clear default like "Failed to decode SK wallet" when message is undefined (e.g. const msg = message ?? 'Failed to decode SK wallet'; super(msg, { cause })), preserving the cause parameter so callers still get the original cause.packages/widget/package.json (1)
59-59: Consider the implications of depending on an external URL for type generation.The
gen:yield-apiscript fetches the OpenAPI spec fromhttps://docs.yield.xyz/openapi/appsyield-apiswagger-docsopenapi.yaml. If this URL becomes unavailable or changes, the build script will fail.Consider:
- Documenting this external dependency
- Optionally caching/vendoring a known-good version of the spec for reproducible builds
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/widget/package.json` at line 59, The gen:yield-api npm script currently pulls the OpenAPI spec from an external URL (gen:yield-api -> https://docs.yield.xyz/...), which can break builds if the remote changes or is unavailable; update the repository to document this external dependency (e.g., note in package.json scripts comment and README/CONTRIBUTING that gen:yield-api relies on that URL) and add a vendored/cached copy of the spec (e.g., add src/specs/appsyield-apiswagger-docsopenapi.yaml committed to the repo) and change the gen:yield-api script to use the local copy (and/or implement a fallback that first tries the remote URL and falls back to the vendored file) so builds are reproducible and resilient.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts`:
- Line 39: The callback type for tokenIconMapping must accept the union type
instead of relying on a cast: update the tokenIconMapping callback signature
(where it's declared in the settings types) to (token: TokenDto | YieldTokenDto)
=> string so mapping(token) no longer needs to cast; adjust the type declaration
in packages/widget/src/providers/settings/types.ts (the tokenIconMapping
definition) and any affected references (e.g., use-variant-token-urls.ts where
mapping is invoked) so the mapping function accepts the union directly.
In `@packages/widget/src/config/index.ts`:
- Around line 17-19: The default URLs in the config are inconsistent: apiUrl and
yieldsApiUrl should use the same trailing-slash convention; update the defaults
for apiUrl and/or yieldsApiUrl in packages/widget/src/config/index.ts so both
either include a trailing slash or both omit it (adjust the values for the
apiUrl and yieldsApiUrl keys), and ensure any consumer code that concatenates
paths uses a single, consistent format.
In `@packages/widget/src/domain/types/pending-action.ts`:
- Around line 41-49: The amount bounds are being coerced to Number via
toNumberOrNull which causes precision loss for large token strings; change the
handling so minimum and maximum are preserved as number | string | null in
PendingActionAmountConfig (remove toNumberOrNull when reading
amountArg.minimum/maximum), pass those string values directly into BigNumber
where needed, and only perform a numerical -1 check for forceMax (e.g., coerce
to number or compare to the string "-1") when computing forceMax in the function
that builds the config (refer to minimum, maximum, forceMax and
PendingActionAmountConfig); apply the same change to the other occurrence around
lines 93–104 so both places preserve string bounds and only coerce for the -1
comparison.
In `@packages/widget/src/hooks/api/use-yield-balances-scan.ts`:
- Around line 40-42: The code force-casts val.network to
YieldBalancesRequestDto["queries"][number]["network"] without runtime narrowing;
update the hook in use-yield-balances-scan.ts to validate the network with
isSupportedChain(val.network) and only set enabled: true when that guard passes
(mirror the pattern used in use-multi-yields.ts), and remove the unsafe cast by
deriving the typed network after the guard so the request payload uses a
properly narrowed network value.
In `@packages/widget/src/hooks/use-position-balance-by-type.ts`:
- Around line 40-42: tokenPriceInUsd is being set from the total USD value
(cur.amountUsd) instead of the per-token USD price; update the assignment in
use-position-balance-by-type.ts so tokenPriceInUsd = BigNumber((cur.amountUsd ??
0) / (cur.amount ?? 1)) (preserve the String(...).replace(/,/g,"") parsing) and
use BigNumber.dividedBy (e.g., new BigNumber(parsedAmountUsd).dividedBy(new
BigNumber(cur.amount || 1))) and ensure you handle zero/undefined cur.amount by
falling back to 0 or 1 as appropriate to avoid division-by-zero.
In `@packages/widget/src/hooks/use-positions-data.ts`:
- Around line 47-57: The aggregation branch in use-positions-data.ts is
mislabeling single-validator keys: when building the map (the block that calls
acc.set with balances and type), change the logic that currently treats every
non-"default" key as type "validators" to explicitly check for key ===
"validators" and set type:"validators" only in that case; for keys that match
the single-validator pattern produced by getPositionBalanceDataKey() (i.e.,
"validator::${address}") set a distinct type (e.g., "validator") and include the
validator address (from the key or validatorsAddresses) accordingly so
single-validator entries aren't conflated with multi-validator entries when
calling acc.set.
In `@packages/widget/src/hooks/use-staked-or-liquid-balance.ts`:
- Around line 10-12: The hook useStakedOrLiquidBalance currently only returns
the "active" entry via positionBalancesByType.chain((pbbt) =>
Maybe.fromNullable(pbbt.get("active"))), which contradicts its name and breaks
callers expecting staked-or-liquid balances; update that chain to check for and
return the union of relevant keys (e.g., "active", "entering", "exiting",
"withdrawable", "claimable", and "locked" as appropriate) by attempting pbbt.get
for those keys in order (or merging their values) so the hook returns a Maybe
with any staked-or-liquid balance, or alternatively rename
useStakedOrLiquidBalance to reflect it only returns "active" if you prefer to
keep behavior unchanged.
In `@packages/widget/src/hooks/use-summary.tsx`:
- Around line 244-251: The reducer currently skips adding positions with
yieldDto.rewardRate === 0 to acc.totalValue, which inflates the weighted APY;
change the logic in the use-summary reducer (where yieldDto.rewardRate,
usdAmount, acc.totalWeightedApy, and acc.totalValue are used) so that usdAmount
is always added to acc.totalValue but acc.totalWeightedApy only adds
usdAmount.times(yieldDto.rewardRate * 100) when yieldDto.rewardRate > 0; locate
the block that currently guards both updates with yieldDto.rewardRate > 0 and
split it so totalValue is updated unconditionally while totalWeightedApy remains
conditional.
- Around line 103-106: The code currently returns [] when yield metadata (const
yieldDto = multiYieldsMapQuery.data.get(p.integrationId)) is missing, which
drops the position entirely; instead, preserve position accounting by providing
a fallback yieldDto and continuing processing: replace the early return with a
default/fallback object (e.g., defaultYieldDto or an empty-yields shape) and use
that in subsequent logic so totals still include the position; update any
downstream destructuring in the same scope (where yieldDto is used) to tolerate
the fallback shape and ensure sums treat missing yield entries as zero.
- Around line 124-131: The code converts positionTotalAmount.amountUsd to a
native number when building the positions array (usdAmount) which loses
precision; keep usdAmount as a BigNumber instead and avoid calling toNumber()
there, then compute allPositionsSum by reducing over these BigNumber usdAmount
values (using BigNumber.plus and initializing with new BigNumber(0)); update any
downstream code that expects usdAmount:number to accept a BigNumber or convert
to number only at the final display/formatting step.
In
`@packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts`:
- Around line 53-56: The USD display currently only shows when
totalAmountUsd.gt(0), dropping USD when amountUsd is missing; update the logic
in use-position-list-item (the ternary that returns
Maybe.of(defaultFormattedNumber(totalAmountUsd)) vs Maybe.empty()) to preserve
the fast path but fall back to the derived USD value computed by
getPositionTotalAmount (which sums b.amountUsd ?? 0) when totalAmountUsd is not
>0; i.e., if totalAmountUsd.gt(0) use defaultFormattedNumber(totalAmountUsd),
else compute the derivedUsd via getPositionTotalAmount (or the same summation of
b.amountUsd ?? 0) and return Maybe.of(defaultFormattedNumber(derivedUsd)) when
that derivedUsd > 0, otherwise return Maybe.empty(); ensure you reference
totalAmountUsd, defaultFormattedNumber, Maybe.of/Maybe.empty, and
getPositionTotalAmount in the change.
In
`@packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts`:
- Around line 41-46: The mapping that builds validatorAddresses in
use-stake-exit-request-dto.ts currently uses "b.validators?.map(... ) ?? []",
which can produce an empty array for a required field; instead ensure you fail
fast by returning Maybe.empty() when no valid addresses exist: when iterating
val.stakedOrLiquidBalances filter only entries with validators?.length>0,
collect their validator.address values, and if the resulting list is empty
return Maybe.empty() (rather than producing objects with validatorAddresses:
[]); update the code paths that construct the DTO (the mapping that creates
validatorAddresses) to propagate Maybe.empty() when no addresses are found.
- Around line 30-35: The aggregator branch in use-stake-exit-request-dto should
return only the providerId to match the union type: update the mapping over
val.stakedOrLiquidBalances (the aggregator branch that currently yields {
providerId: b.validator?.providerId, validatorAddress: b.validator?.address })
to return only { providerId: b.validator?.providerId } (remove validatorAddress)
so the result aligns with the allowed union variant; ensure the mapping uses the
same filter (b => !!b.validator?.providerId) and the produced shape matches the
expected providerId-only case.
---
Outside diff comments:
In `@packages/widget/src/pages/position-details/components/position-balances.tsx`:
- Around line 22-43: The current durationUntilDate useMemo treats both
yieldBalance.type === "entering" and "exiting" the same but always returns
unstaking translation keys, causing wrong copy for entering balances; update the
logic in durationUntilDate to branch on yieldBalance.type: keep the initial
guard for missing date/type, then compute date and isPast as now, and return
t("position_details.exiting_imminent") / t("position_details.exiting_duration",
{duration}) when type === "exiting" but use entering-specific keys (e.g.
t("position_details.entering_imminent") /
t("position_details.entering_duration", {duration})) when type === "entering"
(create those translation keys if they don't exist); keep dependencies
[yieldBalance.date, yieldBalance.type, t].
In `@packages/widget/src/providers/sk-wallet/index.tsx`:
- Around line 331-334: The error mapping currently loses non-string error
context by converting any non-string `e` to undefined when constructing
`SendTransactionError`; update the construction in the `SendTransactionError`
call (the closure that receives `e`) to set the message to `typeof e ===
"string" ? e : undefined` but also pass the original `e` as the `cause` (or
equivalent) so Error/RPC objects are preserved; reference the
`SendTransactionError` constructor and the `e` parameter in your fix.
---
Nitpick comments:
In `@packages/widget/package.json`:
- Line 59: The gen:yield-api npm script currently pulls the OpenAPI spec from an
external URL (gen:yield-api -> https://docs.yield.xyz/...), which can break
builds if the remote changes or is unavailable; update the repository to
document this external dependency (e.g., note in package.json scripts comment
and README/CONTRIBUTING that gen:yield-api relies on that URL) and add a
vendored/cached copy of the spec (e.g., add
src/specs/appsyield-apiswagger-docsopenapi.yaml committed to the repo) and
change the gen:yield-api script to use the local copy (and/or implement a
fallback that first tries the remote URL and falls back to the vendored file) so
builds are reproducible and resilient.
In `@packages/widget/src/providers/sk-wallet/errors.ts`:
- Around line 23-24: The error constructor currently allows an empty message
when callers omit the message; update the constructor (the constructor(message?:
string, cause?: unknown) that calls super(...)) to supply a clear default like
"Failed to decode SK wallet" when message is undefined (e.g. const msg = message
?? 'Failed to decode SK wallet'; super(msg, { cause })), preserving the cause
parameter so callers still get the original cause.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4675266b-b6ba-48e1-adc2-d74576671342
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (60)
.changeset/lemon-drinks-prove.mdAGENTS.mdmise.tomlpackages/widget/package.jsonpackages/widget/src/common/private-api.tspackages/widget/src/components/atoms/token-icon/index.tsxpackages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.tspackages/widget/src/components/atoms/token-icon/token-icon-container/index.tsxpackages/widget/src/config/index.tspackages/widget/src/domain/index.tspackages/widget/src/domain/types/pending-action.tspackages/widget/src/domain/types/positions.tspackages/widget/src/domain/types/price.tspackages/widget/src/domain/types/stake.tspackages/widget/src/hooks/api/use-prices.tspackages/widget/src/hooks/api/use-tokens-prices.tspackages/widget/src/hooks/api/use-yield-balances-scan.tspackages/widget/src/hooks/use-position-balance-by-type.tspackages/widget/src/hooks/use-position-balances.tspackages/widget/src/hooks/use-positions-data.tspackages/widget/src/hooks/use-staked-or-liquid-balance.tspackages/widget/src/hooks/use-summary.tsxpackages/widget/src/hooks/use-yield-meta-info.tsxpackages/widget/src/pages-dashboard/activity/position-balances.tsxpackages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsxpackages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.tspackages/widget/src/pages-dashboard/position-details/components/position-details-info.tsxpackages/widget/src/pages/complete/pages/common.page.tsxpackages/widget/src/pages/details/earn-page/state/use-pending-action-deep-link.tspackages/widget/src/pages/details/positions-page/components/positions-list-item.tsxpackages/widget/src/pages/details/positions-page/hooks/use-position-list-item.tspackages/widget/src/pages/details/positions-page/hooks/use-positions.tspackages/widget/src/pages/position-details/components/amount-block.tsxpackages/widget/src/pages/position-details/components/position-balances.tsxpackages/widget/src/pages/position-details/components/static-action-block.tsxpackages/widget/src/pages/position-details/hooks/use-pending-actions.tspackages/widget/src/pages/position-details/hooks/use-position-details.tspackages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.tspackages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.tspackages/widget/src/pages/position-details/hooks/utils.tspackages/widget/src/pages/position-details/position-details.page.tsxpackages/widget/src/pages/position-details/state/index.tsxpackages/widget/src/pages/position-details/state/types.tspackages/widget/src/pages/position-details/state/utils.tspackages/widget/src/pages/review/hooks/use-fees.tspackages/widget/src/pages/review/pages/common-page/common.page.tsxpackages/widget/src/pages/review/pages/common-page/components/review-top-section.tsxpackages/widget/src/providers/exit-stake-store/index.tsxpackages/widget/src/providers/index.tsxpackages/widget/src/providers/pending-action-store/index.tsxpackages/widget/src/providers/settings/types.tspackages/widget/src/providers/sk-wallet/errors.tspackages/widget/src/providers/sk-wallet/index.tsxpackages/widget/src/providers/yield-api-client-provider/index.tsxpackages/widget/src/providers/yield-api-client-provider/types.tspackages/widget/src/translation/English/translations.jsonpackages/widget/src/translation/French/translations.jsonpackages/widget/src/types/yield-api-schema.d.tspackages/widget/src/utils/formatters.tspackages/widget/tests/use-cases/deep-links-flow/setup.ts
💤 Files with no reviewable changes (3)
- packages/widget/src/common/private-api.ts
- packages/widget/src/pages/position-details/position-details.page.tsx
- packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx
| .chainNullable((mapping) => { | ||
| if (typeof mapping === "function") { | ||
| return mapping(token); | ||
| return mapping(token as TokenDto); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the target file to understand the context around line 39
cat -n packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts | head -60Repository: stakekit/widget
Length of output: 2205
🏁 Script executed:
# Search for tokenIconMapping to understand its type signature
rg -n 'tokenIconMapping' packages/widget/src --type ts --type tsx -B 2 -A 2Repository: stakekit/widget
Length of output: 86
🏁 Script executed:
# Look for TokenDto and YieldTokenDto type definitions
rg -n 'type TokenDto|interface TokenDto|type YieldTokenDto|interface YieldTokenDto' packages/widget/src --type ts --type tsxRepository: stakekit/widget
Length of output: 86
🏁 Script executed:
# Check the settings provider or context that defines the callback
fd 'settings' packages/widget/src/providers --type ts --type tsxRepository: stakekit/widget
Length of output: 228
🏁 Script executed:
# Search for tokenIconMapping definition and type
rg -n 'tokenIconMapping' packages/widget/src -B 2 -A 5Repository: stakekit/widget
Length of output: 3888
🏁 Script executed:
# Look for TokenDto type definition
rg -n 'type TokenDto|interface TokenDto' packages/widget/srcRepository: stakekit/widget
Length of output: 319
🏁 Script executed:
# Look for YieldTokenDto type definition
rg -n 'type YieldTokenDto|interface YieldTokenDto' packages/widget/srcRepository: stakekit/widget
Length of output: 189
🏁 Script executed:
# Find settings provider or useSettings hook
fd 'settings' packages/widget/src/providersRepository: stakekit/widget
Length of output: 98
🏁 Script executed:
# Check if TokenDto and YieldTokenDto are compatible by viewing their definitions
rg -n 'export type YieldTokenDto' packages/widget/src -A 3Repository: stakekit/widget
Length of output: 466
🏁 Script executed:
# Check the import source of TokenDto to understand what it is
cat -n packages/widget/src/providers/settings/types.ts | head -20Repository: stakekit/widget
Length of output: 1014
🏁 Script executed:
# Look at the complete settings types file to see the context
cat -n packages/widget/src/providers/settings/types.tsRepository: stakekit/widget
Length of output: 3504
Update tokenIconMapping callback type to accept the union instead of casting.
At Line 39, the cast suppresses type safety when the function parameter accepts TokenDto | YieldTokenDto. Since tokenIconMapping is defined to only accept TokenDto (in packages/widget/src/providers/settings/types.ts:68), update its callback signature to ((token: TokenDto | YieldTokenDto) => string) to properly reflect what the function can receive and eliminate the cast.
🤖 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`
at line 39, The callback type for tokenIconMapping must accept the union type
instead of relying on a cast: update the tokenIconMapping callback signature
(where it's declared in the settings types) to (token: TokenDto | YieldTokenDto)
=> string so mapping(token) no longer needs to cast; adjust the type declaration
in packages/widget/src/providers/settings/types.ts (the tokenIconMapping
definition) and any affected references (e.g., use-variant-token-urls.ts where
mapping is invoked) so the mapping function accepts the union directly.
| const minimum = toNumberOrNull(amountArg.minimum); | ||
| const maximum = toNumberOrNull(amountArg.maximum); | ||
|
|
||
| return { | ||
| required: !!amountArg.required, | ||
| minimum, | ||
| maximum, | ||
| forceMax: minimum === -1 && maximum === -1, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find the file and verify its structure
echo "=== Checking pending-action.ts structure ==="
head -n 60 packages/widget/src/domain/types/pending-action.ts | cat -n
echo -e "\n=== Finding toNumberOrNull definition ==="
rg -n "toNumberOrNull" packages/widget/src/domain/types/pending-action.ts
echo -e "\n=== Finding toNumberOrNull function ==="
rg -B5 -A10 "export.*function toNumberOrNull|const toNumberOrNull.*=" packages/widget/src/Repository: stakekit/widget
Length of output: 3776
🏁 Script executed:
#!/bin/bash
# Check downstream usage of getPendingActionAmountConfig and amountConfig
echo "=== Finding getPendingActionAmountConfig usages ==="
rg -n "getPendingActionAmountConfig" packages/widget/src/
echo -e "\n=== Finding where amountConfig.minimum and amountConfig.maximum are used ==="
rg -B3 -A3 "amountConfig\.(minimum|maximum)" packages/widget/src/Repository: stakekit/widget
Length of output: 690
🏁 Script executed:
#!/bin/bash
# Check the complete function at lines 41-49 and 93-104
echo "=== Full context around lines 41-49 ==="
sed -n '30,55p' packages/widget/src/domain/types/pending-action.ts | cat -n
echo -e "\n=== Full context around lines 93-104 ==="
sed -n '85,115p' packages/widget/src/domain/types/pending-action.ts | cat -nRepository: stakekit/widget
Length of output: 1586
🏁 Script executed:
#!/bin/bash
# Check the full context of amountConfig usage in position-details/state/index.tsx
echo "=== Context around line 212 in position-details/state/index.tsx ==="
sed -n '200,250p' packages/widget/src/pages/position-details/state/index.tsx | cat -n
echo -e "\n=== Search for BigNumber usage with minimum/maximum ==="
rg -B5 -A5 "new BigNumber.*minimum|new BigNumber.*maximum|BigNumber.*amountConfig" packages/widget/src/pages/position-details/Repository: stakekit/widget
Length of output: 3022
🏁 Script executed:
#!/bin/bash
# Check what happens after amountConfig is retrieved
echo "=== Full usage pattern of amountConfig ==="
rg -B3 -A10 "const amountConfig = getPendingActionAmountConfig" packages/widget/src/pages/position-details/state/index.tsxRepository: stakekit/widget
Length of output: 623
Preserve amount bounds as strings to avoid Number precision loss.
Lines 41–42 coerce minimum/maximum through toNumberOrNull, which calls Number(value). Large numeric strings (common in token amounts) lose precision in JavaScript's 53-bit integer limit before reaching BigNumber at line 214–217 of position-details/state/index.tsx. This produces incorrect min/max clamping.
Recommended fix: Keep values as number | string | null in PendingActionAmountConfig, pass strings directly to BigNumber (which handles them precisely), and only coerce to number for the forceMax check that requires -1 comparison.
♻️ Proposed fix
export type PendingActionAmountConfig = {
required: boolean;
- minimum: number | null;
- maximum: number | null;
+ minimum: number | string | null;
+ maximum: number | string | null;
forceMax: boolean;
};
@@
- const minimum = toNumberOrNull(amountArg.minimum);
- const maximum = toNumberOrNull(amountArg.maximum);
+ const minimum = amountArg.minimum ?? null;
+ const maximum = amountArg.maximum ?? null;
@@
return {
required: !!amountArg.required,
minimum,
maximum,
- forceMax: minimum === -1 && maximum === -1,
+ forceMax: toNumberOrNull(minimum) === -1 && toNumberOrNull(maximum) === -1,
};
};Also applies to: 93–104
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/widget/src/domain/types/pending-action.ts` around lines 41 - 49, The
amount bounds are being coerced to Number via toNumberOrNull which causes
precision loss for large token strings; change the handling so minimum and
maximum are preserved as number | string | null in PendingActionAmountConfig
(remove toNumberOrNull when reading amountArg.minimum/maximum), pass those
string values directly into BigNumber where needed, and only perform a numerical
-1 check for forceMax (e.g., coerce to number or compare to the string "-1")
when computing forceMax in the function that builds the config (refer to
minimum, maximum, forceMax and PendingActionAmountConfig); apply the same change
to the other occurrence around lines 93–104 so both places preserve string
bounds and only coerce for the -1 comparison.
| network: | ||
| val.network as YieldBalancesRequestDto["queries"][number]["network"], | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Cast site:"
rg -nP -C2 'as YieldBalancesRequestDto\["queries"\]\[number\]\["network"\]' packages/widget/src/hooks/api/use-yield-balances-scan.ts
echo
echo "Wallet network typing context:"
rg -nP -C3 '\bnetwork\b' packages/widget/src/providers/sk-wallet/index.tsx
echo
echo "Yield API request network schema context:"
fd 'yield-api-schema.d.ts' packages/widget/src -x rg -nP -C2 'BalancesRequestDto|queries|network' {}Repository: stakekit/widget
Length of output: 12671
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Full context of use-yield-balances-scan.ts with cast ==="
cat -n packages/widget/src/hooks/api/use-yield-balances-scan.ts | head -80
echo
echo "=== Find where val is defined ==="
rg -nP 'const val|let val|\.map.*val|val\s*=' packages/widget/src/hooks/api/use-yield-balances-scan.ts -A2 -B2
echo
echo "=== Find type definitions for wallet network ==="
fd 'index.tsx' packages/widget/src/providers/sk-wallet -x cat -n {} | head -100
echo
echo "=== Find Networks type definition ==="
rg -nP 'type Networks|Networks\s*=' packages/widget/src -A5Repository: stakekit/widget
Length of output: 3062
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Inspect useSKWallet return type ==="
rg -nP 'export.*useSKWallet|function useSKWallet|const useSKWallet' packages/widget/src/providers/sk-wallet/index.tsx -A30 | head -80
echo
echo "=== Find SKWallet interface type definition ==="
fd 'types.ts' packages/widget/src/providers/sk-wallet -x cat -n {} 2>/dev/null | head -150
echo
echo "=== Find Networks enum/type in yield-api-client-provider types ==="
fd 'types.ts' packages/widget/src/providers/yield-api-client-provider -x cat -n {} 2>/dev/null | head -100Repository: stakekit/widget
Length of output: 1465
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find SKWalletContext and its value type ==="
rg -nP 'SKWalletContext|const SKWallet' packages/widget/src/providers/sk-wallet/index.tsx -B5 -A10 | head -100
echo
echo "=== Find types.ts in sk-wallet if exists ==="
fd 'types' packages/widget/src/providers/sk-wallet -type f
echo
echo "=== Search for SKWallet type/interface definition ==="
rg -nP 'interface SKWallet|type SKWallet' packages/widget/src -A20 | head -150
echo
echo "=== Search for Networks type in yield-api-schema ==="
rg -nP 'Networks\s*=' packages/widget/src/types/yield-api-schema.d.ts | head -5
</parameter>Repository: stakekit/widget
Length of output: 1493
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for SKWallet interface/type ==="
rg -nP 'interface SKWallet|type SKWallet' packages/widget/src -A25 | head -200
echo
echo "=== Find wagmiNetworkToSKNetwork and understand network transformation ==="
rg -nP 'wagmiNetworkToSKNetwork|export.*SKNetwork|type SKNetwork' packages/widget/src -B2 -A10
echo
echo "=== Check imports in use-yield-balances-scan.ts ==="
head -15 packages/widget/src/hooks/api/use-yield-balances-scan.tsRepository: stakekit/widget
Length of output: 7599
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find SupportedSKChains type definition ==="
rg -nP 'type SupportedSKChains|SupportedSKChains\s*=' packages/widget/src -B2 -A15
echo
echo "=== Find Networks type from wallet.ts ==="
rg -nP 'type Networks|Networks\s*=' packages/widget/src/domain/types/wallet.ts -B2 -A10
echo
echo "=== Check if SupportedSKChains and Networks are the same ==="
rg -nP 'import.*Networks|from.*yield-api|from.*types' packages/widget/src/domain/types/wallet.ts | head -20Repository: stakekit/widget
Length of output: 5197
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Get full wallet.ts to understand Networks type ==="
cat -n packages/widget/src/domain/types/wallet.ts | head -80
echo
echo "=== Find Networks import in wallet.ts ==="
head -30 packages/widget/src/domain/types/wallet.ts | grep -i 'import\|networks'
echo
echo "=== Find Networks type definition ==="
rg -nP 'type Networks' packages/widget/src/domain/types -A5Repository: stakekit/widget
Length of output: 2685
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check what `@stakekit/api-hooks` provides for Networks ==="
rg -nP 'export.*Networks|type Networks|interface Networks' packages/widget/node_modules/@stakekit/api-hooks 2>/dev/null | head -20
echo
echo "=== Check if SupportedSKChains matches Networks by looking at their members ==="
rg -nP 'SupportedEvmChain|SupportedCosmosChains|SupportedMiscChains|SupportedSubstrateChains' packages/widget/src/domain/types/chains/index.ts -A2 | head -100
echo
echo "=== Look at the actual chains enum values ==="
grep -E 'Ethereum|ethereum|Solana|solana|Cosmos' packages/widget/src/domain/types/chains/index.ts | head -30Repository: stakekit/widget
Length of output: 119
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Get chain definitions to understand SupportedSKChains values ==="
cat -n packages/widget/src/domain/types/chains/index.ts | head -200
echo
echo "=== Check YieldBalancesRequestDto structure to confirm target network type ==="
rg -nP 'YieldBalancesRequestDto|BalancesQueryDto' packages/widget/src/types/yield-api-schema.d.ts -B2 -A10 | head -100Repository: stakekit/widget
Length of output: 4201
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check if there's any validation of network values ==="
rg -nP 'isSupportedChain|isEvmChain|isSolanaChain' packages/widget/src/hooks/api/use-yield-balances-scan.ts
echo
echo "=== Check how other similar hooks handle network validation ==="
fd 'use-.*\.ts' packages/widget/src/hooks/api -x grep -l 'network' {} | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && head -60 {}'
echo
echo "=== Look at the actual SupportedSKChains enum values vs Networks enum ==="
rg -nP 'EvmNetworks\.|MiscNetworks\.|SubstrateNetworks\.' packages/widget/src/domain/types/chains/evm.ts packages/widget/src/domain/types/chains/misc.ts packages/widget/src/domain/types/chains/cosmos.ts 2>/dev/null | head -50Repository: stakekit/widget
Length of output: 116
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Get chain enum values from evm.ts ==="
head -80 packages/widget/src/domain/types/chains/evm.ts
echo
echo "=== Get chain enum values from misc.ts ==="
head -50 packages/widget/src/domain/types/chains/misc.ts
echo
echo "=== Get chain enum values from cosmos.ts ==="
head -50 packages/widget/src/domain/types/chains/cosmos.ts
echo
echo "=== Check yield-api-schema for the Networks enum values ==="
rg -nP 'Networks.*:' packages/widget/src/types/yield-api-schema.d.ts -A40 | head -100Repository: stakekit/widget
Length of output: 6752
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check if Networks values match SupportedSKChains values ==="
echo "Getting Networks enum values..."
rg -nP 'Networks:' packages/widget/src/types/yield-api-schema.d.ts -A200 | grep '|' | sed 's/.*"|//;s/".*//' | sort > /tmp/networks.txt
echo "Sample of Networks enum values:"
head -30 /tmp/networks.txt
echo
echo "=== Check enum value mappings in `@stakekit/common` ==="
# Looking for how EvmNetworks, CosmosNetworks, etc. are defined
rg -nP 'export.*enum|export.*const.*=' packages/widget/src/domain/types/chains -A2 | head -80
echo
echo "=== Check if there's any documented relationship between SupportedSKChains and Networks ==="
rg -nP 'SupportedSKChains|Networks' packages/widget/src/domain/types/wallet.ts | head -20
echo
echo "=== Look for any validation/mapping functions between chain types ==="
rg -nP 'function|const.*=.*=>.*network|skChainName.*network' packages/widget/src/domain/types/chains -B2 -A5 | head -60Repository: stakekit/widget
Length of output: 6288
🏁 Script executed:
#!/bin/bash
set -euoelpipefail
echo "=== Check if EvmNetworks enum values are strings matching the schema ==="
python3 << 'EOF'
# Extract Networks enum values from schema
import re
with open('packages/widget/src/types/yield-api-schema.d.ts', 'r') as f:
content = f.read()
# Find Networks enum
match = re.search(r'Networks:\s*(.*?)(?=\n\s{2,4}[A-Z]|$)', content, re.DOTALL)
if match:
network_values = re.findall(r'"([^"]+)"', match.group(1))
print("Networks enum values (sample):")
for v in sorted(set(network_values))[:30]:
print(f" {v}")
EOF
echo
echo "=== Check how `@stakekit/common` defines these enums ==="
# Look for any documentation or type hints in the codebase
rg -nP 'EvmNetworks\.|MiscNetworks\.|CosmosNetworks\.|SubstrateNetworks\.' packages/widget/src/domain/types/chains/evm.ts -A0 | head -30 | sed 's/:.*/:/' | sort -u
echo
echo "=== Check if there are any GitHub issues or comments about type mismatches ==="
rg -nP 'FIXME|TODO|BUG|HACK|cast|unsafe|network.*type' packages/widget/src/hooks/api/use-yield-balances-scan.ts -B2 -A2
echo
echo "=== Check if SupportedSKChains can be safely assigned to Networks ==="
# Look for type compatibility in broader context
rg -nP 'as Networks|Networks.*SupportedSKChains|SupportedSKChains.*Networks' packages/widget/src -A2 -B2 | head -40Repository: stakekit/widget
Length of output: 733
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Extract Networks enum values from schema ==="
rg -nP 'Networks:' packages/widget/src/types/yield-api-schema.d.ts -A100 | head -120 | tail -100
echo
echo "=== Check actual enum values used in EvmNetworks from `@stakekit/common` imports ==="
rg 'EvmNetworks\.' packages/widget/src/domain/types/chains/evm.ts | head -20
echo
echo "=== Check for any comments about network type compatibility ==="
rg -nP 'network|Network' packages/widget/src/hooks/api/use-yield-balances-scan.ts -B3 -A3
echo
echo "=== Check what val.network type is in context ==="
rg -nP 'Maybe.fromRecord|mapOrDefault' packages/widget/src/hooks/api/use-yield-balances-scan.ts -B5 -A15Repository: stakekit/widget
Length of output: 4928
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Verify type alignment: check if EvmNetworks enum values exist in Networks schema ==="
python3 << 'PYTHON_EOF'
import re
# Read the schema file
with open('packages/widget/src/types/yield-api-schema.d.ts', 'r') as f:
schema_content = f.read()
# Extract Networks enum values
networks_match = re.search(r'Networks:\s*(.*?)(?=\n\s{2,4}[A-Z]|;)', schema_content, re.DOTALL)
if networks_match:
network_values = set(re.findall(r'"([^"]+)"', networks_match.group(1)))
print(f"Found {len(network_values)} network values in schema")
# Check for specific EVMs
evm_networks = {
'ethereum', 'arbitrum', 'optimism', 'polygon', 'base', 'binance',
'avalanche-c', 'celo', 'harmony', 'viction', 'gnosis', 'core', 'sonic',
'linea', 'unichain', 'katana', 'plasma'
}
print("\nEVM network coverage in schema:")
for net in sorted(evm_networks):
status = "✓" if net in network_values else "✗"
print(f" {status} {net}")
# Check for cosmos
cosmos_networks = {
'cosmos', 'juno', 'akash', 'osmosis', 'axelar', 'persistence',
'injective', 'sei', 'dydx', 'stargaze'
}
print("\nCosmos network coverage in schema:")
for net in sorted(cosmos_networks):
status = "✓" if net in network_values else "✗"
print(f" {status} {net}")
# Check misc
misc_networks = {'solana', 'tron', 'ton', 'near', 'tezos', 'cardano'}
print("\nMisc network coverage in schema:")
for net in sorted(misc_networks):
status = "✓" if net in network_values else "✗"
print(f" {status} {net}")
PYTHON_EOF
echo
echo "=== Check if there's any validation in the codebase for network values ==="
rg -nP 'isSupportedChain|isEvmChain|network.*valid|validate.*network' packages/widget/src/hooks/api -B2 -A5Repository: stakekit/widget
Length of output: 2089
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check if isSupportedChain validation exists in use-yield-balances-scan ==="
rg -nP 'isSupportedChain' packages/widget/src/hooks/api/use-yield-balances-scan.ts
echo
echo "=== Compare with similar hooks that DO use validation ==="
echo "=== use-multi-yields.ts approach ==="
rg -nP 'isSupportedChain.*network|network.*isSupportedChain' packages/widget/src/hooks/api/use-multi-yields.ts -B3 -A3
echo
echo "=== Check what isSupportedChain actually does ==="
rg -nP 'export const isSupportedChain|function isSupportedChain' packages/widget/src/domain/types/chains/index.ts -A8
echo
echo "=== Check if wallet.ts guarantees network type when isConnected ==="
grep -A30 'isConnected: true' packages/widget/src/domain/types/wallet.ts | head -15Repository: stakekit/widget
Length of output: 137
Add explicit network validation before enabling the request.
Line 41 force-casts val.network without validation, bypassing type safety. While the supported networks align with the Yield API schema, the cast lacks explicit narrowing and could become problematic if networks diverge. Use isSupportedChain(val.network) to add a type guard before enabled: true, similar to the pattern in use-multi-yields.ts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/widget/src/hooks/api/use-yield-balances-scan.ts` around lines 40 -
42, The code force-casts val.network to
YieldBalancesRequestDto["queries"][number]["network"] without runtime narrowing;
update the hook in use-yield-balances-scan.ts to validate the network with
isSupportedChain(val.network) and only set enabled: true when that guard passes
(mirror the pattern used in use-multi-yields.ts), and remove the unsafe cast by
deriving the typed network after the guard so the request payload uses a
properly narrowed network value.
| const tokenPriceInUsd = new BigNumber( | ||
| String(cur.amountUsd ?? 0).replace(/,/g, "") | ||
| ); |
There was a problem hiding this comment.
Compute unit USD price, not total USD amount.
At Line 40, tokenPriceInUsd is assigned from amountUsd directly. That stores total balance USD in a unit-price field and can overcount downstream calculations.
💡 Proposed fix
- const tokenPriceInUsd = new BigNumber(
- String(cur.amountUsd ?? 0).replace(/,/g, "")
- );
+ const amountUsd = new BigNumber(String(cur.amountUsd ?? 0).replace(/,/g, ""));
+ if (amountUsd.isNaN()) return acc;
+ const tokenPriceInUsd = amountUsd.div(amount);📝 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.
| const tokenPriceInUsd = new BigNumber( | |
| String(cur.amountUsd ?? 0).replace(/,/g, "") | |
| ); | |
| const amountUsd = new BigNumber(String(cur.amountUsd ?? 0).replace(/,/g, "")); | |
| if (amountUsd.isNaN()) return acc; | |
| const tokenPriceInUsd = amountUsd.div(amount); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/widget/src/hooks/use-position-balance-by-type.ts` around lines 40 -
42, tokenPriceInUsd is being set from the total USD value (cur.amountUsd)
instead of the per-token USD price; update the assignment in
use-position-balance-by-type.ts so tokenPriceInUsd = BigNumber((cur.amountUsd ??
0) / (cur.amount ?? 1)) (preserve the String(...).replace(/,/g,"") parsing) and
use BigNumber.dividedBy (e.g., new BigNumber(parsedAmountUsd).dividedBy(new
BigNumber(cur.amount || 1))) and ensure you handle zero/undefined cur.amount by
falling back to 0 or 1 as appropriate to avoid division-by-zero.
| usdAmount: positionTotalAmount.amountUsd.toNumber(), | ||
| }; | ||
| }); | ||
|
|
||
| const allPositionsSum = allPositions.reduce( | ||
| (acc, p) => acc.plus(p.usdAmount), | ||
| new BigNumber(0) | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify BigNumber->number downcasts and aggregation points in summary flow.
rg -nP 'amountUsd\.toNumber\(\)|allPositionsSum|usdAmount:\s*number' \
packages/widget/src/hooks/use-summary.tsx \
packages/widget/src/domain/types/positions.tsRepository: stakekit/widget
Length of output: 520
🏁 Script executed:
# Read the context around lines 124-131 in use-summary.tsx
head -n 135 packages/widget/src/hooks/use-summary.tsx | tail -n 20Repository: stakekit/widget
Length of output: 556
🏁 Script executed:
# Read the type definitions in positions.ts around lines 51-62
head -n 70 packages/widget/src/domain/types/positions.ts | tail -n 30Repository: stakekit/widget
Length of output: 779
Avoid converting USD amounts to number before aggregation.
Line 124 converts positionTotalAmount.amountUsd (a BigNumber) to number, storing it as usdAmount: number. When allPositionsSum sums these values on lines 128–131, the precision has already been lost during the initial conversion to IEEE 754 double precision. Keep usdAmount as BigNumber throughout to preserve precision in financial calculations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/widget/src/hooks/use-summary.tsx` around lines 124 - 131, The code
converts positionTotalAmount.amountUsd to a native number when building the
positions array (usdAmount) which loses precision; keep usdAmount as a BigNumber
instead and avoid calling toNumber() there, then compute allPositionsSum by
reducing over these BigNumber usdAmount values (using BigNumber.plus and
initializing with new BigNumber(0)); update any downstream code that expects
usdAmount:number to accept a BigNumber or convert to number only at the final
display/formatting step.
| if (yieldDto.rewardRate > 0 && usdAmount.gt(0)) { | ||
| return { | ||
| totalWeightedApy: acc.totalWeightedApy.plus( | ||
| usdAmount.times(yieldDto.rewardRate * 100) | ||
| ), | ||
| totalValue: acc.totalValue.plus(usdAmount), | ||
| }; | ||
| } |
There was a problem hiding this comment.
Weighted APY currently overstates portfolios with 0% APY positions.
Line 244 filters with yieldDto.rewardRate > 0, so zero-rate positions are excluded from totalValue and inflate the average.
Proposed fix
- if (yieldDto.rewardRate > 0 && usdAmount.gt(0)) {
+ if (usdAmount.gt(0)) {
+ const rewardRate = yieldDto.rewardRate ?? 0;
return {
totalWeightedApy: acc.totalWeightedApy.plus(
- usdAmount.times(yieldDto.rewardRate * 100)
+ usdAmount.times(rewardRate * 100)
),
totalValue: acc.totalValue.plus(usdAmount),
};
}📝 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.
| if (yieldDto.rewardRate > 0 && usdAmount.gt(0)) { | |
| return { | |
| totalWeightedApy: acc.totalWeightedApy.plus( | |
| usdAmount.times(yieldDto.rewardRate * 100) | |
| ), | |
| totalValue: acc.totalValue.plus(usdAmount), | |
| }; | |
| } | |
| if (usdAmount.gt(0)) { | |
| const rewardRate = yieldDto.rewardRate ?? 0; | |
| return { | |
| totalWeightedApy: acc.totalWeightedApy.plus( | |
| usdAmount.times(rewardRate * 100) | |
| ), | |
| totalValue: acc.totalValue.plus(usdAmount), | |
| }; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/widget/src/hooks/use-summary.tsx` around lines 244 - 251, The
reducer currently skips adding positions with yieldDto.rewardRate === 0 to
acc.totalValue, which inflates the weighted APY; change the logic in the
use-summary reducer (where yieldDto.rewardRate, usdAmount, acc.totalWeightedApy,
and acc.totalValue are used) so that usdAmount is always added to acc.totalValue
but acc.totalWeightedApy only adds usdAmount.times(yieldDto.rewardRate * 100)
when yieldDto.rewardRate > 0; locate the block that currently guards both
updates with yieldDto.rewardRate > 0 and split it so totalValue is updated
unconditionally while totalWeightedApy remains conditional.
| totalAmountUsd.gt(0) | ||
| ? Maybe.of(defaultFormattedNumber(totalAmountUsd)) | ||
| : (Maybe.empty() as Maybe<string>), | ||
| [totalAmountUsd] |
There was a problem hiding this comment.
Don’t drop USD value when amountUsd is absent.
At Line [53], USD output is gated only by totalAmountUsd.gt(0). But getPositionTotalAmount (packages/widget/src/domain/types/positions.ts, Lines [51-62]) sums with b.amountUsd ?? 0, so missing API amountUsd can cause empty/underreported USD in dashboard rows (downstream at packages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsx, Line [170]).
💡 Suggested fallback (preserve current fast path, restore derived USD fallback)
const {
integrationData,
providersDetails,
rewardRateAverage,
inactiveValidator,
baseToken,
+ tokenToDisplay,
+ totalAmount,
totalAmountUsd,
totalAmountFormatted,
} = useBasePositionListItem(item);
@@
const totalAmountPriceFormatted = useMemo(
- () =>
- totalAmountUsd.gt(0)
- ? Maybe.of(defaultFormattedNumber(totalAmountUsd))
- : (Maybe.empty() as Maybe<string>),
- [totalAmountUsd]
+ () => {
+ if (totalAmountUsd.gt(0)) {
+ return Maybe.of(defaultFormattedNumber(totalAmountUsd));
+ }
+
+ return Maybe.fromRecord({
+ baseToken,
+ tokenToDisplay,
+ prices: Maybe.fromNullable(prices.data),
+ })
+ .map((val) =>
+ getTokenPriceInUSD({
+ baseToken: val.baseToken,
+ amount: totalAmount,
+ pricePerShare: null,
+ token: val.tokenToDisplay,
+ prices: val.prices,
+ })
+ )
+ .filter((v) => BigNumber(v).gt(0))
+ .map(defaultFormattedNumber);
+ },
+ [totalAmountUsd, totalAmount, baseToken, tokenToDisplay, prices.data]
);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts`
around lines 53 - 56, The USD display currently only shows when
totalAmountUsd.gt(0), dropping USD when amountUsd is missing; update the logic
in use-position-list-item (the ternary that returns
Maybe.of(defaultFormattedNumber(totalAmountUsd)) vs Maybe.empty()) to preserve
the fast path but fall back to the derived USD value computed by
getPositionTotalAmount (which sums b.amountUsd ?? 0) when totalAmountUsd is not
>0; i.e., if totalAmountUsd.gt(0) use defaultFormattedNumber(totalAmountUsd),
else compute the derivedUsd via getPositionTotalAmount (or the same summation of
b.amountUsd ?? 0) and return Maybe.of(defaultFormattedNumber(derivedUsd)) when
that derivedUsd > 0, otherwise return Maybe.empty(); ensure you reference
totalAmountUsd, defaultFormattedNumber, Maybe.of/Maybe.empty, and
getPositionTotalAmount in the change.
| (b) => !!b.validator?.providerId, | ||
| val.stakedOrLiquidBalances | ||
| ).map((b) => ({ | ||
| providerId: b.providerId, | ||
| validatorAddress: b.validatorAddress, | ||
| providerId: b.validator?.providerId, | ||
| validatorAddress: b.validator?.address, | ||
| })); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how ActionRequestDto args are defined and how providerId/validatorAddress are emitted together.
# 1) Find ActionRequestDto definitions/usages in the repo
echo "=== ActionRequestDto definitions/usages ==="
rg -nP --type=ts -C3 'ActionRequestDto' | head -100
# 2) Find DTO constructions that emit providerId and validatorAddress together
echo -e "\n=== providerId/validatorAddress patterns ==="
rg -nP --type=ts -C3 'providerId|validatorAddress' | head -150
# 3) Find the actual file and inspect the changed lines
echo -e "\n=== Target file content (lines 25-50) ==="
if [ -f "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" ]; then
sed -n '25,50p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -n
fiRepository: stakekit/widget
Length of output: 21142
🏁 Script executed:
# Read the actual target file to see the exact code at lines 30-35
echo "=== Full context of use-stake-exit-request-dto.ts (lines 1-70) ==="
cat -n "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | head -70Repository: stakekit/widget
Length of output: 3276
🏁 Script executed:
# Verify the ActionRequestDto args structure from API hooks
rg -nP --type=ts "type ActionRequestDto|interface ActionRequestDto" -A 20 | head -80
# Check if there are other files using ActionRequestDto args to understand patterns
rg -nP --type=ts "args:\s*{" | grep -i "providerId\|validatorAddress" | head -20Repository: stakekit/widget
Length of output: 41
🏁 Script executed:
# Check if ActionRequestDto is an external import and see how args are typically structured
rg -nP "from.*@stakekit/api-hooks" --type=ts | head -5
# Look at how the three branches use the union to confirm type expectations
echo "=== Checking other uses of validatorsOrProvider variable ==="
sed -n '22,75p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -nRepository: stakekit/widget
Length of output: 3097
🏁 Script executed:
# Check how validatorsOrProvider is used after the chain
sed -n '70,120p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -nRepository: stakekit/widget
Length of output: 971
🏁 Script executed:
# Confirm the type violation by checking if TypeScript would catch this
# Look for any type ignore comments or assertions around this code
sed -n '20,40p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -n
# Check if there's a TypeScript config that might suppress errors
if [ -f "tsconfig.json" ]; then
echo "=== tsconfig.json strict mode ==="
jq '.compilerOptions | {strict, noImplicitAny, skipLibCheck}' tsconfig.json
fiRepository: stakekit/widget
Length of output: 1169
🏁 Script executed:
# Let me check the similar enter-flow file to see the pattern there
echo "=== use-stake-enter-request-dto.ts aggregator branch ==="
sed -n '34,65p' "packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts" | cat -nRepository: stakekit/widget
Length of output: 1509
🏁 Script executed:
# Compare the two implementations side-by-side to confirm the issue
echo "=== ENTER flow (correct) - aggregator branch ==="
sed -n '9,12p' "packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts" | cat -n
echo -e "\n=== EXIT flow (incorrect) - aggregator branch ==="
sed -n '28,35p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -n
echo -e "\n=== Type union definition (both files) ==="
sed -n '24,26p' "packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts" | cat -nRepository: stakekit/widget
Length of output: 876
🏁 Script executed:
# Get correct line ranges from the enter file
echo "=== ENTER flow aggregator branch (lines 39-42) ==="
sed -n '39,42p' "packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts" | cat -n
echo -e "\n=== EXIT flow aggregator branch (lines 28-35) ==="
sed -n '28,35p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -nRepository: stakekit/widget
Length of output: 744
🏁 Script executed:
# Get the full aggregator branch from the enter file
echo "=== ENTER flow - full aggregator branch ==="
sed -n '43,45p' "packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts" | cat -n
echo -e "\n=== EXIT flow - full aggregator branch ==="
sed -n '28,35p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -n
# Show the type union both files use
echo -e "\n=== Type union in ENTER ==="
sed -n '35,39p' "packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts" | cat -n
echo -e "\n=== Type union in EXIT ==="
sed -n '23,27p' "packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts" | cat -nRepository: stakekit/widget
Length of output: 1310
Fix aggregator branch to return only providerId, not both providerId and validatorAddress.
The aggregator branch (lines 28–35) returns both providerId and validatorAddress, but the type union explicitly allows only three combinations: validatorAddresses alone, validatorAddress with subnetId, or providerId alone. Returning both fields violates the contract. The enter-flow file shows the correct pattern: return only { providerId: v.providerId } for aggregators.
🤖 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 30 - 35, The aggregator branch in use-stake-exit-request-dto should
return only the providerId to match the union type: update the mapping over
val.stakedOrLiquidBalances (the aggregator branch that currently yields {
providerId: b.validator?.providerId, validatorAddress: b.validator?.address })
to return only { providerId: b.validator?.providerId } (remove validatorAddress)
so the result aligns with the allowed union variant; ensure the mapping uses the
same filter (b => !!b.validator?.providerId) and the produced shape matches the
expected providerId-only case.
| (b) => !!b.validators?.length, | ||
| val.stakedOrLiquidBalances | ||
| ).map((b) => ({ validatorAddresses: b.validatorAddresses })); | ||
| ).map((b) => ({ | ||
| validatorAddresses: | ||
| b.validators?.map((validator) => validator.address) ?? [], | ||
| })); |
There was a problem hiding this comment.
Avoid defaulting required validatorAddresses to an empty array.
In a required path, ?? [] can silently produce an invalid request instead of failing fast. Prefer returning Maybe.empty() when no valid addresses are available.
Suggested adjustment
- ).map((b) => ({
- validatorAddresses:
- b.validators?.map((validator) => validator.address) ?? [],
- }));
+ ).chain((b) => {
+ const validatorAddresses = (b.validators ?? [])
+ .map((validator) => validator.address)
+ .filter((address): address is string => Boolean(address));
+
+ return validatorAddresses.length > 0
+ ? Just({ validatorAddresses })
+ : Maybe.empty();
+ });🤖 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 41 - 46, The mapping that builds validatorAddresses in
use-stake-exit-request-dto.ts currently uses "b.validators?.map(... ) ?? []",
which can produce an empty array for a required field; instead ensure you fail
fast by returning Maybe.empty() when no valid addresses exist: when iterating
val.stakedOrLiquidBalances filter only entries with validators?.length>0,
collect their validator.address values, and if the resulting list is empty
return Maybe.empty() (rather than producing objects with validatorAddresses:
[]); update the code paths that construct the DTO (the mapping that creates
validatorAddresses) to propagate Maybe.empty() when no addresses are found.
Summary by CodeRabbit
New Features
Documentation
Chores