-
Notifications
You must be signed in to change notification settings - Fork 2
feat: migrate to yield api balances #514
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@stakekit/widget": patch | ||
| --- | ||
|
|
||
| feat: migrate to yield api balances |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # StakeKit Widget — Agent Guide | ||
|
|
||
| ## Project Overview | ||
| - Monorepo managed with `pnpm` workspaces + Turborepo. | ||
| - Main package is `@stakekit/widget` in `packages/widget` (React + TypeScript + Vite). | ||
| - Widget supports two entry modes: | ||
| - React component export (`src/index.package.ts`) | ||
| - Fully bundled renderer (`src/index.bundle.ts`) | ||
| - Runtime branches between classic widget and dashboard variant in `src/App.tsx`. | ||
|
|
||
| ## Repo Layout (important paths) | ||
| - `packages/widget/src/App.tsx` — root app, router setup, bundle renderer. | ||
| - `packages/widget/src/Widget.tsx` — non-dashboard route flow (earn/review/steps/details). | ||
| - `packages/widget/src/Dashboard.tsx` + `pages-dashboard/*` — dashboard variant UI. | ||
| - `packages/widget/src/providers/*` — global provider composition (API, query, wallet, tracking, theme, stores). | ||
| - `packages/widget/src/hooks/*` — feature and API hooks. | ||
| - `packages/widget/src/domain/*` — shared domain types/helpers. | ||
| - `packages/widget/src/translation/*` — i18n resources (`English`, `French`). | ||
| - `packages/widget/tests/*` — Vitest browser tests (MSW-backed). | ||
| - `packages/examples/*` — integration examples (`with-vite`, `with-vite-bundled`, `with-nextjs`, `with-cdn-script`). | ||
|
|
||
| ## Commands Agents Should Use | ||
|
|
||
| ### From repo root (all workspaces via Turbo) | ||
| - `pnpm build` — build all packages. | ||
| - `pnpm lint` — lint/type-check all packages. | ||
| - `pnpm test` — run all workspace tests. | ||
| - `pnpm format` — run formatting checks/tasks. | ||
|
|
||
| ### Focused widget commands (recommended for most tasks) | ||
| - `pnpm --filter @stakekit/widget {command}` | ||
|
|
||
| ## Agent Working Guidelines (short) | ||
| - Keep public API compatibility in `src/index.package.ts` and `src/index.bundle.ts`. | ||
| - When changing user-facing copy, update both: | ||
| - `packages/widget/src/translation/English/translations.json` | ||
| - `packages/widget/src/translation/French/translations.json` | ||
| - After changes, confirm nothing is broken with lint command which checks lint/type errors | ||
|
|
||
| ## Useful Context for Debugging | ||
| - API client is configured in `packages/widget/src/providers/api/api-client-provider.tsx`. | ||
| - React Query defaults are in `packages/widget/src/providers/query-client/index.tsx`. | ||
| - App-level config/env mapping is in `packages/widget/src/config/index.ts`. | ||
| - Test bootstrapping + MSW worker setup: | ||
| - `packages/widget/tests/utils/setup.ts` | ||
| - `packages/widget/tests/mocks/worker.ts` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| [tools] | ||
| node = "22" | ||
| pnpm = "10" | ||
| node = "24" | ||
| pnpm = "10" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| import type { PendingActionDto as LegacyPendingActionDto } from "@stakekit/api-hooks"; | ||
| import type { YieldPendingActionDto } from "../../providers/yield-api-client-provider/types"; | ||
|
|
||
| type PendingActionArgName = | ||
| | "amount" | ||
| | "validatorAddress" | ||
| | "validatorAddresses"; | ||
|
|
||
| export type AnyPendingActionDto = | ||
| | LegacyPendingActionDto | ||
| | YieldPendingActionDto; | ||
|
|
||
| export type PendingActionAmountConfig = { | ||
| required: boolean; | ||
| minimum: number | null; | ||
| maximum: number | null; | ||
| forceMax: boolean; | ||
| }; | ||
|
|
||
| export const isPendingActionAmountRequired = ( | ||
| pendingAction: AnyPendingActionDto | ||
| ) => !!getPendingActionAmountConfig(pendingAction)?.required; | ||
|
|
||
| export const isPendingActionValidatorAddressRequired = ( | ||
| pendingAction: AnyPendingActionDto | ||
| ) => !!getPendingActionArgument(pendingAction, "validatorAddress")?.required; | ||
|
|
||
| export const isPendingActionValidatorAddressesRequired = ( | ||
| pendingAction: AnyPendingActionDto | ||
| ) => !!getPendingActionArgument(pendingAction, "validatorAddresses")?.required; | ||
|
|
||
| export const getPendingActionAmountConfig = ( | ||
| pendingAction: AnyPendingActionDto | ||
| ): PendingActionAmountConfig | null => { | ||
| const amountArg = getPendingActionArgument(pendingAction, "amount"); | ||
|
|
||
| if (!amountArg) { | ||
| return null; | ||
| } | ||
|
|
||
| const minimum = toNumberOrNull(amountArg.minimum); | ||
| const maximum = toNumberOrNull(amountArg.maximum); | ||
|
|
||
| return { | ||
| required: !!amountArg.required, | ||
| minimum, | ||
| maximum, | ||
| forceMax: minimum === -1 && maximum === -1, | ||
| }; | ||
|
Comment on lines
+41
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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 Recommended fix: Keep values as ♻️ 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 |
||
| }; | ||
|
|
||
| const getPendingActionArgument = ( | ||
| pendingAction: AnyPendingActionDto, | ||
| name: PendingActionArgName | ||
| ) => { | ||
| const v2Field = ( | ||
| pendingAction as YieldPendingActionDto | ||
| ).arguments?.fields?.find( | ||
| ( | ||
| field: NonNullable<YieldPendingActionDto["arguments"]>["fields"][number] | ||
| ) => field.name === name | ||
| ); | ||
|
|
||
| if (v2Field) { | ||
| return { | ||
| required: !!v2Field.required, | ||
| minimum: v2Field.minimum ?? null, | ||
| maximum: v2Field.maximum ?? null, | ||
| }; | ||
| } | ||
|
|
||
| const legacyField = (pendingAction as LegacyPendingActionDto).args?.args?.[ | ||
| name | ||
| ] as | ||
| | { | ||
| required?: boolean; | ||
| minimum?: number | string | null; | ||
| maximum?: number | string | null; | ||
| } | ||
| | undefined; | ||
|
|
||
| if (!legacyField) { | ||
| return null; | ||
| } | ||
|
|
||
| return { | ||
| required: !!legacyField.required, | ||
| minimum: legacyField.minimum ?? null, | ||
| maximum: legacyField.maximum ?? null, | ||
| }; | ||
| }; | ||
|
|
||
| const toNumberOrNull = (value: number | string | null | undefined) => { | ||
| if (value === null || value === undefined) { | ||
| return null; | ||
| } | ||
|
|
||
| if (typeof value === "number") { | ||
| return Number.isFinite(value) ? value : null; | ||
| } | ||
|
|
||
| const parsed = Number(value); | ||
| return Number.isFinite(parsed) ? parsed : null; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: stakekit/widget
Length of output: 2205
🏁 Script executed:
Repository: stakekit/widget
Length of output: 86
🏁 Script executed:
Repository: stakekit/widget
Length of output: 86
🏁 Script executed:
Repository: stakekit/widget
Length of output: 228
🏁 Script executed:
Repository: stakekit/widget
Length of output: 3888
🏁 Script executed:
Repository: stakekit/widget
Length of output: 319
🏁 Script executed:
Repository: stakekit/widget
Length of output: 189
🏁 Script executed:
Repository: stakekit/widget
Length of output: 98
🏁 Script executed:
Repository: stakekit/widget
Length of output: 466
🏁 Script executed:
Repository: 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
tokenIconMappingcallback type to accept the union instead of casting.At Line 39, the cast suppresses type safety when the function parameter accepts
TokenDto | YieldTokenDto. SincetokenIconMappingis defined to only acceptTokenDto(inpackages/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