From efeafe7bfe026d8a3efd20aa6541f01a8a7e806b Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 4 Mar 2026 11:50:39 +0100 Subject: [PATCH 1/2] feat: migrate to yield api balances --- .changeset/lemon-drinks-prove.md | 5 + AGENTS.md | 46 + mise.toml | 4 +- packages/widget/package.json | 6 +- packages/widget/src/common/private-api.ts | 19 - .../src/components/atoms/token-icon/index.tsx | 3 +- .../hooks/use-variant-token-urls.ts | 5 +- .../token-icon/token-icon-container/index.tsx | 3 +- packages/widget/src/config/index.ts | 2 + packages/widget/src/domain/index.ts | 25 +- .../widget/src/domain/types/pending-action.ts | 104 + packages/widget/src/domain/types/positions.ts | 70 +- packages/widget/src/domain/types/price.ts | 13 +- packages/widget/src/domain/types/stake.ts | 2 +- packages/widget/src/hooks/api/use-prices.ts | 20 +- .../widget/src/hooks/api/use-tokens-prices.ts | 7 +- .../src/hooks/api/use-yield-balances-scan.ts | 88 +- .../src/hooks/use-position-balance-by-type.ts | 42 +- .../widget/src/hooks/use-position-balances.ts | 9 +- .../widget/src/hooks/use-positions-data.ts | 54 +- .../src/hooks/use-staked-or-liquid-balance.ts | 4 +- packages/widget/src/hooks/use-summary.tsx | 263 +- .../widget/src/hooks/use-yield-meta-info.tsx | 3 +- .../activity/position-balances.tsx | 3 +- .../components/positions-list-item.tsx | 30 +- .../positions/hooks/use-position-list-item.ts | 25 +- .../components/position-details-info.tsx | 34 - .../src/pages/complete/pages/common.page.tsx | 3 +- .../state/use-pending-action-deep-link.ts | 48 +- .../components/positions-list-item.tsx | 3 +- .../hooks/use-position-list-item.ts | 15 +- .../positions-page/hooks/use-positions.ts | 60 +- .../components/amount-block.tsx | 7 +- .../components/position-balances.tsx | 7 +- .../components/static-action-block.tsx | 13 +- .../hooks/use-pending-actions.ts | 22 +- .../hooks/use-position-details.ts | 38 +- .../hooks/use-stake-exit-request-dto.ts | 19 +- .../hooks/use-validator-addresses-handling.ts | 32 +- .../src/pages/position-details/hooks/utils.ts | 37 +- .../position-details.page.tsx | 38 - .../pages/position-details/state/index.tsx | 51 +- .../src/pages/position-details/state/types.ts | 23 +- .../src/pages/position-details/state/utils.ts | 14 +- .../widget/src/pages/review/hooks/use-fees.ts | 3 +- .../review/pages/common-page/common.page.tsx | 3 +- .../components/review-top-section.tsx | 3 +- .../src/providers/exit-stake-store/index.tsx | 3 +- packages/widget/src/providers/index.tsx | 97 +- .../providers/pending-action-store/index.tsx | 3 +- .../widget/src/providers/settings/types.ts | 1 + .../widget/src/providers/sk-wallet/errors.ts | 8 +- .../widget/src/providers/sk-wallet/index.tsx | 2 +- .../yield-api-client-provider/index.tsx | 65 + .../yield-api-client-provider/types.ts | 21 + .../src/translation/English/translations.json | 9 + .../src/translation/French/translations.json | 9 + .../widget/src/types/yield-api-schema.d.ts | 5223 +++++++++++++++++ packages/widget/src/utils/formatters.ts | 3 +- .../tests/use-cases/deep-links-flow/setup.ts | 25 + pnpm-lock.yaml | 190 +- 61 files changed, 6261 insertions(+), 726 deletions(-) create mode 100644 .changeset/lemon-drinks-prove.md create mode 100644 AGENTS.md create mode 100644 packages/widget/src/domain/types/pending-action.ts create mode 100644 packages/widget/src/providers/yield-api-client-provider/index.tsx create mode 100644 packages/widget/src/providers/yield-api-client-provider/types.ts create mode 100644 packages/widget/src/types/yield-api-schema.d.ts diff --git a/.changeset/lemon-drinks-prove.md b/.changeset/lemon-drinks-prove.md new file mode 100644 index 00000000..ae8fa51c --- /dev/null +++ b/.changeset/lemon-drinks-prove.md @@ -0,0 +1,5 @@ +--- +"@stakekit/widget": patch +--- + +feat: migrate to yield api balances diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..371e1c6f --- /dev/null +++ b/AGENTS.md @@ -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` diff --git a/mise.toml b/mise.toml index e264c145..984ab6e7 100644 --- a/mise.toml +++ b/mise.toml @@ -1,3 +1,3 @@ [tools] -node = "22" -pnpm = "10" \ No newline at end of file +node = "24" +pnpm = "10" diff --git a/packages/widget/package.json b/packages/widget/package.json index 8d8f9017..6d351456 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -55,7 +55,8 @@ "clean": "rm -rf dist", "preview": "vite -c vite/vite.config.dev.ts preview --outDir dist/website", "check-unused": "npx knip", - "check-circular-deps": "skott ./src/index.package.ts -m 'raw' && pnpm lint" + "check-circular-deps": "skott ./src/index.package.ts -m 'raw' && pnpm lint", + "gen:yield-api": "openapi-typescript https://docs.yield.xyz/openapi/appsyield-apiswagger-docsopenapi.yaml -o src/types/yield-api-schema.d.ts" }, "peerDependencies": { "react": ">=18", @@ -130,6 +131,9 @@ "mixpanel-browser": "^2.72.0", "motion": "12.23.26", "msw": "^2.12.4", + "openapi-fetch": "^0.17.0", + "openapi-react-query": "^0.5.4", + "openapi-typescript": "^7.13.0", "playwright": "^1.57.0", "postcss": "^8.5.6", "purify-ts": "2.1.0", diff --git a/packages/widget/src/common/private-api.ts b/packages/widget/src/common/private-api.ts index 23df6ebb..4a657f0d 100644 --- a/packages/widget/src/common/private-api.ts +++ b/packages/widget/src/common/private-api.ts @@ -3,8 +3,6 @@ import { type TokenBalanceScanDto, type TokenBalanceScanResponseDto, type ValidatorSearchResultDto, - type YieldBalanceScanRequestDto, - type YieldBalancesWithIntegrationIdDto, type YieldDto, } from "@stakekit/api-hooks"; @@ -25,23 +23,6 @@ export const tokenTokenBalancesScan = ( }); }; -/** - * Scans for yield balances among enabled yields. - * @summary Scan for yield balances - */ -export const yieldYieldBalancesScan = ( - yieldBalanceScanRequestDto: YieldBalanceScanRequestDto, - signal?: AbortSignal -) => { - return customFetch({ - url: "/v1/yields/balances/scan", - method: "POST", - headers: { "Content-Type": "application/json" }, - data: yieldBalanceScanRequestDto, - signal, - }); -}; - /** * Returns a yield that is associated with given integration ID * @summary Get a yield given an integration ID diff --git a/packages/widget/src/components/atoms/token-icon/index.tsx b/packages/widget/src/components/atoms/token-icon/index.tsx index a237dbe7..e6a90eeb 100644 --- a/packages/widget/src/components/atoms/token-icon/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/index.tsx @@ -1,5 +1,6 @@ import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; import { useSettings } from "../../../providers/settings"; +import type { YieldTokenDto } from "../../../providers/yield-api-client-provider/types"; import type { Atoms } from "../../../styles/theme/atoms.css"; import { NetworkLogoImage } from "./network-icon-image"; import { TokenIconContainer } from "./token-icon-container"; @@ -12,7 +13,7 @@ export const TokenIcon = ({ tokenNetworkLogoHw, hideNetwork, }: { - token: TokenDto; + token: TokenDto | YieldTokenDto; metadata?: YieldMetadataDto; tokenLogoHw?: Atoms["hw"]; tokenNetworkLogoHw?: Atoms["hw"]; diff --git a/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts b/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts index 94930944..cdfa3c0c 100644 --- a/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts +++ b/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-token-urls.ts @@ -3,9 +3,10 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { config } from "../../../../../config"; import { useSettings } from "../../../../../providers/settings"; +import type { YieldTokenDto } from "../../../../../providers/yield-api-client-provider/types"; export const useVariantTokenUrls = ( - token: TokenDto, + token: TokenDto | YieldTokenDto, metadata?: YieldMetadataDto ): { mainUrl: string | undefined; @@ -35,7 +36,7 @@ export const useVariantTokenUrls = ( const tokenMappingResult = Maybe.fromNullable(tokenIconMapping) .chainNullable((mapping) => { if (typeof mapping === "function") { - return mapping(token); + return mapping(token as TokenDto); } return mapping[token.symbol]; diff --git a/packages/widget/src/components/atoms/token-icon/token-icon-container/index.tsx b/packages/widget/src/components/atoms/token-icon/token-icon-container/index.tsx index a17d64f8..9b587b46 100644 --- a/packages/widget/src/components/atoms/token-icon/token-icon-container/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/token-icon-container/index.tsx @@ -1,12 +1,13 @@ import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; import type { Networks } from "@stakekit/common"; import type { ReactElement } from "react"; +import type { YieldTokenDto } from "../../../../providers/yield-api-client-provider/types"; import { Box } from "../../box"; import { useVariantNetworkUrls } from "./hooks/use-variant-network-urls"; import { useVariantTokenUrls } from "./hooks/use-variant-token-urls"; type TokenIconContainerProps = { - token: TokenDto; + token: TokenDto | YieldTokenDto; metadata?: YieldMetadataDto; hideNetwork?: boolean; children: (props: TokenIconContainerReturnType) => ReactElement; diff --git a/packages/widget/src/config/index.ts b/packages/widget/src/config/index.ts index d8045bd5..0a04004d 100644 --- a/packages/widget/src/config/index.ts +++ b/packages/widget/src/config/index.ts @@ -15,6 +15,8 @@ export const config = { appPrefix: "sk-widget", env: { apiUrl: import.meta.env.VITE_API_URL ?? "https://api.stakek.it/", + yieldsApiUrl: + import.meta.env.VITE_YIELDS_API_URL ?? "https://api.yield.xyz", isTestMode: import.meta.env.MODE === "test", isDevMode: import.meta.env.MODE === "development", forceAddress: import.meta.env.VITE_FORCE_ADDRESS, diff --git a/packages/widget/src/domain/index.ts b/packages/widget/src/domain/index.ts index d1cc1834..095ea757 100644 --- a/packages/widget/src/domain/index.ts +++ b/packages/widget/src/domain/index.ts @@ -1,6 +1,5 @@ import type { ActionDto, - PendingActionDto, TokenDto, TransactionDto, TransactionStatus, @@ -9,15 +8,25 @@ import type { import BigNumber from "bignumber.js"; import { Left, type Maybe, Right } from "purify-ts"; import type { Override } from "../types/utils"; +import type { AnyPendingActionDto } from "./types/pending-action"; +import { + isPendingActionValidatorAddressesRequired, + isPendingActionValidatorAddressRequired, +} from "./types/pending-action"; import type { TokenString } from "./types/tokens"; export { getTokenPriceInUSD } from "./types/price"; -export const tokenString = (token: TokenDto): TokenString => { - return `${token.network}-${token.address?.toLowerCase()}`; +type TokenLike = Pick & { + network: string; + address?: string; }; -export const equalTokens = (a: TokenDto, b: TokenDto) => +export const tokenString = (token: TokenLike): TokenString => { + return `${token.network}-${token.address?.toLowerCase() ?? ""}` as TokenString; +}; + +export const equalTokens = (a: TokenLike, b: TokenLike) => tokenString(a) === tokenString(b) && a.symbol === b.symbol; export const stakeTokenSameAsGasToken = ({ @@ -81,11 +90,11 @@ export const getValidStakeSessionTx = (stakeDto: ActionDto) => { export const isTxError = (txStatus: TransactionStatus) => txStatus === "FAILED" || txStatus === "BLOCKED"; -export const PAMultiValidatorsRequired = (pa: PendingActionDto) => - !!pa.args?.args?.validatorAddresses?.required; +export const PAMultiValidatorsRequired = (pa: AnyPendingActionDto) => + isPendingActionValidatorAddressesRequired(pa); -export const PASingleValidatorRequired = (pa: PendingActionDto) => - !!pa.args?.args?.validatorAddress?.required; +export const PASingleValidatorRequired = (pa: AnyPendingActionDto) => + isPendingActionValidatorAddressRequired(pa); export const skNormalizeChainId = (chainId: string) => { const cId = Number(chainId); diff --git a/packages/widget/src/domain/types/pending-action.ts b/packages/widget/src/domain/types/pending-action.ts new file mode 100644 index 00000000..e8a3fbbd --- /dev/null +++ b/packages/widget/src/domain/types/pending-action.ts @@ -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, + }; +}; + +const getPendingActionArgument = ( + pendingAction: AnyPendingActionDto, + name: PendingActionArgName +) => { + const v2Field = ( + pendingAction as YieldPendingActionDto + ).arguments?.fields?.find( + ( + field: NonNullable["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; +}; diff --git a/packages/widget/src/domain/types/positions.ts b/packages/widget/src/domain/types/positions.ts index 0bfb385a..f91b63fc 100644 --- a/packages/widget/src/domain/types/positions.ts +++ b/packages/widget/src/domain/types/positions.ts @@ -1,14 +1,13 @@ +import BigNumber from "bignumber.js"; import type { - BalanceTypes, - TokenDto, YieldBalanceDto, - YieldBalancesWithIntegrationIdDto, -} from "@stakekit/api-hooks"; -import BigNumber from "bignumber.js"; -import { equalTokens } from ".."; + YieldBalancesByYieldDto, + YieldBalanceType, +} from "../../providers/yield-api-client-provider/types"; +import type { components } from "../../types/yield-api-schema"; export type PositionBalancesByType = Map< - BalanceTypes, + YieldBalanceType, (YieldBalanceDto & { tokenPriceInUsd: BigNumber; })[] @@ -16,12 +15,18 @@ export type PositionBalancesByType = Map< export type PositionDetailsLabelType = "hasFrozenV1"; +type BalanceType = "validators" | "default"; + +export type BalanceDataKey = + | BalanceType + | `validator::${components["schemas"]["ValidatorDto"]["address"]}`; + export type PositionsData = Map< - YieldBalancesWithIntegrationIdDto["integrationId"], + YieldBalancesByYieldDto["yieldId"], { - integrationId: YieldBalancesWithIntegrationIdDto["integrationId"]; + yieldId: YieldBalancesByYieldDto["yieldId"]; balanceData: Map< - YieldBalanceDto["groupId"], + BalanceDataKey, { balances: YieldBalanceDto[] } & ( | { type: "validators"; validatorsAddresses: string[] } | { type: "default" } @@ -30,22 +35,29 @@ export type PositionsData = Map< } >; -export const getPositionTotalAmount = ({ - token, - balances, -}: { - token: TokenDto & { pricePerShare: YieldBalanceDto["pricePerShare"] }; - balances: YieldBalanceDto[]; -}) => - balances.reduce((acc, b) => { - if (b.token.isPoints) return acc; - - if (equalTokens(b.token, token)) { - return BigNumber(b.amount).plus(acc); - } - - return BigNumber(b.amount) - .times(b.pricePerShare) - .dividedBy(token.pricePerShare) - .plus(acc); - }, new BigNumber(0)); +export const getPositionBalanceDataKey = ( + balance: YieldBalanceDto +): BalanceDataKey => { + if (Array.isArray(balance.validators) && balance.validators.length > 1) { + return "validators"; + } + + if (balance.validator?.address) { + return `validator::${balance.validator.address}` as BalanceDataKey; + } + + return "default"; +}; + +export const getPositionTotalAmount = (balances: YieldBalanceDto[]) => + balances.reduce( + (acc, b) => { + if (b.token.isPoints) return acc; + + acc.amount = BigNumber(b.amount).plus(acc.amount); + acc.amountUsd = BigNumber(b.amountUsd ?? 0).plus(acc.amountUsd); + + return acc; + }, + { amount: new BigNumber(0), amountUsd: new BigNumber(0) } + ); diff --git a/packages/widget/src/domain/types/price.ts b/packages/widget/src/domain/types/price.ts index b63a7433..76494684 100644 --- a/packages/widget/src/domain/types/price.ts +++ b/packages/widget/src/domain/types/price.ts @@ -1,9 +1,14 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { tokenString } from ".."; import type { TokenString } from "./tokens"; +type PriceToken = { + symbol: string; + network: string; + address?: string; +}; + export type Price = { price: number | undefined; price24H: number | undefined; @@ -12,7 +17,7 @@ export type Price = { export class Prices { constructor(public value: Map) {} - getByToken(token: TokenDto) { + getByToken(token: PriceToken) { return Maybe.fromNullable(this.value.get(tokenString(token))); } } @@ -24,8 +29,8 @@ export const getTokenPriceInUSD = ({ prices, pricePerShare, }: { - token: TokenDto; - baseToken: TokenDto | null; + token: PriceToken; + baseToken: PriceToken | null; amount: string | BigNumber; pricePerShare: string | null; prices: Prices; diff --git a/packages/widget/src/domain/types/stake.ts b/packages/widget/src/domain/types/stake.ts index 115a5b71..ce1a343a 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -174,7 +174,7 @@ export const getMinStakeAmount = ( const hasStaked = Maybe.fromNullable(positionsData.get(yieldDto.id)) .map((val) => [...val.balanceData.values()]) .map((val) => - val.some((v) => v.balances.some((b) => b.type === "staked")) + val.some((v) => v.balances.some((b) => b.type === "active")) ) .orDefault(false); diff --git a/packages/widget/src/hooks/api/use-prices.ts b/packages/widget/src/hooks/api/use-prices.ts index b048feec..f9942941 100644 --- a/packages/widget/src/hooks/api/use-prices.ts +++ b/packages/widget/src/hooks/api/use-prices.ts @@ -3,6 +3,7 @@ import { useTokenGetTokenPrices } from "@stakekit/api-hooks"; import { useCallback } from "react"; import { createSelector } from "reselect"; import type { Prices } from "../../domain/types/price"; +import type { YieldTokenDto } from "../../providers/yield-api-client-provider/types"; import { priceResponseDtoToPrices } from "../../utils/mappers"; const defaultParam: PriceRequestDto = { @@ -17,14 +18,29 @@ const pricesSelector = createSelector( (val) => priceResponseDtoToPrices(val) ); +type PriceRequestInput = Omit & { + tokenList: (PriceRequestDto["tokenList"][number] | YieldTokenDto)[]; +}; + export const usePrices = ( - priceRequestDto: PriceRequestDto | null | undefined, + priceRequestDto: PriceRequestInput | null | undefined, opts?: { enabled?: boolean; select?: (val: Prices) => T; } ) => { - return useTokenGetTokenPrices(priceRequestDto ?? defaultParam, { + const requestDto = priceRequestDto + ? ({ + ...priceRequestDto, + tokenList: priceRequestDto.tokenList.map((token) => ({ + ...token, + network: + token.network as PriceRequestDto["tokenList"][number]["network"], + })), + } satisfies PriceRequestDto) + : defaultParam; + + return useTokenGetTokenPrices(requestDto, { query: { enabled: !!priceRequestDto && opts?.enabled, select: useCallback( diff --git a/packages/widget/src/hooks/api/use-tokens-prices.ts b/packages/widget/src/hooks/api/use-tokens-prices.ts index 43a716be..ff0fb803 100644 --- a/packages/widget/src/hooks/api/use-tokens-prices.ts +++ b/packages/widget/src/hooks/api/use-tokens-prices.ts @@ -1,7 +1,8 @@ -import type { PriceRequestDto, TokenDto, YieldDto } from "@stakekit/api-hooks"; +import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { config } from "../../config"; +import type { YieldTokenDto } from "../../providers/yield-api-client-provider/types"; import { useBaseToken } from "../use-base-token"; import { useGasFeeToken } from "../use-gas-fee-token"; import { usePrices } from "./use-prices"; @@ -13,7 +14,7 @@ export const useTokensPrices = ({ token, yieldDto, }: { - token: Maybe; + token: Maybe; yieldDto: Maybe; }) => { const baseToken = useBaseToken(yieldDto); @@ -22,7 +23,7 @@ export const useTokensPrices = ({ const priceRequestDto = useMemo( () => Maybe.fromRecord({ baseToken, gasFeeToken, token }) - .map((val) => ({ + .map((val) => ({ currency: config.currency, tokenList: [val.token, val.baseToken, val.gasFeeToken], })) diff --git a/packages/widget/src/hooks/api/use-yield-balances-scan.ts b/packages/widget/src/hooks/api/use-yield-balances-scan.ts index eed36cef..99ad6117 100644 --- a/packages/widget/src/hooks/api/use-yield-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-yield-balances-scan.ts @@ -1,22 +1,22 @@ -import type { - YieldBalanceScanRequestDto, - YieldBalancesWithIntegrationIdDto, -} from "@stakekit/api-hooks"; -import { useQuery } from "@tanstack/react-query"; -import { Just, Maybe } from "purify-ts"; +import type { UseQueryResult } from "@tanstack/react-query"; +import { Maybe } from "purify-ts"; import { useCallback, useMemo } from "react"; -import { yieldYieldBalancesScan } from "../../common/private-api"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; import { useActionHistoryData } from "../../providers/stake-history"; +import { useYieldApiClient } from "../../providers/yield-api-client-provider"; +import type { + YieldBalancesByYieldDto, + YieldBalancesRequestDto, +} from "../../providers/yield-api-client-provider/types"; import { useInvalidateQueryNTimes } from "../use-invalidate-query-n-times"; -export const useYieldBalancesScan = < - T = YieldBalancesWithIntegrationIdDto[], ->(opts?: { - select?: (data: YieldBalancesWithIntegrationIdDto[]) => T; -}) => { - const { network, address, additionalAddresses } = useSKWallet(); +export const useYieldBalancesScan = (opts?: { + select?: (data: YieldBalancesByYieldDto[]) => T; + // biome-ignore lint/suspicious/noExplicitAny: fix later +}): UseQueryResult => { + const yieldApi = useYieldApiClient(); + const { network, address } = useSKWallet(); const actionHistoryData = useActionHistoryData(); @@ -28,38 +28,51 @@ export const useYieldBalancesScan = < const param = useMemo( () => Maybe.fromRecord({ - additionalAddresses: Just(additionalAddresses ?? undefined), address: Maybe.fromNullable(address), network: Maybe.fromNullable(network), - }).mapOrDefault<{ dto: YieldBalanceScanRequestDto; enabled: boolean }>( + }).mapOrDefault<{ dto: YieldBalancesRequestDto; enabled: boolean }>( (val) => ({ enabled: true, dto: { - addresses: { - address: val.address, - additionalAddresses: val.additionalAddresses, - }, - network: val.network, + queries: [ + { + address: val.address, + network: + val.network as YieldBalancesRequestDto["queries"][number]["network"], + }, + ], }, }), { enabled: false, dto: { - addresses: { address: "", additionalAddresses: undefined }, - network: "ethereum", + queries: [{ address: "", network: "ethereum" }], }, } ), - [additionalAddresses, address, network] + [address, network] ); - const res = useQuery({ - queryKey: getYieldYieldBalancesScanQueryKey(param.dto), - queryFn: () => yieldYieldBalancesScan(param.dto), - enabled: param.enabled, - select: opts?.select, - refetchInterval: 1000 * 60, - }); + const res = yieldApi.useQuery( + "post", + "/v1/yields/balances", + { + body: param.dto, + }, + { + enabled: param.enabled, + refetchInterval: 1000 * 60, + select: (data) => { + const items = data.items as YieldBalancesByYieldDto[]; + + if (opts?.select) { + return opts.select(items); + } + + return items as T; + }, + } + ); /** * This is a hack to make sure that the yield balances are updated after a transaction @@ -67,7 +80,7 @@ export const useYieldBalancesScan = < useInvalidateQueryNTimes({ enabled: !!lastActionTimestamp, key: ["yield-balances-refetch", lastActionTimestamp], - queryKey: [getYieldYieldBalancesScanQueryKey(param.dto)[0]], + queryKey: getYieldYieldBalancesScanQueryKey(), waitMs: 4000, shouldRefetch: () => !!lastActionTimestamp && Date.now() - lastActionTimestamp < 1000 * 12, @@ -82,18 +95,11 @@ export const useInvalidateYieldBalances = () => { return useCallback( () => queryClient.invalidateQueries({ - queryKey: [ - getYieldYieldBalancesScanQueryKey( - {} as YieldBalanceScanRequestDto - )[0], - ], + queryKey: getYieldYieldBalancesScanQueryKey(), }), [queryClient] ); }; -const getYieldYieldBalancesScanQueryKey = ( - yieldBalanceScanRequestDto: YieldBalanceScanRequestDto -) => { - return ["/v1/yields/balances/scan", yieldBalanceScanRequestDto] as const; -}; +const getYieldYieldBalancesScanQueryKey = () => + ["post", "/v1/yields/balances"] as const; diff --git a/packages/widget/src/hooks/use-position-balance-by-type.ts b/packages/widget/src/hooks/use-position-balance-by-type.ts index 5a9967db..cb7e7af4 100644 --- a/packages/widget/src/hooks/use-position-balance-by-type.ts +++ b/packages/widget/src/hooks/use-position-balance-by-type.ts @@ -1,67 +1,45 @@ -import type { TokenDto, YieldBalanceDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; -import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { createSelector } from "reselect"; -import { getTokenPriceInUSD } from "../domain"; import type { PositionBalancesByType } from "../domain/types/positions"; -import type { Prices } from "../domain/types/price"; -import type { usePrices } from "./api/use-prices"; +import type { YieldBalanceDto } from "../providers/yield-api-client-provider/types"; import type { usePositionBalances } from "./use-position-balances"; export const usePositionBalanceByType = ({ positionBalancesData, - prices, - baseToken, }: { positionBalancesData: ReturnType["data"]; - prices: ReturnType>; - baseToken: Maybe; }) => { /** * @summary Position balance by type */ return useMemo( () => - Maybe.fromRecord({ positionBalancesData, baseToken }).map((val) => - getPositionBalanceByTypeWithPrices({ - baseToken: val.baseToken, - prices: prices.data, - pvd: val.positionBalancesData.balances, + positionBalancesData.map((val) => + getPositionBalanceByTypeWithUsd({ + pvd: val.balances, }) ), - [positionBalancesData, prices, baseToken] + [positionBalancesData] ); }; type Args = { - prices: Prices | undefined; pvd: YieldBalanceDto[]; - baseToken: TokenDto; }; -const selectPrices = (val: Args) => val.prices; const selectPvd = (val: Args) => val.pvd; -const selectBaseToken = (val: Args) => val.baseToken; -export const getPositionBalanceByTypeWithPrices = createSelector( - selectPrices, +export const getPositionBalanceByTypeWithUsd = createSelector( selectPvd, - selectBaseToken, - (prices, pvd, baseToken) => + (pvd) => pvd.reduce((acc, cur) => { const amount = new BigNumber(cur.amount); if (amount.isZero() || amount.isNaN()) return acc; - const tokenPriceInUsd = prices - ? getTokenPriceInUSD({ - amount: cur.amount, - prices, - token: cur.token, - pricePerShare: cur.pricePerShare, - baseToken, - }) - : new BigNumber(0); + const tokenPriceInUsd = new BigNumber( + String(cur.amountUsd ?? 0).replace(/,/g, "") + ); const prev = acc.get(cur.type); diff --git a/packages/widget/src/hooks/use-position-balances.ts b/packages/widget/src/hooks/use-position-balances.ts index 85ef2568..a4d3a6db 100644 --- a/packages/widget/src/hooks/use-position-balances.ts +++ b/packages/widget/src/hooks/use-position-balances.ts @@ -1,5 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import type { BalanceDataKey } from "../domain/types/positions"; import { usePositionData } from "./use-position-data"; export const usePositionBalances = ({ @@ -15,9 +16,11 @@ export const usePositionBalances = ({ () => Maybe.fromRecord({ positionData: data, - balanceId: Maybe.fromNullable(balanceId), - }).chainNullable((val) => - val.positionData.balanceData.get(val.balanceId) + balanceId: Maybe.fromNullable(balanceId as BalanceDataKey), + }).chainNullable( + (val) => + val.positionData.balanceData.get(val.balanceId) ?? + val.positionData.balanceData.values().next().value ), [balanceId, data] ); diff --git a/packages/widget/src/hooks/use-positions-data.ts b/packages/widget/src/hooks/use-positions-data.ts index 1ad3b2b5..7ced4b88 100644 --- a/packages/widget/src/hooks/use-positions-data.ts +++ b/packages/widget/src/hooks/use-positions-data.ts @@ -1,10 +1,14 @@ -import type { - YieldBalanceDto, - YieldBalancesWithIntegrationIdDto, -} from "@stakekit/api-hooks"; import { useMemo } from "react"; import { createSelector } from "reselect"; -import type { PositionsData } from "../domain/types/positions"; +import { + type BalanceDataKey, + getPositionBalanceDataKey, + type PositionsData, +} from "../domain/types/positions"; +import type { + YieldBalanceDto, + YieldBalancesByYieldDto, +} from "../providers/yield-api-client-provider/types"; import { useYieldBalancesScan } from "./api/use-yield-balances-scan"; export const usePositionsData = () => { @@ -20,41 +24,43 @@ export const usePositionsData = () => { return { data: val, ...rest }; }; -type YieldBalanceDtoID = YieldBalanceDto["groupId"]; - const positionsDataSelector = createSelector( - (balancesData: YieldBalancesWithIntegrationIdDto[]) => balancesData, + (balancesData: YieldBalancesByYieldDto[]) => balancesData, (balancesData) => balancesData.reduce((acc, val) => { - acc.set(val.integrationId, { - integrationId: val.integrationId, + acc.set(val.yieldId, { + yieldId: val.yieldId, balanceData: [...val.balances] - .sort((a, b) => (a.groupId ?? "").localeCompare(b.groupId ?? "")) + .sort((a, b) => + getPositionBalanceDataKey(a).localeCompare( + getPositionBalanceDataKey(b) + ) + ) .reduce((acc, b) => { - const prev = acc.get(b.groupId); + const key = getPositionBalanceDataKey(b); + const prev = acc.get(key); + const validatorsAddresses = getBalanceValidatorAddresses(b); if (prev) { prev.balances.push(b); } else { - if (b.validatorAddresses || b.validatorAddress || b.providerId) { - acc.set(b.groupId, { + if (key === "default") { + acc.set(key, { balances: [b], - type: "validators", - validatorsAddresses: - b.validatorAddresses ?? - (b.providerId ? [b.providerId] : [b.validatorAddress!]), + type: "default", }); } else { - acc.set(b.groupId, { + acc.set(key, { balances: [b], - type: "default", + type: "validators", + validatorsAddresses, }); } } return acc; }, new Map< - YieldBalanceDtoID, + BalanceDataKey, { balances: YieldBalanceDto[] } & ( | { type: "validators"; validatorsAddresses: string[] } | { type: "default" } @@ -65,3 +71,9 @@ const positionsDataSelector = createSelector( return acc; }, new Map() as PositionsData) ); + +const getBalanceValidatorAddresses = (balance: YieldBalanceDto) => + ( + balance.validators?.map((validator) => validator.address) ?? + (balance.validator?.address ? [balance.validator.address] : []) + ).filter(Boolean); diff --git a/packages/widget/src/hooks/use-staked-or-liquid-balance.ts b/packages/widget/src/hooks/use-staked-or-liquid-balance.ts index 9a19eedd..544a81eb 100644 --- a/packages/widget/src/hooks/use-staked-or-liquid-balance.ts +++ b/packages/widget/src/hooks/use-staked-or-liquid-balance.ts @@ -8,9 +8,7 @@ export const useStakedOrLiquidBalance = ( return useMemo( () => positionBalancesByType.chain((pbbt) => - Maybe.fromNullable(pbbt.get("staked")).altLazy(() => - Maybe.fromNullable(pbbt.get("available")) - ) + Maybe.fromNullable(pbbt.get("active")) ), [positionBalancesByType] ); diff --git a/packages/widget/src/hooks/use-summary.tsx b/packages/widget/src/hooks/use-summary.tsx index 50f30cc1..1177a648 100644 --- a/packages/widget/src/hooks/use-summary.tsx +++ b/packages/widget/src/hooks/use-summary.tsx @@ -20,17 +20,19 @@ import { const SummaryContext = createContext< | { - allPositionsQuery: UseQueryResult< - { - allPositions: { - yieldName: string; - usdAmount: number; - providerDetails: ReturnType; - }[]; - allPositionsSum: BigNumber; - }, - StakeKitErrorDto - >; + allPositionsQuery: { + data: + | { + allPositions: { + yieldName: string; + usdAmount: number; + providerDetails: ReturnType; + }[]; + allPositionsSum: BigNumber; + } + | undefined; + isLoading: boolean; + }; rewardsPositionsQuery: UseQueryResult< { rewardsPositions: { @@ -45,7 +47,10 @@ const SummaryContext = createContext< }, StakeKitErrorDto >; - averageApyQuery: UseQueryResult; + averageApyQuery: { + data: BigNumber | undefined; + isLoading: boolean; + }; availableBalanceSumQuery: UseQueryResult; } | undefined @@ -86,84 +91,54 @@ export const SummaryProvider = ({ ), }); - const allPositionsQuery = usePrices( - { - currency: config.currency, - tokenList: useMemo(() => { - if (!multiYieldsMapQuery.data) return []; - - return positionsData.data.flatMap((v) => { - const yieldDto = multiYieldsMapQuery.data.get(v.integrationId); - - if (!yieldDto) return []; - - const baseToken = getBaseToken(yieldDto); - - return [...v.allBalances.map((b) => b.token), baseToken]; - }); - }, [multiYieldsMapQuery.data, positionsData.data]), - }, - { - enabled: !multiYieldsMapQuery.isLoading, - select: useCallback( - (prices: Prices) => { - if (!positionsData.data || !multiYieldsMapQuery.data) { - return { allPositions: [], allPositionsSum: new BigNumber(0) }; - } - - const allPositions = positionsData.data.flatMap((p) => { - const yieldDto = multiYieldsMapQuery.data.get(p.integrationId); - - if (!yieldDto) return []; - - const baseToken = getBaseToken(yieldDto); - - const pricePerShare = "1"; - - const positionTotalAmount = getPositionTotalAmount({ - token: { ...baseToken, pricePerShare }, - balances: p.balancesWithAmount, - }); - - const yields = [...multiYieldsMapQuery.data.values()]; - - const providerDetails = getProviderDetails({ - integrationData: Maybe.of(yieldDto), - validatorAddress: - p.type === "validators" - ? List.head(p.validatorsAddresses) - : Maybe.empty(), - selectedProviderYieldId: Maybe.empty(), - yields: Maybe.of(yields), - }); - - return { - yieldName: yieldDto.metadata.name, - providerDetails, - usdAmount: getTokenPriceInUSD({ - baseToken, - amount: positionTotalAmount, - pricePerShare, - token: baseToken, - prices, - }).toNumber(), - }; - }); - - const allPositionsSum = allPositions.reduce( - (acc, p) => acc.plus(p.usdAmount), - new BigNumber(0) - ); - - return { - allPositions, - allPositionsSum, - }; - }, - [multiYieldsMapQuery.data, positionsData.data] - ), + const allPositionsQuery = useMemo(() => { + if (!multiYieldsMapQuery.data) { + return { + data: undefined as undefined, + isLoading: multiYieldsMapQuery.isLoading, + }; } - ); + + const allPositions = positionsData.data.flatMap((p) => { + const yieldDto = multiYieldsMapQuery.data.get(p.integrationId); + + if (!yieldDto) return []; + + const positionTotalAmount = getPositionTotalAmount(p.balancesWithAmount); + + const yields = [...multiYieldsMapQuery.data.values()]; + + const providerDetails = getProviderDetails({ + integrationData: Maybe.of(yieldDto), + validatorAddress: + p.type === "validators" + ? List.head(p.validatorsAddresses) + : Maybe.empty(), + selectedProviderYieldId: Maybe.empty(), + yields: Maybe.of(yields), + }); + + return { + yieldName: yieldDto.metadata.name, + providerDetails, + usdAmount: positionTotalAmount.amountUsd.toNumber(), + }; + }); + + const allPositionsSum = allPositions.reduce( + (acc, p) => acc.plus(p.usdAmount), + new BigNumber(0) + ); + + return { + data: { allPositions, allPositionsSum }, + isLoading: false as const, + }; + }, [ + multiYieldsMapQuery.data, + multiYieldsMapQuery.isLoading, + positionsData.data, + ]); const rewardsPositionsQuery = usePrices( { @@ -246,81 +221,53 @@ export const SummaryProvider = ({ } ); - const averageApyQuery = usePrices( - { - currency: config.currency, - tokenList: useMemo(() => { - if (!multiYieldsMapQuery.data) return []; - - return positionsData.data.flatMap((v) => { - const yieldDto = multiYieldsMapQuery.data.get(v.integrationId); + const averageApyQuery = useMemo(() => { + if (!multiYieldsMapQuery.data) { + return { + data: undefined as undefined, + isLoading: multiYieldsMapQuery.isLoading, + }; + } - if (!yieldDto) return []; + const { totalWeightedApy, totalValue } = positionsData.data.reduce( + (acc, p) => { + const yieldDto = multiYieldsMapQuery.data.get(p.integrationId); - const baseToken = getBaseToken(yieldDto); + if (!yieldDto) return acc; - return [...v.allBalances.map((b) => b.token), baseToken]; - }); - }, [multiYieldsMapQuery.data, positionsData.data]), - }, - { - enabled: !multiYieldsMapQuery.isLoading, - select: useCallback( - (prices: Prices) => { - if (!positionsData.data || !multiYieldsMapQuery.data) { - return new BigNumber(0); - } + const positionTotalAmount = getPositionTotalAmount( + p.balancesWithAmount + ); - const { totalWeightedApy, totalValue } = positionsData.data.reduce( - (acc, p) => { - const yieldDto = multiYieldsMapQuery.data.get(p.integrationId); - - if (!yieldDto) return acc; - - const baseToken = getBaseToken(yieldDto); - - const pricePerShare = "1"; - - const positionTotalAmount = getPositionTotalAmount({ - token: { ...baseToken, pricePerShare }, - balances: p.balancesWithAmount, - }); - - const usdAmount = getTokenPriceInUSD({ - baseToken, - amount: positionTotalAmount, - pricePerShare, - token: baseToken, - prices, - }); - - if (yieldDto.rewardRate > 0 && usdAmount.gt(0)) { - return { - totalWeightedApy: acc.totalWeightedApy.plus( - usdAmount.times(yieldDto.rewardRate * 100) - ), - totalValue: acc.totalValue.plus(usdAmount), - }; - } - - return acc; - }, - { - totalWeightedApy: new BigNumber(0), - totalValue: new BigNumber(0), - } - ); + const usdAmount = positionTotalAmount.amountUsd; - if (totalValue.gt(0)) { - return totalWeightedApy.div(totalValue); - } + if (yieldDto.rewardRate > 0 && usdAmount.gt(0)) { + return { + totalWeightedApy: acc.totalWeightedApy.plus( + usdAmount.times(yieldDto.rewardRate * 100) + ), + totalValue: acc.totalValue.plus(usdAmount), + }; + } - return new BigNumber(0); - }, - [multiYieldsMapQuery.data, positionsData.data] - ), - } - ); + return acc; + }, + { + totalWeightedApy: new BigNumber(0), + totalValue: new BigNumber(0), + } + ); + + const data = totalValue.gt(0) + ? totalWeightedApy.div(totalValue) + : new BigNumber(0); + + return { data, isLoading: false as const }; + }, [ + multiYieldsMapQuery.data, + multiYieldsMapQuery.isLoading, + positionsData.data, + ]); const tokenBalancesScan = useTokenBalancesScan(); diff --git a/packages/widget/src/hooks/use-yield-meta-info.tsx b/packages/widget/src/hooks/use-yield-meta-info.tsx index b614624b..f714e86d 100644 --- a/packages/widget/src/hooks/use-yield-meta-info.tsx +++ b/packages/widget/src/hooks/use-yield-meta-info.tsx @@ -5,6 +5,7 @@ import { type ReactNode, useMemo } from "react"; import { Trans, useTranslation } from "react-i18next"; import { SKAnchor } from "../components/atoms/anchor"; import { isEthenaUsdeStaking } from "../domain/types/yields"; +import type { YieldTokenDto } from "../providers/yield-api-client-provider/types"; import { capitalizeFirstLowerRest } from "../utils/text"; export const useYieldMetaInfo = ({ @@ -16,7 +17,7 @@ export const useYieldMetaInfo = ({ validators: { [Key in keyof Pick]?: ValidatorDto[Key]; }[]; - tokenDto: Maybe; + tokenDto: Maybe; }) => { const { t } = useTranslation(); diff --git a/packages/widget/src/pages-dashboard/activity/position-balances.tsx b/packages/widget/src/pages-dashboard/activity/position-balances.tsx index 6faab230..8678ed06 100644 --- a/packages/widget/src/pages-dashboard/activity/position-balances.tsx +++ b/packages/widget/src/pages-dashboard/activity/position-balances.tsx @@ -1,9 +1,10 @@ -import type { YieldBalanceDto, YieldDto } from "@stakekit/api-hooks"; +import type { YieldDto } from "@stakekit/api-hooks"; import type BigNumber from "bignumber.js"; import { useTranslation } from "react-i18next"; import { Box } from "../../components/atoms/box"; import { TokenIcon } from "../../components/atoms/token-icon"; import { Text } from "../../components/atoms/typography/text"; +import type { YieldBalanceDto } from "../../providers/yield-api-client-provider/types"; import { defaultFormattedNumber } from "../../utils"; export const PositionBalances = ({ diff --git a/packages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsx b/packages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsx index 9b204254..e695915e 100644 --- a/packages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsx +++ b/packages/widget/src/pages-dashboard/overview/positions/components/positions-list-item.tsx @@ -165,28 +165,20 @@ export const PositionsListItem = memo( {/* Staked */} - {totalAmountFormatted - .map((v) => ( - <> - {v} + {totalAmountFormatted} - {totalAmountPriceFormatted - .map((v) => ( - - {v}$ - - )) - .orDefaultLazy(() => ( - - - ))} - + {totalAmountPriceFormatted + .map((v) => ( + + {v}$ + )) - .orDefault( + .orDefaultLazy(() => ( - - )} + ))} diff --git a/packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts b/packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts index 91d6e7ee..0470f59d 100644 --- a/packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts +++ b/packages/widget/src/pages-dashboard/overview/positions/hooks/use-position-list-item.ts @@ -18,9 +18,8 @@ export const usePositionListItem = ( rewardRateAverage, inactiveValidator, baseToken, - totalAmount, + totalAmountUsd, totalAmountFormatted, - tokenToDisplay, } = useBasePositionListItem(item); const rewardsSummaryQuery = useRewardsSummary(item.integrationId); @@ -37,7 +36,6 @@ export const usePositionListItem = ( currency: config.currency, tokenList: [ ...baseToken.mapOrDefault((v) => [v], []), - ...tokenToDisplay.mapOrDefault((v) => [v], []), ...rewardsSummary.mapOrDefault((v) => [v.token], []), ], }); @@ -52,23 +50,10 @@ export const usePositionListItem = ( const totalAmountPriceFormatted = useMemo( () => - Maybe.fromRecord({ - totalAmount, - baseToken, - prices: Maybe.fromNullable(prices.data), - tokenToDisplay, - }) - .map((val) => - getTokenPriceInUSD({ - baseToken: val.baseToken, - amount: val.totalAmount, - pricePerShare: val.tokenToDisplay.pricePerShare, - token: val.tokenToDisplay, - prices: val.prices, - }) - ) - .map(defaultFormattedNumber), - [totalAmount, baseToken, prices, tokenToDisplay] + totalAmountUsd.gt(0) + ? Maybe.of(defaultFormattedNumber(totalAmountUsd)) + : (Maybe.empty() as Maybe), + [totalAmountUsd] ); const rewardsAmountPriceFormatted = useMemo( diff --git a/packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx index 0e666712..005cfef1 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx @@ -7,7 +7,6 @@ import { CollapsibleRoot, CollapsibleTrigger, } from "../../../components/atoms/collapsible"; -import { InfoIcon } from "../../../components/atoms/icons/info"; import { Spinner } from "../../../components/atoms/spinner"; import { Text } from "../../../components/atoms/typography/text"; import { PositionBalances } from "../../../pages/position-details/components/position-balances"; @@ -21,7 +20,6 @@ export const PositionDetailsInfo = () => { integrationData, positionBalancesByType, providersDetails, - positionLabel, liquidTokensToNativeConversion, } = usePositionDetails(); @@ -51,38 +49,6 @@ export const PositionDetailsInfo = () => { px="4" py="4" > - {positionLabel - .map((l) => ( - - - - - - - - { - t( - `position_details.labels.${l.type}.details`, - l.params - ) as string - } - - - - )) - .extractNullable()} - {providersDetails .map((pd) => diff --git a/packages/widget/src/pages/complete/pages/common.page.tsx b/packages/widget/src/pages/complete/pages/common.page.tsx index c2621c1f..8704dbad 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -19,6 +19,7 @@ import { isEthenaUsdeStaking, } from "../../../domain/types/yields"; import { AnimationPage } from "../../../navigation/containers/animation-page"; +import type { YieldTokenDto } from "../../../providers/yield-api-client-provider/types"; import { capitalizeFirstLowerRest } from "../../../utils/text"; import { PageContainer } from "../../components/page-container"; import { useComplete } from "../hooks/use-complete.hook"; @@ -28,7 +29,7 @@ import { } from "../state"; type Props = { - token: Maybe; + token: Maybe; metadata: Maybe; network: string; amount: string; diff --git a/packages/widget/src/pages/details/earn-page/state/use-pending-action-deep-link.ts b/packages/widget/src/pages/details/earn-page/state/use-pending-action-deep-link.ts index b32a301c..f5ff1c08 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-pending-action-deep-link.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-pending-action-deep-link.ts @@ -1,9 +1,6 @@ -import { - type AddressWithTokenDtoAdditionalAddresses, - type PendingActionDto, - type YieldBalanceDto, - type YieldDto, - yieldGetSingleYieldBalances, +import type { + AddressWithTokenDtoAdditionalAddresses, + YieldDto, } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; @@ -12,6 +9,7 @@ import { PAMultiValidatorsRequired, PASingleValidatorRequired, } from "../../../../domain"; +import { getPositionBalanceDataKey } from "../../../../domain/types/positions"; import type { ValidatorsConfig } from "../../../../domain/types/yields"; import { getYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity/get-yield-opportunity"; import { getInitParams } from "../../../../hooks/use-init-params"; @@ -19,6 +17,11 @@ import { useValidatorsConfig } from "../../../../hooks/use-validators-config"; import { useSKQueryClient } from "../../../../providers/query-client"; import { useSettings } from "../../../../providers/settings"; import { useSKWallet } from "../../../../providers/sk-wallet"; +import { useYieldApiFetchClient } from "../../../../providers/yield-api-client-provider"; +import type { + YieldBalanceDto, + YieldPendingActionDto, +} from "../../../../providers/yield-api-client-provider/types"; import type { GetEitherRight, Override } from "../../../../types/utils"; import { preparePendingActionRequestDto } from "../../../position-details/hooks/utils"; @@ -27,6 +30,7 @@ export const usePendingActionDeepLink = () => { useSKWallet(); const queryClient = useSKQueryClient(); + const yieldApiFetchClient = useYieldApiFetchClient(); const { externalProviders } = useSettings(); @@ -49,6 +53,7 @@ export const usePendingActionDeepLink = () => { additionalAddresses, address: addr, queryClient, + yieldApiFetchClient, externalProviders, validatorsConfig, }) @@ -62,6 +67,7 @@ const fn = ({ additionalAddresses, address, queryClient, + yieldApiFetchClient, externalProviders, validatorsConfig, }: { @@ -69,6 +75,7 @@ const fn = ({ address: string; additionalAddresses: AddressWithTokenDtoAdditionalAddresses | null; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; externalProviders: ReturnType["externalProviders"]; validatorsConfig: ValidatorsConfig; }) => @@ -94,19 +101,29 @@ const fn = ({ return EitherAsync.liftEither(initQueryParams) .chain((initQueryParams) => EitherAsync(() => - yieldGetSingleYieldBalances(initQueryParams.yieldId, { - addresses: { + yieldApiFetchClient.POST("/v1/yields/{yieldId}/balances", { + params: { + path: { + yieldId: initQueryParams.yieldId, + }, + }, + body: { address, - additionalAddresses: additionalAddresses ?? undefined, }, }) ) - .mapLeft(() => new Error("could not get yield balances")) + .chain((response) => + EitherAsync.liftEither( + Maybe.fromNullable(response.data).toEither( + new Error("could not get yield balances") + ) + ) + ) .map((val) => ({ yieldId: initQueryParams.yieldId, pendingaction: initQueryParams.pendingaction, validatorAddress: initQueryParams.validator, - singleYieldBalances: val, + singleYieldBalances: val.balances, address: address, additionalAddresses: additionalAddresses ?? undefined, })) @@ -117,7 +134,10 @@ const fn = ({ for (const balance of balances) { if ( data.validatorAddress && - balance.validatorAddress !== data.validatorAddress + balance.validator?.address !== data.validatorAddress && + !balance.validators?.some( + (validator) => validator.address === data.validatorAddress + ) ) { continue; } @@ -130,7 +150,7 @@ const fn = ({ return Right({ pendingAction, balance, - balanceId: balance.groupId ?? "default", + balanceId: getPositionBalanceDataKey(balance), }); } } @@ -151,7 +171,7 @@ const fn = ({ | { type: "positionDetails"; yieldOp: YieldDto; - pendingAction: PendingActionDto; + pendingAction: YieldPendingActionDto; balance: YieldBalanceDto; balanceId: string; } diff --git a/packages/widget/src/pages/details/positions-page/components/positions-list-item.tsx b/packages/widget/src/pages/details/positions-page/components/positions-list-item.tsx index 44f23441..85a0f079 100644 --- a/packages/widget/src/pages/details/positions-page/components/positions-list-item.tsx +++ b/packages/widget/src/pages/details/positions-page/components/positions-list-item.tsx @@ -160,7 +160,6 @@ export const PositionsListItem = memo( {Maybe.fromRecord({ token: item.token, rewardRateAverage, - totalAmountFormatted, }) .map((val) => ( - {val.totalAmountFormatted} {val.token.symbol} + {totalAmountFormatted} {val.token.symbol} )) diff --git a/packages/widget/src/pages/details/positions-page/hooks/use-position-list-item.ts b/packages/widget/src/pages/details/positions-page/hooks/use-position-list-item.ts index 28f99e26..f4e95899 100644 --- a/packages/widget/src/pages/details/positions-page/hooks/use-position-list-item.ts +++ b/packages/widget/src/pages/details/positions-page/hooks/use-position-list-item.ts @@ -63,19 +63,13 @@ export const usePositionListItem = ( [integrationData] ); - const totalAmount = useMemo( - () => - tokenToDisplay.map((val) => - getPositionTotalAmount({ - token: val, - balances: item.balancesWithAmount, - }) - ), - [item.balancesWithAmount, tokenToDisplay] + const { amount: totalAmount, amountUsd: totalAmountUsd } = useMemo( + () => getPositionTotalAmount(item.balancesWithAmount), + [item.balancesWithAmount] ); const totalAmountFormatted = useMemo( - () => totalAmount.map(defaultFormattedNumber), + () => (totalAmount.gt(0) ? defaultFormattedNumber(totalAmount) : "-"), [totalAmount] ); @@ -85,6 +79,7 @@ export const usePositionListItem = ( rewardRateAverage, inactiveValidator, totalAmount, + totalAmountUsd, totalAmountFormatted, baseToken, tokenToDisplay, diff --git a/packages/widget/src/pages/details/positions-page/hooks/use-positions.ts b/packages/widget/src/pages/details/positions-page/hooks/use-positions.ts index bdba10cb..bc4e379f 100644 --- a/packages/widget/src/pages/details/positions-page/hooks/use-positions.ts +++ b/packages/widget/src/pages/details/positions-page/hooks/use-positions.ts @@ -1,10 +1,6 @@ -import type { - YieldBalanceDto, - YieldBalanceLabelDto, - YieldBalancesWithIntegrationIdDto, -} from "@stakekit/api-hooks"; +import type { YieldBalanceLabelDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; -import { compare, Just, List, type Maybe } from "purify-ts"; +import { compare, Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { createSelector } from "reselect"; import type { YieldFindValidatorsParams } from "../../../../common/private-api"; @@ -12,6 +8,10 @@ import { usePositionsData } from "../../../../hooks/use-positions-data"; import { useSettings } from "../../../../providers/settings"; import type { SettingsContextType } from "../../../../providers/settings/types"; import { useSKWallet } from "../../../../providers/sk-wallet"; +import type { + YieldBalanceDto, + YieldBalanceType, +} from "../../../../providers/yield-api-client-provider/types"; import { defaultFormattedNumber } from "../../../../utils"; export const usePositions = () => { @@ -72,26 +72,20 @@ const positionsTableDataSelector = createSelector( .ifJust((v) => acc.push({ ...value, - integrationId: val.integrationId, + integrationId: val.yieldId, balancesWithAmount: v, balanceId: id, allBalances: value.balances, - yieldLabelDto: List.find( - (b) => !!b.label, - value.balances - ).chainNullable((v) => v.label), + yieldLabelDto: Maybe.empty() as Maybe, token: List.head( List.sort( (a, b) => compare(priorityOrder[a.type], priorityOrder[b.type]), value.balances ) - ).map((v) => ({ - ...v.token, - pricePerShare: v.pricePerShare, - })), + ).map((v) => v.token), actionRequired: v.some( - (b) => b.type === "locked" || b.type === "unstaked" + (b) => b.type === "locked" || b.type === "claimable" ), pointsRewardTokenBalances: v .filter((v) => !!v.token.isPoints) @@ -99,17 +93,11 @@ const positionsTableDataSelector = createSelector( ...v, amount: defaultFormattedNumber(v.amount), })), - hasPendingClaimRewards: List.find( - (b) => b.type === "rewards", - v - ) - .chain((b) => - List.find( - (a) => a.type === "CLAIM_REWARDS", - b.pendingActions - ) + hasPendingClaimRewards: v.some((balance) => + balance.pendingActions.some( + (action) => action.type === "CLAIM_REWARDS" ) - .isJust(), + ), }) ); }); @@ -117,14 +105,14 @@ const positionsTableDataSelector = createSelector( return acc; }, [] as ({ - integrationId: YieldBalancesWithIntegrationIdDto["integrationId"]; + integrationId: string; balancesWithAmount: YieldBalanceDto[]; allBalances: YieldBalanceDto[]; - balanceId: YieldBalanceDto["groupId"]; + balanceId: string; actionRequired: boolean; pointsRewardTokenBalances: YieldBalanceDto[]; hasPendingClaimRewards: boolean; - token: Maybe; + token: Maybe; yieldLabelDto: Maybe; } & ( | { type: "validators"; validatorsAddresses: string[] } @@ -144,15 +132,13 @@ const positionsTableDataSelector = createSelector( .unsafeCoerce() ); -const priorityOrder: { [key in YieldBalanceDto["type"]]: number } = { - available: 1, - staked: 2, - unstaking: 3, - unstaked: 4, - preparing: 5, +const priorityOrder: Record = { + active: 1, + entering: 2, + exiting: 3, + withdrawable: 4, + claimable: 5, locked: 6, - unlocking: 7, - rewards: 8, }; export const getYieldFindValidatorsQueryKey = ( diff --git a/packages/widget/src/pages/position-details/components/amount-block.tsx b/packages/widget/src/pages/position-details/components/amount-block.tsx index 4244598b..d6f201ad 100644 --- a/packages/widget/src/pages/position-details/components/amount-block.tsx +++ b/packages/widget/src/pages/position-details/components/amount-block.tsx @@ -14,6 +14,7 @@ import { import { Text } from "../../../components/atoms/typography/text"; import * as AmountToggle from "../../../components/molecules/amount-toggle"; import { useYieldMetaInfo } from "../../../hooks/use-yield-meta-info"; +import type { YieldTokenDto } from "../../../providers/yield-api-client-provider/types"; import { defaultFormattedNumber, formatNumber } from "../../../utils"; import { priceTxt } from "../styles.css"; @@ -27,11 +28,11 @@ type AmountBlockProps = { onMaxClick: (() => void) | null; label: string; formattedAmount: string; - balance: { amount: BigNumber; token: TokenDto } | null; + balance: { amount: BigNumber; token: TokenDto | YieldTokenDto } | null; } & ( | { variant: "unstake"; - unstakeToken: TokenDto; + unstakeToken: TokenDto | YieldTokenDto; yieldDto: YieldDto; validators: { [Key in keyof Pick< @@ -221,7 +222,7 @@ const UnstakeInfo = ({ validators: { [Key in keyof Pick]?: ValidatorDto[Key]; }[]; - unstakeToken: TokenDto; + unstakeToken: TokenDto | YieldTokenDto; }) => { const { withdrawnTime, withdrawnNotAvailable, positionLocked } = useYieldMetaInfo({ diff --git a/packages/widget/src/pages/position-details/components/position-balances.tsx b/packages/widget/src/pages/position-details/components/position-balances.tsx index 1322140a..deb8d991 100644 --- a/packages/widget/src/pages/position-details/components/position-balances.tsx +++ b/packages/widget/src/pages/position-details/components/position-balances.tsx @@ -1,4 +1,4 @@ -import type { YieldBalanceDto, YieldDto } from "@stakekit/api-hooks"; +import type { YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { isPast } from "date-fns"; import { useMemo } from "react"; @@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Text } from "../../../components/atoms/typography/text"; +import type { YieldBalanceDto } from "../../../providers/yield-api-client-provider/types"; import { defaultFormattedNumber } from "../../../utils"; import { formatDurationUntilDate } from "../../../utils/date"; @@ -21,9 +22,7 @@ export const PositionBalances = ({ const durationUntilDate = useMemo(() => { if ( !yieldBalance.date || - (yieldBalance.type !== "unstaking" && - yieldBalance.type !== "unlocking" && - yieldBalance.type !== "preparing") + (yieldBalance.type !== "entering" && yieldBalance.type !== "exiting") ) { return null; } diff --git a/packages/widget/src/pages/position-details/components/static-action-block.tsx b/packages/widget/src/pages/position-details/components/static-action-block.tsx index 4dd6f267..312477c8 100644 --- a/packages/widget/src/pages/position-details/components/static-action-block.tsx +++ b/packages/widget/src/pages/position-details/components/static-action-block.tsx @@ -1,20 +1,19 @@ -import type { - ActionTypes, - PendingActionDto, - YieldBalanceDto, - YieldDto, -} from "@stakekit/api-hooks"; +import type { ActionTypes, YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Trans, useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; import { Button } from "../../../components/atoms/button"; import { Text } from "../../../components/atoms/typography/text"; import { isEthenaUsdeStaking } from "../../../domain/types/yields"; +import type { + YieldBalanceDto, + YieldPendingActionDto, +} from "../../../providers/yield-api-client-provider/types"; import { formatNumber } from "../../../utils"; import type { usePositionDetails } from "../hooks/use-position-details"; type StaticActionBlockProps = { - pendingActionDto: PendingActionDto; + pendingActionDto: YieldPendingActionDto; yieldBalance: YieldBalanceDto & { tokenPriceInUsd: BigNumber; }; diff --git a/packages/widget/src/pages/position-details/hooks/use-pending-actions.ts b/packages/widget/src/pages/position-details/hooks/use-pending-actions.ts index 820a3802..4c0cee9e 100644 --- a/packages/widget/src/pages/position-details/hooks/use-pending-actions.ts +++ b/packages/widget/src/pages/position-details/hooks/use-pending-actions.ts @@ -1,9 +1,4 @@ -import type { - PendingActionDto, - ValidatorDto, - YieldBalanceDto, - YieldDto, -} from "@stakekit/api-hooks"; +import type { ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Left, List, Maybe, Right } from "purify-ts"; import { useEffect, useMemo, useRef } from "react"; @@ -13,12 +8,17 @@ import { PAMultiValidatorsRequired, PASingleValidatorRequired, } from "../../../domain"; +import { isPendingActionAmountRequired } from "../../../domain/types/pending-action"; import { usePendingActionSelectValidatorMatch } from "../../../hooks/navigation/use-pending-action-select-validator-match"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useBaseToken } from "../../../hooks/use-base-token"; import { useSavedRef } from "../../../hooks/use-saved-ref"; import { usePendingActionStore } from "../../../providers/pending-action-store"; import { useSKWallet } from "../../../providers/sk-wallet"; +import type { + YieldBalanceDto, + YieldPendingActionDto, +} from "../../../providers/yield-api-client-provider/types"; import { defaultFormattedNumber } from "../../../utils"; import { useUnstakeOrPendingActionDispatch, @@ -54,8 +54,8 @@ export const usePendingActions = () => { val.flatMap((balance) => balance.pendingActions.map((pa) => { const amount = Maybe.fromPredicate( - (v) => !!v, - pa.args?.args?.amount?.required + (v) => v, + isPendingActionAmountRequired(pa) ).chain(() => Maybe.fromNullable( pendingActionsState.get( @@ -79,7 +79,7 @@ export const usePendingActions = () => { amount: val.amount, token: val.reducedStakedOrLiquidBalance.token, prices: val.prices, - pricePerShare: balance.pricePerShare, + pricePerShare: null, baseToken: val.baseToken, }) ) @@ -150,7 +150,7 @@ export const usePendingActions = () => { yieldBalance, pendingActionDto, }: { - pendingActionDto: PendingActionDto; + pendingActionDto: YieldPendingActionDto; yieldBalance: YieldBalanceDto; }) => { trackEvent("pendingActionClicked", { @@ -235,7 +235,7 @@ export const usePendingActions = () => { selectedValidators, }: { integrationData: YieldDto; - pendingActionDto: PendingActionDto; + pendingActionDto: YieldPendingActionDto; yieldBalance: YieldBalanceDto; selectedValidators: ValidatorDto["address"][]; }) => { diff --git a/packages/widget/src/pages/position-details/hooks/use-position-details.ts b/packages/widget/src/pages/position-details/hooks/use-position-details.ts index 57c40ca6..4e1a5a9b 100644 --- a/packages/widget/src/pages/position-details/hooks/use-position-details.ts +++ b/packages/widget/src/pages/position-details/hooks/use-position-details.ts @@ -4,7 +4,7 @@ import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useNavigate } from "react-router"; -import { equalTokens, getTokenPriceInUSD } from "../../../domain"; +import { equalTokens } from "../../../domain"; import { isForceMaxAmount } from "../../../domain/types/stake"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useBaseToken } from "../../../hooks/use-base-token"; @@ -90,14 +90,6 @@ export const usePositionDetails = () => { const _unstakeAmountError = onClickHandler.isError || unstakeAmountError; - const positionLabel = useMemo( - () => - positionBalances.data.chainNullable( - (b) => b.balances.find((b) => b.label)?.label - ), - [positionBalances.data] - ); - const dispatch = useUnstakeOrPendingActionDispatch(); const trackEvent = useTrackEvent(); @@ -119,27 +111,10 @@ export const usePositionDetails = () => { const unstakeFormattedAmount = useMemo( () => - Maybe.fromRecord({ - prices: Maybe.fromNullable(positionBalancePrices.data), - reducedStakedOrLiquidBalance, - baseToken, - }) - .map((val) => - getTokenPriceInUSD({ - amount: unstakeAmount, - token: val.reducedStakedOrLiquidBalance.token, - prices: val.prices, - pricePerShare: val.reducedStakedOrLiquidBalance.pricePerShare, - baseToken: val.baseToken, - }) - ) + reducedStakedOrLiquidBalance + .map((val) => val.amountUsd) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""), - [ - positionBalancePrices.data, - reducedStakedOrLiquidBalance, - unstakeAmount, - baseToken, - ] + [reducedStakedOrLiquidBalance] ); const onMaxClick = () => { @@ -175,14 +150,14 @@ export const usePositionDetails = () => { .filter( (yb) => !yb.token.isPoints && - yb.pricePerShare && + !!yb.validator?.pricePerShare && !equalTokens(yb.token, v.baseToken) ) .forEach((yb) => { acc.set( yb.token.symbol, `1 ${yb.token.symbol} = ${defaultFormattedNumber( - new BigNumber(yb.pricePerShare) + new BigNumber(yb.validator?.pricePerShare ?? 0) )} ${v.baseToken.symbol}` ); }); @@ -221,7 +196,6 @@ export const usePositionDetails = () => { onValidatorsSubmit, onPendingActionAmountChange, unstakeToken, - positionLabel, unstakeAmountError: _unstakeAmountError, unstakeMaxAmount, unstakeMinAmount, diff --git a/packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts b/packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts index 83b93997..324fe22b 100644 --- a/packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts +++ b/packages/widget/src/pages/position-details/hooks/use-stake-exit-request-dto.ts @@ -27,26 +27,29 @@ export const useStakeExitRequestDto = () => { >(() => { if (val.integrationData.metadata.isIntegrationAggregator) { return List.find( - (b) => !!b.providerId, + (b) => !!b.validator?.providerId, val.stakedOrLiquidBalances ).map((b) => ({ - providerId: b.providerId, - validatorAddress: b.validatorAddress, + providerId: b.validator?.providerId, + validatorAddress: b.validator?.address, })); } if ( val.integrationData.args.exit?.args?.validatorAddresses?.required ) { return List.find( - (b) => !!b.validatorAddresses, + (b) => !!b.validators?.length, val.stakedOrLiquidBalances - ).map((b) => ({ validatorAddresses: b.validatorAddresses })); + ).map((b) => ({ + validatorAddresses: + b.validators?.map((validator) => validator.address) ?? [], + })); } if ( val.integrationData.args.exit?.args?.validatorAddress?.required ) { return List.find( - (b) => !!b.validatorAddress, + (b) => !!b.validator?.address, val.stakedOrLiquidBalances ).map((b) => { const subnetId = Maybe.fromNullable( @@ -54,14 +57,14 @@ export const useStakeExitRequestDto = () => { ) .chainNullable(() => val.integrationData.validators.find( - (v) => v.address === b.validatorAddress + (v) => v.address === b.validator?.address ) ) .map((validator) => validator.subnetId) .extract(); return { - validatorAddress: b.validatorAddress, + validatorAddress: b.validator?.address, subnetId, }; }); diff --git a/packages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.ts b/packages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.ts index e24391c2..f75e4a2c 100644 --- a/packages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.ts +++ b/packages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.ts @@ -1,10 +1,11 @@ -import type { - PendingActionDto, - ValidatorDto, - YieldBalanceDto, -} from "@stakekit/api-hooks"; +import type { ValidatorDto } from "@stakekit/api-hooks"; import { useCallback, useMemo, useReducer } from "react"; import type { SelectModalProps } from "../../../components/atoms/select-modal"; +import { isPendingActionValidatorAddressesRequired } from "../../../domain/types/pending-action"; +import type { + YieldBalanceDto, + YieldPendingActionDto, +} from "../../../providers/yield-api-client-provider/types"; import type { Action } from "../../../types/utils"; type State = { @@ -14,7 +15,7 @@ type State = { | { showValidatorsModal: true; yieldBalance: YieldBalanceDto; - pendingActionDto: PendingActionDto; + pendingActionDto: YieldPendingActionDto; } | { showValidatorsModal: false; @@ -25,7 +26,7 @@ type State = { type ValidatorOpenAction = Action< "validator/open", - { yieldBalance: YieldBalanceDto; pendingActionDto: PendingActionDto } + { yieldBalance: YieldBalanceDto; pendingActionDto: YieldPendingActionDto } >; type ValidatorCloseAction = Action<"validator/close">; type ValidatorMultiSelectAction = Action< @@ -82,15 +83,18 @@ const reducer = (state: State, action: Actions): State => { } case "validator/open": { - const newSelectedValidators: State["selectedValidators"] = new Set( - action.data.yieldBalance.validatorAddresses - ); + const newSelectedValidators: State["selectedValidators"] = new Set([ + ...(action.data.yieldBalance.validators?.map((v) => v.address) ?? []), + ...(action.data.yieldBalance.validator?.address + ? [action.data.yieldBalance.validator.address] + : []), + ]); return { ...state, - multiSelect: - !!action.data.pendingActionDto.args?.args?.validatorAddresses - ?.required, + multiSelect: isPendingActionValidatorAddressesRequired( + action.data.pendingActionDto + ), selectedValidators: newSelectedValidators, pendingActionDto: action.data.pendingActionDto, yieldBalance: action.data.yieldBalance, @@ -122,7 +126,7 @@ export const useValidatorAddressesHandling = () => { const openModal = useCallback( (args: { yieldBalance: YieldBalanceDto; - pendingActionDto: PendingActionDto; + pendingActionDto: YieldPendingActionDto; }) => dispatch({ type: "validator/open", data: args }), [] ); diff --git a/packages/widget/src/pages/position-details/hooks/utils.ts b/packages/widget/src/pages/position-details/hooks/utils.ts index a8261931..80fa3e12 100644 --- a/packages/widget/src/pages/position-details/hooks/utils.ts +++ b/packages/widget/src/pages/position-details/hooks/utils.ts @@ -1,16 +1,32 @@ import type { - PendingActionDto, + PendingActionDto as LegacyPendingActionDto, + YieldBalanceDto as LegacyYieldBalanceDto, PendingActionRequestDto, ValidatorDto, - YieldBalanceDto, YieldDto, } from "@stakekit/api-hooks"; import type { Either } from "purify-ts"; import { List, Maybe } from "purify-ts"; +import { + type AnyPendingActionDto, + isPendingActionAmountRequired, + isPendingActionValidatorAddressesRequired, + isPendingActionValidatorAddressRequired, +} from "../../../domain/types/pending-action"; import type { SKWallet } from "../../../domain/types/wallet"; +import type { + YieldBalanceDto, + YieldTokenDto, +} from "../../../providers/yield-api-client-provider/types"; import type { State } from "../state/types"; import { getBalanceTokenActionType } from "../state/utils"; +type AnyYieldBalanceDto = { + amount: string; + token: YieldTokenDto; + type: LegacyYieldBalanceDto["type"] | YieldBalanceDto["type"]; +}; + export const preparePendingActionRequestDto = ({ pendingActionsState, additionalAddresses, @@ -23,8 +39,8 @@ export const preparePendingActionRequestDto = ({ pendingActionsState: State["pendingActions"]; address: SKWallet["address"]; additionalAddresses: SKWallet["additionalAddresses"]; - pendingActionDto: PendingActionDto; - yieldBalance: YieldBalanceDto; + pendingActionDto: AnyPendingActionDto; + yieldBalance: AnyYieldBalanceDto; integration: YieldDto; selectedValidators: ValidatorDto["address"][]; }): Either< @@ -45,14 +61,15 @@ export const preparePendingActionRequestDto = ({ const args: PendingActionRequestDto["args"] = { amount: Maybe.fromPredicate( Boolean, - pendingActionDto.args?.args?.amount?.required + isPendingActionAmountRequired(pendingActionDto) ) .chainNullable(() => pendingActionsState.get( getBalanceTokenActionType({ - balanceType: yieldBalance.type, + balanceType: yieldBalance.type as YieldBalanceDto["type"], token: yieldBalance.token, - actionType: pendingActionDto.type, + actionType: + pendingActionDto.type as LegacyPendingActionDto["type"], }) ) ) @@ -62,9 +79,9 @@ export const preparePendingActionRequestDto = ({ }; if (selectedValidators.length) { - if (pendingActionDto.args?.args?.validatorAddresses?.required) { + if (isPendingActionValidatorAddressesRequired(pendingActionDto)) { args.validatorAddresses = selectedValidators; - } else if (pendingActionDto.args?.args?.validatorAddress?.required) { + } else if (isPendingActionValidatorAddressRequired(pendingActionDto)) { args.validatorAddress = List.head(selectedValidators).orDefault(""); } } @@ -74,7 +91,7 @@ export const preparePendingActionRequestDto = ({ args, integrationId: integration.id, passthrough: pendingActionDto.passthrough, - type: pendingActionDto.type, + type: pendingActionDto.type as LegacyPendingActionDto["type"], }, address: val, additionalAddresses: additionalAddresses ?? undefined, diff --git a/packages/widget/src/pages/position-details/position-details.page.tsx b/packages/widget/src/pages/position-details/position-details.page.tsx index c45311b8..5565896e 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -3,7 +3,6 @@ import { Just, Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; import { Box } from "../../components/atoms/box"; import { Button } from "../../components/atoms/button"; -import { InfoIcon } from "../../components/atoms/icons/info"; import { Spinner } from "../../components/atoms/spinner"; import { TokenIcon } from "../../components/atoms/token-icon"; import { Heading } from "../../components/atoms/typography/heading"; @@ -43,7 +42,6 @@ const PositionDetails = () => { unstakeToken, canUnstake, unstakeAmountError, - positionLabel, unstakeMaxAmount, unstakeMinAmount, unstakeIsGreaterOrLessIntegrationLimitError, @@ -108,42 +106,6 @@ const PositionDetails = () => { )) .extractNullable()} - {positionLabel - .map((l) => ( - - - - - - - - { - t( - `position_details.labels.${l.type}.details`, - l.params - ) as string - } - - - - )) - .extractNullable()} - {providersDetails .map((pd) => diff --git a/packages/widget/src/pages/position-details/state/index.tsx b/packages/widget/src/pages/position-details/state/index.tsx index 45e59edc..c8d9621b 100644 --- a/packages/widget/src/pages/position-details/state/index.tsx +++ b/packages/widget/src/pages/position-details/state/index.tsx @@ -1,10 +1,4 @@ -import type { - ActionTypes, - PendingActionDto, - PriceRequestDto, - TokenDto, - YieldBalanceDto, -} from "@stakekit/api-hooks"; +import type { ActionTypes } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import type { Dispatch, PropsWithChildren } from "react"; @@ -17,7 +11,7 @@ import { useRef, } from "react"; import { config } from "../../../config"; -import { isForceMaxAmount } from "../../../domain/types/stake"; +import { getPendingActionAmountConfig } from "../../../domain/types/pending-action"; import { isERC4626 } from "../../../domain/types/yields"; import { usePrices } from "../../../hooks/api/use-prices"; import { useYieldOpportunity } from "../../../hooks/api/use-yield-opportunity"; @@ -27,6 +21,11 @@ import { useMaxMinYieldAmount } from "../../../hooks/use-max-min-yield-amount"; import { usePositionBalanceByType } from "../../../hooks/use-position-balance-by-type"; import { usePositionBalances } from "../../../hooks/use-position-balances"; import { useStakedOrLiquidBalance } from "../../../hooks/use-staked-or-liquid-balance"; +import type { + YieldBalanceDto, + YieldPendingActionDto, + YieldTokenDto, +} from "../../../providers/yield-api-client-provider/types"; import type { Actions, BalanceTokenActionType, @@ -89,7 +88,7 @@ export const UnstakeOrPendingActionProvider = ({ positionBalances: positionBalances.data, baseToken, }) - .map((val) => ({ + .map((val) => ({ currency: config.currency, tokenList: [ val.baseToken, @@ -105,9 +104,7 @@ export const UnstakeOrPendingActionProvider = ({ * @summary Position balance by type */ const positionBalancesByType = usePositionBalanceByType({ - baseToken, positionBalancesData: positionBalances.data, - prices: positionBalancePrices, }); const stakedOrLiquidBalances = useStakedOrLiquidBalance( @@ -120,15 +117,17 @@ export const UnstakeOrPendingActionProvider = ({ b.reduce( (acc, next) => { acc.amount = acc.amount.plus(new BigNumber(next.amount)); + acc.amountUsd = acc.amountUsd.plus( + new BigNumber(next.amountUsd ?? 0) + ); acc.token = next.token; - acc.pricePerShare = next.pricePerShare; return acc; }, { + amountUsd: new BigNumber(0), amount: new BigNumber(0), token: b[0].token, - pricePerShare: b[0].pricePerShare, } ) ), @@ -139,10 +138,7 @@ export const UnstakeOrPendingActionProvider = ({ () => stakedOrLiquidBalances .chain((balances) => List.head(balances)) - .map((v) => ({ - ...v.token, - pricePerShare: v.pricePerShare, - })), + .map((v) => v.token), [stakedOrLiquidBalances] ); @@ -155,7 +151,7 @@ export const UnstakeOrPendingActionProvider = ({ yieldOpportunity: integrationData, type: "exit", availableAmount: reducedStakedOrLiquidBalance.map((v) => v.amount), - pricePerShare: unstakeToken.map((v) => v.pricePerShare).extractNullable(), + pricePerShare: null, }); const canChangeUnstakeAmount = integrationData.map( @@ -167,7 +163,7 @@ export const UnstakeOrPendingActionProvider = ({ () => new Map< BalanceTokenActionType, - { pendingAction: PendingActionDto; balance: YieldBalanceDto } + { pendingAction: YieldPendingActionDto; balance: YieldBalanceDto } >( positionBalancesByType .map((pbbt) => @@ -201,7 +197,7 @@ export const UnstakeOrPendingActionProvider = ({ }: { state: State["pendingActions"]; balanceType: YieldBalanceDto["type"]; - token: TokenDto; + token: YieldTokenDto; actionType: ActionTypes; amount: BigNumber; }) => { @@ -213,20 +209,13 @@ export const UnstakeOrPendingActionProvider = ({ const newMap = new Map(state); newMap.set(key, amount); + const amountConfig = getPendingActionAmountConfig(val.pendingAction); const max = new BigNumber( - val.pendingAction.args?.args?.amount?.maximum ?? - Number.POSITIVE_INFINITY - ); - const min = new BigNumber( - val.pendingAction.args?.args?.amount?.minimum ?? 0 + amountConfig?.maximum ?? Number.POSITIVE_INFINITY ); + const min = new BigNumber(amountConfig?.minimum ?? 0); - if ( - Maybe.fromNullable(val.pendingAction.args?.args?.amount).mapOrDefault( - isForceMaxAmount, - false - ) - ) { + if (amountConfig?.forceMax) { newMap.set(key, new BigNumber(val.balance.amount)); } else if (amount.isLessThan(min)) { newMap.set(key, min); diff --git a/packages/widget/src/pages/position-details/state/types.ts b/packages/widget/src/pages/position-details/state/types.ts index 60c4957c..9ebb9560 100644 --- a/packages/widget/src/pages/position-details/state/types.ts +++ b/packages/widget/src/pages/position-details/state/types.ts @@ -1,9 +1,4 @@ -import type { - ActionTypes, - TokenDto, - YieldBalanceDto, - YieldDto, -} from "@stakekit/api-hooks"; +import type { ActionTypes, TokenDto, YieldDto } from "@stakekit/api-hooks"; import type BigNumber from "bignumber.js"; import type { Maybe } from "purify-ts"; import type { PositionBalancesByType } from "../../../domain/types/positions"; @@ -13,19 +8,23 @@ import type { usePrices } from "../../../hooks/api/use-prices"; import type { useYieldOpportunity } from "../../../hooks/api/use-yield-opportunity"; import type { usePositionBalances } from "../../../hooks/use-position-balances"; import type { useStakedOrLiquidBalance } from "../../../hooks/use-staked-or-liquid-balance"; +import type { + YieldBalanceType, + YieldTokenDto, +} from "../../../providers/yield-api-client-provider/types"; import type { Action } from "../../../types/utils"; type UnstakeAmountChange = Action<"unstake/amount/change", BigNumber>; type UnstakeAmountMax = Action<"unstake/amount/max">; export type BalanceTokenActionType = - `${YieldBalanceDto["type"]}-${TokenString}-${ActionTypes}`; + `${YieldBalanceType}-${TokenString}-${ActionTypes}`; export type PendingActionAmountChange = Action< "pendingAction/amount/change", { - balanceType: YieldBalanceDto["type"]; - token: TokenDto; + balanceType: YieldBalanceType; + token: TokenDto | YieldTokenDto; actionType: ActionTypes; amount: BigNumber; } @@ -50,12 +49,12 @@ export type ExtraData = { stakedOrLiquidBalances: ReturnType; reducedStakedOrLiquidBalance: Maybe<{ amount: BigNumber; - token: TokenDto; - pricePerShare: string; + amountUsd: BigNumber; + token: TokenDto | YieldTokenDto; }>; positionBalancePrices: ReturnType>; unstakeAmountValid: boolean; - unstakeToken: Maybe; + unstakeToken: Maybe; unstakeAmountError: boolean; canChangeUnstakeAmount: Maybe; unstakeIsGreaterOrLessIntegrationLimitError: boolean; diff --git a/packages/widget/src/pages/position-details/state/utils.ts b/packages/widget/src/pages/position-details/state/utils.ts index d07f150b..d5fdda4c 100644 --- a/packages/widget/src/pages/position-details/state/utils.ts +++ b/packages/widget/src/pages/position-details/state/utils.ts @@ -1,9 +1,9 @@ -import type { - ActionTypes, - TokenDto, - YieldBalanceDto, -} from "@stakekit/api-hooks"; +import type { ActionTypes, TokenDto } from "@stakekit/api-hooks"; import { tokenString } from "../../../domain"; +import type { + YieldBalanceType, + YieldTokenDto, +} from "../../../providers/yield-api-client-provider/types"; import type { BalanceTokenActionType } from "./types"; export const getBalanceTokenActionType = ({ @@ -11,8 +11,8 @@ export const getBalanceTokenActionType = ({ balanceType, token, }: { - balanceType: YieldBalanceDto["type"]; - token: TokenDto; + balanceType: YieldBalanceType; + token: TokenDto | YieldTokenDto; actionType: ActionTypes; }): BalanceTokenActionType => `${balanceType}-${tokenString(token)}-${actionType}`; diff --git a/packages/widget/src/pages/review/hooks/use-fees.ts b/packages/widget/src/pages/review/hooks/use-fees.ts index 73d1a94b..b2d70b89 100644 --- a/packages/widget/src/pages/review/hooks/use-fees.ts +++ b/packages/widget/src/pages/review/hooks/use-fees.ts @@ -4,6 +4,7 @@ import { Just, type Maybe } from "purify-ts"; import { useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; import type { Prices } from "../../../domain/types/price"; +import type { YieldTokenDto } from "../../../providers/yield-api-client-provider/types"; import { bpsToAmount, bpsToPercentage } from "../../../utils"; import { getFeesInUSD } from "../../../utils/formatters"; import type { FeesBps } from "../types"; @@ -15,7 +16,7 @@ export const useFees = ({ token, }: { prices: Maybe; - token: Maybe; + token: Maybe; amount: BigNumber; feeConfigDto: Maybe; }): { diff --git a/packages/widget/src/pages/review/pages/common-page/common.page.tsx b/packages/widget/src/pages/review/pages/common-page/common.page.tsx index 0e62d7c8..20569484 100644 --- a/packages/widget/src/pages/review/pages/common-page/common.page.tsx +++ b/packages/widget/src/pages/review/pages/common-page/common.page.tsx @@ -12,6 +12,7 @@ import { WarningBox } from "../../../../components/atoms/warning-box"; import type { RewardTokenDetails } from "../../../../components/molecules/reward-token-details"; import { useTrackEvent } from "../../../../hooks/tracking/use-track-event"; import { AnimationPage } from "../../../../navigation/containers/animation-page"; +import type { YieldTokenDto } from "../../../../providers/yield-api-client-provider/types"; import { MetaInfo } from "../../../components/meta-info"; import { PageContainer } from "../../../components/page-container"; import type { FeesBps } from "../../types"; @@ -25,7 +26,7 @@ export type MetaInfoProps = type ReviewPageProps = { fee: string; title: string; - token: Maybe; + token: Maybe; metadata: Maybe; info: ReactNode; rewardTokenDetailsProps: Maybe>; diff --git a/packages/widget/src/pages/review/pages/common-page/components/review-top-section.tsx b/packages/widget/src/pages/review/pages/common-page/components/review-top-section.tsx index fdfad48b..662d5406 100644 --- a/packages/widget/src/pages/review/pages/common-page/components/review-top-section.tsx +++ b/packages/widget/src/pages/review/pages/common-page/components/review-top-section.tsx @@ -8,11 +8,12 @@ import { TokenIcon } from "../../../../../components/atoms/token-icon"; import { Heading } from "../../../../../components/atoms/typography/heading"; import { Text } from "../../../../../components/atoms/typography/text"; import type { RewardTokenDetails } from "../../../../../components/molecules/reward-token-details"; +import type { YieldTokenDto } from "../../../../../providers/yield-api-client-provider/types"; import { headingStyles } from "../../style.css"; type Props = { title: string; - token: Maybe; + token: Maybe; metadata: Maybe; info: ReactNode; rewardTokenDetailsProps?: Maybe>; diff --git a/packages/widget/src/providers/exit-stake-store/index.tsx b/packages/widget/src/providers/exit-stake-store/index.tsx index 5542f1e9..2d8e13fb 100644 --- a/packages/widget/src/providers/exit-stake-store/index.tsx +++ b/packages/widget/src/providers/exit-stake-store/index.tsx @@ -8,13 +8,14 @@ import { createStore } from "@xstate/store"; import type BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; +import type { YieldTokenDto } from "../yield-api-client-provider/types"; type InitData = { requestDto: ActionRequestDto; gasFeeToken: YieldDto["token"]; unstakeAmount: BigNumber; integrationData: YieldDto; - unstakeToken: TokenDto; + unstakeToken: TokenDto | YieldTokenDto; }; type Store = Maybe }>; diff --git a/packages/widget/src/providers/index.tsx b/packages/widget/src/providers/index.tsx index 10ab2017..796e8f67 100644 --- a/packages/widget/src/providers/index.tsx +++ b/packages/widget/src/providers/index.tsx @@ -29,6 +29,7 @@ import { ActionHistoryContextProvider } from "./stake-history"; import { ThemeWrapper } from "./theme-wrapper"; import { TrackingContextProviderWithProps } from "./tracking"; import { WagmiConfigProvider } from "./wagmi/provider"; +import { YieldApiClientProvider } from "./yield-api-client-provider"; export const Providers = ({ children, @@ -38,53 +39,55 @@ export const Providers = ({ - - - - - - - - - - - - - - - - - - - - - - - - {children} - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + {children} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/widget/src/providers/pending-action-store/index.tsx b/packages/widget/src/providers/pending-action-store/index.tsx index 2350baff..290864f0 100644 --- a/packages/widget/src/providers/pending-action-store/index.tsx +++ b/packages/widget/src/providers/pending-action-store/index.tsx @@ -9,13 +9,14 @@ import type { import { createStore } from "@xstate/store"; import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; +import type { YieldTokenDto } from "../yield-api-client-provider/types"; type InitData = { requestDto: PendingActionRequestDto; addresses: AddressesDto; pendingActionType: ActionTypes; integrationData: YieldDto; - interactedToken: TokenDto; + interactedToken: TokenDto | YieldTokenDto; gasFeeToken: TokenDto; }; diff --git a/packages/widget/src/providers/settings/types.ts b/packages/widget/src/providers/settings/types.ts index ba141092..b22e09d3 100644 --- a/packages/widget/src/providers/settings/types.ts +++ b/packages/widget/src/providers/settings/types.ts @@ -29,6 +29,7 @@ export type VariantProps = export type SettingsProps = { apiKey: string; baseUrl?: string; + yieldsApiUrl?: string; theme?: ThemeWrapperTheme; tracking?: { trackEvent?: (event: TrackEventVal, properties?: Properties) => void; diff --git a/packages/widget/src/providers/sk-wallet/errors.ts b/packages/widget/src/providers/sk-wallet/errors.ts index 2418f6b6..f3b4756b 100644 --- a/packages/widget/src/providers/sk-wallet/errors.ts +++ b/packages/widget/src/providers/sk-wallet/errors.ts @@ -11,8 +11,8 @@ export class SafeFailedError extends Error { export class SendTransactionError extends Error { _tag = "SendTransactionError"; - constructor(message?: string) { - super(message); + constructor(cause?: unknown) { + super("Send transaction failed", { cause }); this._tag = "SendTransactionError"; } @@ -20,8 +20,8 @@ export class SendTransactionError extends Error { export class TransactionDecodeError extends Error { _tag = "TransactionDecodeError"; - constructor(message?: string) { - super(message); + constructor(message?: string, cause?: unknown) { + super(message, { cause }); this._tag = "TransactionDecodeError"; } diff --git a/packages/widget/src/providers/sk-wallet/index.tsx b/packages/widget/src/providers/sk-wallet/index.tsx index 945f0a1f..8edc2543 100644 --- a/packages/widget/src/providers/sk-wallet/index.tsx +++ b/packages/widget/src/providers/sk-wallet/index.tsx @@ -453,7 +453,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { type: val.maxFeePerGas ? "eip1559" : "legacy", }) ) - .mapLeft(() => new SendTransactionError()) + .mapLeft((e) => new SendTransactionError(e)) .map((val) => ({ signedTx: val, broadcasted: true })) ); }), diff --git a/packages/widget/src/providers/yield-api-client-provider/index.tsx b/packages/widget/src/providers/yield-api-client-provider/index.tsx new file mode 100644 index 00000000..42b22534 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/index.tsx @@ -0,0 +1,65 @@ +import type { Client } from "openapi-fetch"; +import createFetchClient from "openapi-fetch"; +import type { OpenapiQueryClient } from "openapi-react-query"; +import createClient from "openapi-react-query"; +import type { PropsWithChildren } from "react"; +import { createContext, useContext, useMemo } from "react"; +import { config } from "../../config"; +import type { paths } from "../../types/yield-api-schema"; +import { useSettings } from "../settings"; + +const QueryContext = createContext | undefined>( + undefined +); +const FetchContext = createContext | undefined>(undefined); + +export const YieldApiClientProvider = ({ children }: PropsWithChildren) => { + const { apiKey, yieldsApiUrl } = useSettings(); + const url = yieldsApiUrl ?? config.env.yieldsApiUrl; + + const clients = useMemo(() => { + const fetchClient = createFetchClient({ + baseUrl: url, + headers: { + "x-api-key": apiKey, + }, + }); + + return { + fetchClient, + queryClient: createClient(fetchClient), + }; + }, [apiKey, url]); + + return ( + + + {children} + + + ); +}; + +export const useYieldApiClient = () => { + const value = useContext(QueryContext); + + if (!value) { + throw new Error( + "useYieldApiClient must be used within a YieldApiClientProvider" + ); + } + + return value; +}; + +export const useYieldApiFetchClient = () => { + const value = useContext(FetchContext); + + if (!value) { + throw new Error( + "useYieldApiFetchClient must be used within a YieldApiClientProvider" + ); + } + + return value; +}; diff --git a/packages/widget/src/providers/yield-api-client-provider/types.ts b/packages/widget/src/providers/yield-api-client-provider/types.ts new file mode 100644 index 00000000..9f5a3ec3 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/types.ts @@ -0,0 +1,21 @@ +import type { components } from "../../types/yield-api-schema"; + +export type YieldBalancesRequestDto = + components["schemas"]["BalancesRequestDto"]; +export type YieldSingleBalancesRequestDto = + components["schemas"]["YieldBalancesRequestDto"]; +export type YieldBalanceType = components["schemas"]["BalanceType"]; + +export type YieldPendingActionDto = components["schemas"]["PendingActionDto"]; +export type YieldTokenDto = components["schemas"]["TokenDto"]; + +export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; + +export type YieldBalanceDto = components["schemas"]["BalanceDto"]; + +export type YieldBalancesByYieldDto = components["schemas"]["YieldBalancesDto"]; +export type YieldSingleBalancesResponseDto = + components["schemas"]["YieldBalancesDto"]; + +export type YieldBalancesResponseDto = + components["schemas"]["BalancesResponseDto"]; diff --git a/packages/widget/src/translation/English/translations.json b/packages/widget/src/translation/English/translations.json index f4f13e8d..2baf1772 100644 --- a/packages/widget/src/translation/English/translations.json +++ b/packages/widget/src/translation/English/translations.json @@ -463,6 +463,15 @@ "restaking": "Unstake" }, "balance_type": { + "active": "Active", + "active_yearn_or_deposit": "Deposited", + "entering": "Entering", + "entering_yearn_or_deposit": "Depositing", + "exiting": "Exiting", + "exiting_yearn_or_deposit": "Withdrawing", + "withdrawable": "Withdrawable", + "withdrawable_yearn_or_deposit": "Available to withdraw", + "claimable": "Claimable", "available": "Available", "staked": "Staked", "staked_yearn_or_deposit": "Deposited", diff --git a/packages/widget/src/translation/French/translations.json b/packages/widget/src/translation/French/translations.json index 0ca0efac..d0c42eb0 100644 --- a/packages/widget/src/translation/French/translations.json +++ b/packages/widget/src/translation/French/translations.json @@ -413,6 +413,15 @@ "restaking": "Déstaker" }, "balance_type": { + "active": "Actif", + "active_yearn_or_deposit": "Déposé", + "entering": "En cours d'entrée", + "entering_yearn_or_deposit": "En cours de dépôt", + "exiting": "En cours de sortie", + "exiting_yearn_or_deposit": "En cours de retrait", + "withdrawable": "Retirable", + "withdrawable_yearn_or_deposit": "Disponible au retrait", + "claimable": "Réclamable", "available": "Disponible", "staked": "Staké", "staked_yearn_or_deposit": "Déposé", diff --git a/packages/widget/src/types/yield-api-schema.d.ts b/packages/widget/src/types/yield-api-schema.d.ts new file mode 100644 index 00000000..bf972271 --- /dev/null +++ b/packages/widget/src/types/yield-api-schema.d.ts @@ -0,0 +1,5223 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/v1/yields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List all yield opportunities + * @description Retrieve a paginated list of available yield opportunities across all supported networks and protocols. + */ + get: operations["YieldsController_getYields"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/balances": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Get balances across multiple yields and networks + * @description Retrieve balances for multiple wallet addresses across different networks and yield opportunities. Send an array of balance requests - each request can specify a yieldId (optional for chain scanning), address, network, and custom arguments. This is the same format as the single yield balance endpoint but in array form. Duplicate requests (same yieldId + address + network) are automatically deduplicated, with specific yield requests taking precedence over chain scans. + */ + post: operations["YieldsController_getAggregateBalances"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/{yieldId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get yield metadata + * @description Retrieve detailed information about a specific yield opportunity including APY, tokens, protocol details, and more. + */ + get: operations["YieldsController_getYield"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/{yieldId}/risk": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get risk metadata for a yield + * @description Retrieve risk metadata associated with a specific yield. + */ + get: operations["YieldsController_getYieldRisk"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/{yieldId}/balances": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Get balances for a specific yield + * @description Retrieve all balances associated with a yield opportunity for a specific wallet address, including active, pending, claimable, and withdrawable balances. The network is automatically determined from the yield configuration. + */ + post: operations["YieldsController_getYieldBalances"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/{yieldId}/validators": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get yield validators + * @description Retrieve a paginated list of validators available for staking or delegation for this yield opportunity. + */ + get: operations["YieldsController_getYieldValidators"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/actions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get user actions + * @description Retrieve all actions performed by a user, with optional filtering by yield, status, category, etc. In the future, this may include personalized action recommendations. + */ + get: operations["ActionsController_getActions"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/actions/{actionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get action details + * @description Retrieve detailed information about a specific action including current status, transactions, and execution details. + */ + get: operations["ActionsController_getAction"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/actions/enter": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Enter a yield + * @description Generate the transactions needed to enter a yield position with the provided parameters. + */ + post: operations["ActionsController_enterYield"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/actions/exit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Exit a yield + * @description Generate the transactions needed to exit a yield position with the provided parameters. + */ + post: operations["ActionsController_exitYield"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/actions/manage": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Manage a yield + * @description Generate the transactions needed to perform management actions on a yield position. + */ + post: operations["ActionsController_manageYield"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/transactions/{transactionId}/submit-hash": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** + * Submit transaction hash + * @description Submit the transaction hash after broadcasting a transaction to the blockchain. This updates the transaction status and enables tracking. + */ + put: operations["TransactionsController_submitTransactionHash"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/transactions/{transactionId}/submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Submit transaction + * @description Submit the transaction to the blockchain. + */ + post: operations["TransactionsController_submitTransaction"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/transactions/{transactionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get transaction details + * @description Retrieve detailed information about a specific transaction including current status, hash, and execution details. + */ + get: operations["TransactionsController_getTransaction"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/networks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List all available networks + * @description Retrieve a list of all supported networks that can be used for filtering yields and other operations. + */ + get: operations["NetworksController_getNetworks"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/providers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get all providers + * @description Returns a paginated list of all providers, including both protocol and validator providers. + */ + get: operations["ProvidersController_getProviders"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/providers/{providerId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get provider by ID + * @description Returns detailed information about a specific provider. + */ + get: operations["ProvidersController_getProvider"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Health check + * @description Get the health status of the yield API with current timestamp + */ + get: operations["HealthController_health"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + TokenDto: { + /** + * @description Token symbol + * @example ETH + */ + symbol: string; + /** + * @description Token name + * @example Ethereum + */ + name: string; + /** + * @description Token decimal places + * @example 18 + */ + decimals: number; + /** + * @description Token network + * @example Ethereum + * @enum {string} + */ + network: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Token address (if applicable) + * @example 0x... + */ + address?: string; + /** + * @description Token logo URI + * @example https://... + */ + logoURI?: string; + /** + * @description Token is points + * @example true + */ + isPoints?: boolean; + /** + * @description Token CoinGecko ID + * @example ethereum + */ + coinGeckoId?: string; + }; + RewardDto: { + /** + * @description Reward rate as a decimal (e.g. 0.04 = 4%) + * @example 0.04 + */ + rate: number; + /** + * @description Whether this rate is APR or APY + * @example APR + */ + rateType: string; + /** @description Token received as reward */ + token: components["schemas"]["TokenDto"]; + /** + * @description Structured source of yield (e.g. staking, protocol incentive) + * @example protocol_incentive + * @enum {string} + */ + yieldSource: + | "staking" + | "restaking" + | "protocol_incentive" + | "points" + | "lending_interest" + | "mev" + | "real_world_asset_yield" + | "vault"; + /** + * @description Optional human-readable description of this reward + * @example LDO distributed to incentivize stETH adoption via Lido Boost + */ + description?: string; + }; + RewardRateDto: { + /** + * @description Estimated reward rate across all sources (e.g. staking, points) + * @example 6.5 + */ + total: number; + /** + * @description Whether this reward rate is APR or APY + * @example APR + */ + rateType: string; + /** @description Breakdown of reward rates by source */ + components: components["schemas"]["RewardDto"][]; + }; + YieldStatisticsDto: { + /** + * @description Total value locked in USD + * @example 1,200,000 + */ + tvlUsd?: string | null; + /** + * @description Total value locked in primary underlying token + * @example 500.25 + */ + tvl?: string | null; + /** + * @description Raw total value locked (full precision) + * @example 500250000000000000000 + */ + tvlRaw?: string | null; + /** + * @description Number of users with active positions in the yield + * @example 348 + */ + uniqueUsers?: number | null; + /** + * @description Average position size in USD + * @example 3,448.27 + */ + averagePositionSizeUsd?: string | null; + /** + * @description Average position size in primary underlying token + * @example 1.44 + */ + averagePositionSize?: string | null; + }; + YieldRiskExponentialDto: { + /** @description Exponential pool rating */ + poolRating?: Record; + /** @description Exponential pool score (1-5) */ + poolScore?: Record; + /** @description Exponential rating description */ + ratingDescription?: Record; + /** @description Exponential pool URL */ + url?: Record; + }; + YieldRiskCredoraDto: { + /** @description Credora rating */ + rating?: Record; + /** @description Credora score (1-5) */ + score?: Record; + /** @description Probability of Significant Loss (annualized) */ + psl?: Record; + /** @description Credora publish date */ + publishDate?: Record; + /** @description Credora curator name */ + curator?: Record; + }; + YieldRiskDto: { + /** @description Risk data last update timestamp */ + updatedAt: string; + exponentialFi?: components["schemas"]["YieldRiskExponentialDto"]; + credora?: components["schemas"]["YieldRiskCredoraDto"]; + }; + YieldStatusDto: { + /** + * @description Whether the user can currently enter this yield + * @example true + */ + enter: boolean; + /** + * @description Whether the user can currently exit this yield + * @example true + */ + exit: boolean; + }; + /** + * @description Supported standards for this yield + * @enum {string} + */ + ERCStandards: "ERC20" | "ERC4626" | "ERC721" | "ERC1155"; + YieldMetadataDto: { + /** + * @description Display name of the yield opportunity + * @example Lido Staking + */ + name: string; + /** + * @description Yield opportunity logo URI + * @example https://lido.fi/logo.png + */ + logoURI: string; + /** + * @description Markdown-supported short description of this yield opportunity, including where rewards come from. + * @example Stake ETH with Lido to earn auto-compounding validator rewards via stETH. + */ + description: string; + /** + * @description Link to documentation or integration guide + * @example https://docs.lido.fi + */ + documentation: string; + /** + * @description Whether this yield is currently under maintenance + * @example false + */ + underMaintenance: boolean; + /** + * @description Whether this yield is deprecated and will be discontinued + * @example false + */ + deprecated: boolean; + supportedStandards: components["schemas"]["ERCStandards"][]; + }; + /** + * @description Type of yield mechanism (staking, restaking, LP, vault, etc.) + * @enum {string} + */ + YieldType: + | "staking" + | "restaking" + | "lending" + | "vault" + | "fixed_yield" + | "real_world_asset" + | "concentrated_liquidity_pool" + | "liquidity_pool"; + /** + * @description How often rewards are distributed (e.g. continuously, epoch-based) + * @enum {string} + */ + RewardSchedule: + | "block" + | "hour" + | "day" + | "week" + | "month" + | "era" + | "epoch" + | "campaign"; + /** + * @description How rewards are claimed: auto, manual, or mixed + * @enum {string} + */ + RewardClaiming: "auto" | "manual"; + TimePeriodDto: { + /** + * @description Duration in seconds + * @example 86400 + */ + seconds: number; + }; + YieldFeeDto: { + /** + * @description Deposit fee percentage + * @example 0.00 + */ + deposit?: string; + /** + * @description Withdrawal fee percentage + * @example 0.00 + */ + withdrawal?: string; + /** + * @description Management fee percentage (annual) + * @example 2.00 + */ + management?: string; + /** + * @description Performance fee percentage + * @example 20.00 + */ + performance?: string; + }; + YieldEntryLimitsDto: { + /** + * @description Minimum amount required to enter this yield in token units (null if no minimum) + * @example 0.01 + */ + minimum: string | null; + /** + * @description Maximum amount allowed to enter this yield in token units (null if no limit) + * @example 1000.0 + */ + maximum: string | null; + }; + YieldRequirementsDto: { + /** @description Whether off-chain KYC is required before transacting */ + kycRequired: boolean; + /** @description Issuer's KYC portal URL */ + kycUrl?: string; + }; + ArgumentFieldDto: { + /** + * @description Field name + * @example amount + * @enum {string} + */ + name: + | "amount" + | "amounts" + | "validatorAddress" + | "validatorAddresses" + | "receiverAddress" + | "providerId" + | "duration" + | "inputToken" + | "inputTokenNetwork" + | "outputToken" + | "outputTokenNetwork" + | "subnetId" + | "tronResource" + | "feeConfigurationId" + | "cosmosPubKey" + | "tezosPubKey" + | "cAddressBech" + | "pAddressBech" + | "executionMode" + | "ledgerWalletApiCompatible" + | "useMaxAmount" + | "useInstantExecution" + | "rangeMin" + | "rangeMax" + | "percentage" + | "tokenId" + | "skipPrechecks"; + /** + * @description Field type + * @example string + * @enum {string} + */ + type: "string" | "number" | "address" | "enum" | "boolean"; + /** + * @description Field label + * @example Amount to Enter + */ + label: string; + /** @description Field description */ + description?: string; + /** + * @description Whether the field is required + * @example true + */ + required?: boolean; + /** + * @description Options for enum fields + * @example [ + * "individual", + * "batched" + * ] + */ + options?: string[]; + /** + * @description Reference to API endpoint that provides options dynamically + * @example /api/v1/validators?integrationId=eth-lido + */ + optionsRef?: string; + /** @description Default value for the field */ + default?: Record; + /** @description Placeholder text for the field */ + placeholder?: string; + /** + * @description Minimum allowed value for number fields (null if no minimum) + * @example 1.0 + */ + minimum?: string | null; + /** + * @description Maximum allowed value for number fields (null if no maximum) + * @example 100.0 + */ + maximum?: string | null; + /** + * @description Whether the field is an array + * @example false + */ + isArray?: boolean; + }; + ArgumentSchemaDto: { + /** @description List of argument fields */ + fields: components["schemas"]["ArgumentFieldDto"][]; + /** @description Notes or instructions for these arguments */ + notes?: string; + }; + YieldMechanicsArgumentsDto: { + enter?: components["schemas"]["ArgumentSchemaDto"]; + exit?: components["schemas"]["ArgumentSchemaDto"]; + /** @description Manage action schemas. Each yield supports different ActionTypes (CLAIM_UNSTAKED, CLAIM_REWARDS, etc.). Keys are ActionTypes enum values. */ + manage?: { + [key: string]: components["schemas"]["ArgumentSchemaDto"]; + }; + /** @description Arguments for the balances endpoint (e.g., alternative addresses, chain-specific fields) */ + balance?: components["schemas"]["ArgumentSchemaDto"]; + }; + PossibleFeeTakingMechanismsDto: { + /** + * @description User can take (earn) a deposit fee + * @example false + */ + depositFee: boolean; + /** + * @description User can take (earn) a management fee + * @example false + */ + managementFee: boolean; + /** + * @description User can take (earn) a performance fee + * @example false + */ + performanceFee: boolean; + /** + * @description User can take (earn) validator rebates + * @example false + */ + validatorRebates: boolean; + }; + YieldMechanicsDto: { + type: components["schemas"]["YieldType"]; + /** + * @description Indicates whether this yield requires validator selection + * @example true + */ + requiresValidatorSelection?: boolean; + rewardSchedule: components["schemas"]["RewardSchedule"]; + rewardClaiming: components["schemas"]["RewardClaiming"]; + /** @description Token used for gas fees (typically native) */ + gasFeeToken: components["schemas"]["TokenDto"]; + /** @description Lockup period - minimum time before exit can be initiated */ + lockupPeriod?: components["schemas"]["TimePeriodDto"]; + /** @description Cooldown period required before exit is allowed */ + cooldownPeriod?: components["schemas"]["TimePeriodDto"]; + /** @description Warmup period before rewards start accruing */ + warmupPeriod?: components["schemas"]["TimePeriodDto"]; + /** @description Fees charged to the user for this yield (e.g., deposit, management, performance). */ + fee?: components["schemas"]["YieldFeeDto"]; + /** @description Entry amount limits for this yield */ + entryLimits?: components["schemas"]["YieldEntryLimitsDto"]; + /** @description Access requirements (e.g. KYC) for this yield */ + requirements?: components["schemas"]["YieldRequirementsDto"]; + /** @description Supports Ledger Wallet API (connect via Ledger Live) */ + supportsLedgerWalletApi?: boolean; + /** @description Additional transaction formats supported (e.g. safe, batch) */ + extraTransactionFormatsSupported?: ("raw" | "default")[]; + /** @description Arguments required for each action (enter, exit, manage, etc.) */ + arguments?: components["schemas"]["YieldMechanicsArgumentsDto"]; + /** @description Possible fee-taking mechanisms for the user or integrator (i.e., what fees the user/integrator can potentially earn from this yield). */ + possibleFeeTakingMechanisms?: components["schemas"]["PossibleFeeTakingMechanismsDto"]; + }; + CuratorDto: { + /** @description Curator name */ + name?: Record | null; + /** @description Curator description */ + description?: Record | null; + /** @description Curator logo URI */ + logoURI?: Record | null; + }; + PricePerShareStateDto: { + /** + * @description Price per share for the yield (e.g., LP token price, vault share price) + * @example 1.05 + */ + price: number; + /** @description Share token (the token you own shares of) */ + shareToken: components["schemas"]["TokenDto"]; + /** @description Quote token (the token the price is denominated in) */ + quoteToken: components["schemas"]["TokenDto"]; + }; + ConcentratedLiquidityPoolStateDto: { + /** + * @description Full-range trading APR (24h or rolling) + * @example 0.12 + */ + baseApr: number; + /** + * @description Current mid-price from the AMM (token1 per token0) + * @example 3950.42 + */ + price: number; + /** + * @description Tick spacing required so UI can snap ranges + * @example 50 + */ + tickSpacing: number; + /** + * @description Minimum tick bound for the pool + * @example -887272 + */ + minTick: number; + /** + * @description Maximum tick bound for the pool + * @example 887272 + */ + maxTick: number; + /** + * @description 24-hour trading volume in USD + * @example 149550871.99 + */ + volume24hUsd: number | null; + /** + * @description 24-hour fees earned by LPs in USD + * @example 14955.09 + */ + fee24hUsd: number | null; + /** + * @description Total value locked in USD + * @example 9213550.2 + */ + tvlUsd: number | null; + /** + * @description Pool fee tier as a decimal (e.g., 0.0005 for 0.05%) + * @example 0.0005 + */ + feeTier: number; + /** @description Base token (token0) */ + baseToken: components["schemas"]["TokenDto"]; + /** @description Quote token (token1) */ + quoteToken: components["schemas"]["TokenDto"]; + }; + CapacityDto: { + /** @description Current total assets in the yield */ + current: string; + /** @description Maximum capacity of the yield */ + max?: string | null; + /** @description Remaining capacity available for deposits */ + remaining?: string | null; + }; + LiquidityStateDto: { + /** + * @description Available liquidity in underlying token units + * @example 250000.00 + */ + liquidity?: Record | null; + /** + * @description Utilization rate as a decimal (e.g., 0.8 = 80%) + * @example 0.80 + */ + utilization?: Record | null; + }; + AllocationRewardRateDto: { + /** + * @description Total reward rate + * @example 5.25 + */ + total: number; + /** + * @description Whether this rate is APR or APY + * @example APY + */ + rateType: string; + }; + AllocationDto: { + /** + * @description Contract address of the underlying strategy + * @example 0x1234567890abcdef1234567890abcdef12345678 + */ + address: string; + /** + * @description Network the underlying strategy is on + * @example base + * @enum {string} + */ + network: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Display name of the underlying strategy + * @example Morpho Moonwell USDC + */ + name: string; + /** + * @description Yield ID if this strategy is supported as a separate yield opportunity + * @example base-usdc-morpho-moonwell-usdc + */ + yieldId?: string; + /** + * @description Provider ID for this strategy (e.g., morpho, aave, lido) + * @example morpho + */ + providerId?: string; + /** + * @description Amount allocated to this strategy in input token units + * @example 50000.00 + */ + allocation: string; + /** + * @description USD value of the allocation + * @example 50000.00 + */ + allocationUsd: string | null; + /** + * @description Current weight of this strategy as a percentage (0-100) + * @example 50.5 + */ + weight: number; + /** + * @description Target weight of this strategy as a percentage (0-100) + * @example 50 + */ + targetWeight: number; + /** @description Reward rate of the underlying strategy */ + rewardRate: components["schemas"]["AllocationRewardRateDto"] | null; + /** + * @description Total value locked in the underlying strategy in input token units + * @example 500.25 + */ + tvl: string | null; + /** + * @description Total value locked in USD for the underlying strategy + * @example 10000000.00 + */ + tvlUsd: string | null; + /** + * @description Maximum capacity of the underlying strategy + * @example 1000000.00 + */ + maxCapacity: string | null; + /** + * @description Remaining capacity in the underlying strategy + * @example 500000.00 + */ + remainingCapacity: string | null; + }; + YieldStateDto: { + /** @description Price per share state metadata */ + pricePerShareState?: components["schemas"]["PricePerShareStateDto"]; + /** @description Concentrated liquidity pool state metadata */ + concentratedLiquidityPoolState?: components["schemas"]["ConcentratedLiquidityPoolStateDto"]; + /** @description Capacity state metadata */ + capacityState?: components["schemas"]["CapacityDto"]; + /** @description Liquidity state (available liquidity, utilization rate) */ + liquidityState?: components["schemas"]["LiquidityStateDto"]; + /** @description Allocations to underlying strategies for vault yields (e.g., OAV, Morpho). Includes allocation, APY, TVL, and capacity per strategy. */ + allocations?: components["schemas"]["AllocationDto"][]; + }; + YieldDto: { + /** + * @description Unique identifier for this yield opportunity + * @example ethereum-eth-lido-staking + */ + id: string; + /** + * @description Network this yield opportunity is on + * @enum {string} + */ + network: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description EVM chain ID for this network (only for EVM networks) + * @example 1 + */ + chainId?: string; + /** @description Accepted input tokens (auto-converted as needed) */ + inputTokens: components["schemas"]["TokenDto"][]; + /** @description Token received from the protocol */ + outputToken?: components["schemas"]["TokenDto"]; + /** @description Canonical deposit token - used for balances */ + token: components["schemas"]["TokenDto"]; + /** @description Canonical deposit tokens - used for balances */ + tokens: components["schemas"]["TokenDto"][]; + /** @description Total effective yield broken down by source and token. */ + rewardRate: components["schemas"]["RewardRateDto"]; + /** @description Key statistics and analytics for this yield opportunity */ + statistics?: components["schemas"]["YieldStatisticsDto"]; + /** @description Risk scores and provider ratings for this yield */ + risk?: components["schemas"]["YieldRiskDto"]; + /** @description Current availability of user actions like enter, exit, claim */ + status: components["schemas"]["YieldStatusDto"]; + /** @description Descriptive metadata including name, logo, description, and documentation */ + metadata: components["schemas"]["YieldMetadataDto"]; + /** @description Operational mechanics including constraints, fees, and capabilities */ + mechanics: components["schemas"]["YieldMechanicsDto"]; + /** + * @description The provider ID this yield belongs to + * @example lido + */ + providerId: string; + /** @description Curator information for the yield (if applicable) */ + curator?: components["schemas"]["CuratorDto"]; + /** + * @description Optional tags for filtering or categorization + * @example [ + * "restaking", + * "ETH", + * "LST" + * ] + */ + tags?: string[]; + /** @description Dynamic, real-time protocol-level state values that affect entering or exiting a yield (e.g., pool price, capacity, price per share, liquidity, queue depth) */ + state?: components["schemas"]["YieldStateDto"]; + }; + /** + * @description Type of balance + * @enum {string} + */ + BalanceType: + | "active" + | "entering" + | "exiting" + | "withdrawable" + | "claimable" + | "locked"; + PendingActionDto: { + /** + * @description High-level action intent + * @example manage + * @enum {string} + */ + intent: "enter" | "manage" | "exit"; + /** + * @description Specific action type + * @example CLAIM_REWARDS + * @enum {string} + */ + type: + | "STAKE" + | "UNSTAKE" + | "CLAIM_REWARDS" + | "RESTAKE_REWARDS" + | "WITHDRAW" + | "WITHDRAW_ALL" + | "RESTAKE" + | "CLAIM_UNSTAKED" + | "UNLOCK_LOCKED" + | "STAKE_LOCKED" + | "VOTE" + | "REVOKE" + | "VOTE_LOCKED" + | "REVOTE" + | "REBOND" + | "MIGRATE" + | "VERIFY_WITHDRAW_CREDENTIALS" + | "DELEGATE"; + /** + * @description Server-generated passthrough that must be included when executing the action + * @example eyJhZGRyZXNzZXMiOnsiYWRkcmVzcyI6ImNvc21vczF5ZXk... + */ + passthrough: string; + /** @description Argument schema required to execute this action */ + arguments?: components["schemas"]["ArgumentSchemaDto"] | null; + /** + * @description Amount involved in the action + * @example 0.1 + */ + amount?: string | null; + }; + RevShareDetailsDto: { + /** + * @description Minimum revenue share percentage (0-1) + * @example 0.3 + */ + minRevShare: number; + /** + * @description Maximum revenue share percentage (0-1) + * @example 0.7 + */ + maxRevShare: number; + }; + RevShareTiersDto: { + /** @description Trial tier revenue share details */ + trial?: components["schemas"]["RevShareDetailsDto"]; + /** @description Standard tier revenue share details */ + standard?: components["schemas"]["RevShareDetailsDto"]; + /** @description Pro tier revenue share details */ + pro?: components["schemas"]["RevShareDetailsDto"]; + }; + ValidatorProviderDto: { + /** + * @description Provider name + * @example Morpho + */ + name: string; + /** + * @description Provider ID + * @example morpho + */ + id: string; + /** + * @description Provider logo URI + * @example https://morpho.xyz/logo.png + */ + logoURI: string; + /** + * @description Short description of the provider + * @example A peer-to-peer DeFi lending protocol + */ + description: string; + /** + * @description Provider website + * @example https://morpho.xyz + */ + website: string; + /** + * @description Total TVL across the entire provider in USD + * @example 10,200,000 + */ + tvlUsd: Record | null; + /** + * @description Type of provider (protocol or validator provider) + * @example protocol + * @enum {string} + */ + type: "protocol" | "validator_provider"; + /** @description Optional social/media references or audit links */ + references?: string[] | null; + /** + * @description Provider ranking (lower numbers indicate higher preference) + * @example 1 + */ + rank: number; + /** + * @description Whether this provider is marked as preferred + * @example true + */ + preferred: boolean; + /** + * @description Revenue sharing details by tier + * @example { + * "standard": { + * "minRevShare": 0.3, + * "maxRevShare": 0.7 + * }, + * "pro": { + * "minRevShare": 0.4, + * "maxRevShare": 0.8 + * } + * } + */ + revshare?: components["schemas"]["RevShareTiersDto"]; + /** + * @deprecated + * @description Provider ID (deprecated, use `id` instead) + * @example luganodes + */ + uniqueId?: string; + /** + * Format: date-time + * @deprecated + * @description Creation timestamp (deprecated) + */ + createdAt?: string; + /** + * Format: date-time + * @deprecated + * @description Last update timestamp (deprecated) + */ + updatedAt?: string; + }; + ValidatorDto: { + /** + * @description Validator address or ID + * @example cosmosvaloper1abc... + */ + address: string; + /** + * @description Validator display name + * @example StakeKit Validator + */ + name?: string; + /** + * @description Validator logo URI + * @example https://stakekit.com/logo.png + */ + logoURI?: string; + /** + * @description Link to validator website + * @example https://stakekit.com + */ + website?: string; + /** + * @description Detailed reward rate breakdown by source (emissions, MEV, fees, etc.) + * @example { + * "total": 8.4, + * "rateType": "APR", + * "components": [ + * { + * "rate": 6.8, + * "rateType": "APR", + * "token": { + * "symbol": "SOL", + * "name": "Solana" + * }, + * "yieldSource": "staking", + * "description": "Solana network inflation rewards" + * }, + * { + * "rate": 1.2, + * "rateType": "APR", + * "token": { + * "symbol": "SOL", + * "name": "Solana" + * }, + * "yieldSource": "validator_commission", + * "description": "Transaction fees from processed transactions" + * }, + * { + * "rate": 0.4, + * "rateType": "APR", + * "token": { + * "symbol": "SOL", + * "name": "Solana" + * }, + * "yieldSource": "mev", + * "description": "MEV from Jito block space auctions" + * } + * ] + * } + */ + rewardRate?: components["schemas"]["RewardRateDto"]; + /** @description Provider information for this validator */ + provider?: components["schemas"]["ValidatorProviderDto"]; + /** + * @description Commission rate charged by validator + * @example 0.05 + */ + commission?: number; + /** + * @description Total value locked with this validator in USD + * @example 18,340,000 + */ + tvlUsd?: string; + /** + * @description Total value locked with this validator in native token + * @example 8250.45 + */ + tvl?: string; + /** + * @description Raw total value locked with this validator (full precision) + * @example 8250450000000000000000 + */ + tvlRaw?: string; + /** + * @description Validator's voting power share (0–1) + * @example 0.013 + */ + votingPower?: number; + /** + * @description Whether this validator is flagged as preferred + * @example true + */ + preferred?: boolean; + /** + * @description Minimum stake allowed in native token + * @example 1.0 + */ + minimumStake?: string; + /** + * @description Maximum available stake before hitting cap in native token + * @example 285,714.28 + */ + remainingPossibleStake?: string; + /** + * @description Number of remaining nominator/delegator slots (for capped chains) + * @example 8 + */ + remainingSlots?: number; + /** + * @description Number of current nominators + * @example 321 + */ + nominatorCount?: number; + /** + * @description Validator status description (active, jailed, unbonding, etc.) + * @example active + */ + status?: string; + /** + * @description ID of the provider backing this validator + * @example provider-1 + */ + providerId?: string; + /** + * @description Price per share of the validator + * @example 1.0 + */ + pricePerShare?: string; + /** + * @description Subnet ID + * @example 1 + */ + subnetId?: number; + /** + * @description Subnet name + * @example Apex + */ + subnetName?: string; + /** + * @description Market cap of the subnet + * @example 1000000 + */ + marketCap?: string; + /** + * @description Token symbol of the subnet + * @example α + */ + tokenSymbol?: string; + }; + BalanceDto: { + /** + * @description User wallet address that owns this balance + * @example 0x1234... + */ + address: string; + type: components["schemas"]["BalanceType"]; + /** + * @description Balance amount in underlying token + * @example 2.625 + */ + amount: string; + /** + * @description Raw balance amount (full precision) + * @example 2625000000000000000 + */ + amountRaw: string; + /** + * Format: date-time + * @description Date relevant to this balance state + * @example 2025-04-23T08:00:00Z + */ + date?: string | null; + /** + * @description Fee configuration ID (if applicable) + * @example fee-config-1 + */ + feeConfigurationId?: string; + /** @description Pending actions for this balance */ + pendingActions: components["schemas"]["PendingActionDto"][]; + /** @description Token used for balance amounts */ + token: components["schemas"]["TokenDto"]; + /** @description Validator information (if applicable) */ + validator?: components["schemas"]["ValidatorDto"] | null; + /** @description Multiple validators information (when balance is distributed across multiple validators) */ + validators?: components["schemas"]["ValidatorDto"][] | null; + /** + * @description Value of the balance in USD + * @example 2,500.00 + */ + amountUsd?: string | null; + /** + * @description Whether this balance is currently earning rewards + * @example true + */ + isEarning: boolean; + /** + * @description Price range for concentrated liquidity positions in tokens[1]/tokens[0] format (e.g., if tokens[0]=WETH and tokens[1]=USDC, then priceRange represents USDC/WETH) + * @example { + * "min": "2700", + * "max": "3310" + * } + */ + priceRange?: Record; + /** + * @description NFT token ID for liquidity positions (e.g., PancakeSwap V3 position NFT ID) + * @example 12345 + */ + tokenId?: string; + /** + * @description Share balance in human-readable format + * @example 1.5 + */ + shareAmount?: string; + /** + * @description Share balance in full precision (smallest unit) + * @example 1500000000000000000 + */ + shareAmountRaw?: string; + /** @description The share token that shareAmount and shareAmountRaw are denominated in */ + shareToken?: components["schemas"]["TokenDto"]; + }; + YieldBalancesDto: { + /** + * @description Unique identifier of the yield + * @example ethereum-eth-lido-staking + */ + yieldId: string; + /** @description List of balances for this yield */ + balances: components["schemas"]["BalanceDto"][]; + /** @description Balance for the output token */ + outputTokenBalance?: components["schemas"]["BalanceDto"] | null; + }; + YieldErrorDto: { + /** + * @description Unique identifier of the yield that failed + * @example ethereum-compound-usdc + */ + yieldId: string; + /** + * @description Error message describing what went wrong + * @example Failed to fetch data from blockchain: RPC timeout + */ + error: string; + }; + BalancesResponseDto: { + /** @description Successful yield balance results */ + items: components["schemas"]["YieldBalancesDto"][]; + /** @description Errors encountered while fetching balances */ + errors: components["schemas"]["YieldErrorDto"][]; + }; + /** @enum {string} */ + Networks: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + GetBalancesArgumentsDto: { + /** + * @description Avalanche C-chain address + * @example 0x123... + */ + cAddressBech?: string; + /** + * @description Avalanche P-chain address + * @example P-avax1... + */ + pAddressBech?: string; + }; + BalancesQueryDto: { + /** + * @description The unique identifier of the yield (optional for chain scanning) + * @example ethereum-eth-lido-staking + */ + yieldId?: string; + /** + * @description User wallet address to check balances for + * @example 0x742d35Cc6634C0532925a3b844Bc454e4438f44e + */ + address: string; + /** @example ethereum */ + network: components["schemas"]["Networks"]; + /** @description Arguments for balance queries */ + arguments?: components["schemas"]["GetBalancesArgumentsDto"]; + }; + BalancesRequestDto: { + /** + * @description Array of balance queries + * @example [ + * { + * "yieldId": "ethereum-eth-lido-staking", + * "address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", + * "network": "ethereum" + * } + * ] + */ + queries: components["schemas"]["BalancesQueryDto"][]; + }; + YieldBalancesRequestDto: { + /** + * @description User wallet address to check balances for + * @example 0x742d35Cc6634C0532925a3b844Bc454e4438f44e + */ + address: string; + /** @description Optional arguments for advanced or protocol-specific balance queries */ + arguments?: components["schemas"]["GetBalancesArgumentsDto"]; + }; + ValidatorQueryDto: { + /** + * @description Offset for pagination + * @default 0 + * @example 0 + */ + offset: number; + /** + * @description Maximum number of items to return + * @default 20 + * @example 20 + */ + limit: number; + /** @description Filter by validator name (case-insensitive, partial match) */ + name?: string; + /** @description Filter by validator address */ + address?: string; + /** @description Filter by provider ID */ + provider?: string; + /** @description Filter by validator status */ + status?: string; + /** @description Filter by preferred flag */ + preferred?: boolean; + }; + TransactionDto: { + /** + * @description Unique transaction identifier + * @example tx_123abc + */ + id: string; + /** + * @description Display title for the transaction + * @example Approve USDC + */ + title: string; + /** + * @description Network this transaction is for + * @example ethereum + * @enum {string} + */ + network: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Current status of the transaction + * @example PENDING + * @enum {string} + */ + status: + | "NOT_FOUND" + | "CREATED" + | "BLOCKED" + | "WAITING_FOR_SIGNATURE" + | "SIGNED" + | "BROADCASTED" + | "PENDING" + | "CONFIRMED" + | "FAILED" + | "SKIPPED"; + /** + * @description Type of transaction operation + * @example STAKE + * @enum {string} + */ + type: + | "SWAP" + | "DEPOSIT" + | "APPROVAL" + | "STAKE" + | "CLAIM_UNSTAKED" + | "CLAIM_REWARDS" + | "RESTAKE_REWARDS" + | "UNSTAKE" + | "SPLIT" + | "MERGE" + | "LOCK" + | "UNLOCK" + | "SUPPLY" + | "ADD_LIQUIDITY" + | "REMOVE_LIQUIDITY" + | "BRIDGE" + | "VOTE" + | "REVOKE" + | "RESTAKE" + | "REBOND" + | "WITHDRAW" + | "WITHDRAW_ALL" + | "CREATE_ACCOUNT" + | "REVEAL" + | "MIGRATE" + | "DELEGATE" + | "UNDELEGATE" + | "UTXO_P_TO_C_IMPORT" + | "UTXO_C_TO_P_IMPORT" + | "WRAP" + | "UNWRAP" + | "UNFREEZE_LEGACY" + | "UNFREEZE_LEGACY_BANDWIDTH" + | "UNFREEZE_LEGACY_ENERGY" + | "UNFREEZE_BANDWIDTH" + | "UNFREEZE_ENERGY" + | "FREEZE_BANDWIDTH" + | "FREEZE_ENERGY" + | "UNDELEGATE_BANDWIDTH" + | "UNDELEGATE_ENERGY" + | "P2P_NODE_REQUEST" + | "CREATE_EIGENPOD" + | "VERIFY_WITHDRAW_CREDENTIALS" + | "START_CHECKPOINT" + | "VERIFY_CHECKPOINT_PROOFS" + | "QUEUE_WITHDRAWALS" + | "COMPLETE_QUEUED_WITHDRAWALS" + | "LZ_DEPOSIT" + | "LZ_WITHDRAW" + | "LUGANODES_PROVISION" + | "LUGANODES_EXIT_REQUEST" + | "INFSTONES_PROVISION" + | "INFSTONES_EXIT_REQUEST" + | "INFSTONES_CLAIM_REQUEST"; + /** + * @description Transaction hash (available after broadcast) + * @example 0x1234567890abcdef... + */ + hash: string | null; + /** + * Format: date-time + * @description When the transaction was created + */ + createdAt: string; + /** + * Format: date-time + * @description When the transaction was broadcasted to the network + */ + broadcastedAt: string | null; + /** @description Signed transaction data (ready for broadcast) */ + signedTransaction: string | null; + /** + * @description The unsigned transaction data to be signed by the wallet + * @example 0x02f87082012a022f2f83018000947a250d5630b4cf539739df2c5dacb4c659f2488d880de0b6b3a764000080c080a0ef0de6c7b46fc75dd6cb86dccc3cfd731c2bdf6f3d736557240c3646c6fe01a6a07cd60b58dfe01847249dfdd7950ba0d045dded5bbe410b07a015a0ed34e5e00d + */ + unsignedTransaction: (string | Record) | null; + /** + * @description Human-readable breakdown of the transaction for display purposes + * @example { + * "method": "stake", + * "inputs": { + * "amount": "1000000000000000000" + * } + * } + */ + annotatedTransaction?: Record | null; + /** @description Detailed transaction data for client-side validation or simulation */ + structuredTransaction?: Record | null; + /** + * @description Zero-based index of the step in the action flow + * @example 0 + */ + stepIndex?: number; + /** + * @description User-friendly description of what this transaction does + * @example Approve USDC for staking + */ + description?: string; + /** @description Error message if the transaction failed */ + error?: string | null; + /** + * @description Estimated gas cost for the transaction + * @example 21000 + */ + gasEstimate?: string; + /** + * @description Link to the blockchain explorer for this transaction + * @example https://etherscan.io/tx/0x1234... + */ + explorerUrl?: string | null; + /** + * @description Whether this transaction is a message rather than a value transfer + * @example false + */ + isMessage?: boolean; + }; + ActionArgumentsDto: { + /** + * @description Amount to stake/unstake + * @example 1000000000000000000 + */ + amount?: string; + /** + * @description Amounts to stake/unstake + * @example [ + * "1000000000000000000", + * "2000000000000000000" + * ] + */ + amounts?: string[]; + /** + * @description Validator address for single validator selection + * @example cosmosvaloper1... + */ + validatorAddress?: string; + /** + * @description Multiple validator addresses + * @example [ + * "cosmosvaloper1...", + * "cosmosvaloper2..." + * ] + */ + validatorAddresses?: string[]; + /** + * @description Provider ID for Ethereum native staking + * @example kiln + */ + providerId?: string; + /** + * @description Duration for Avalanche native staking (in seconds) + * @example 1209600 + */ + duration?: number; + /** + * @description Token for deposits. Use "0x" for native token or provide the token address. For cross-chain deposits, also provide inputTokenNetwork. + * @example 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + */ + inputToken?: string; + /** + * @description Network for the input token. Required for cross-chain deposits when the token is on a different network than the vault. + * @enum {string} + */ + inputTokenNetwork?: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Token for withdrawals. Use "0x" for native token or provide the token address. For cross-chain withdrawals, also provide outputTokenNetwork. + * @example 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + */ + outputToken?: string; + /** + * @description Network for the output token. Required for cross-chain withdrawals when the destination is on a different network than the vault. + * @enum {string} + */ + outputTokenNetwork?: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Subnet ID for Bittensor staking + * @example 1 + */ + subnetId?: number; + /** + * @description Tron resource type for Tron staking + * @enum {string} + */ + tronResource?: "BANDWIDTH" | "ENERGY"; + /** + * @description Fee configuration ID for custom fee settings + * @example custom-fee-config-1 + */ + feeConfigurationId?: string; + /** + * @description Cosmos public key for Cosmos staking + * @example cosmospub1... + */ + cosmosPubKey?: string; + /** + * @description Tezos public key for Tezos staking + * @example edpk... + */ + tezosPubKey?: string; + /** + * @description Avalanche C-chain address + * @example 0x123... + */ + cAddressBech?: string; + /** + * @description Avalanche P-chain address + * @example P-avax1... + */ + pAddressBech?: string; + /** + * @description Transaction execution mode + * @example individual + * @enum {string} + */ + executionMode?: "individual" | "batched"; + /** + * @description Transactions should have Ledger wallet API compatibility for hardware wallet users + * @example true + */ + ledgerWalletApiCompatible?: boolean; + /** + * @description Use max amount for ERC4626 withdraw + * @example true + */ + useMaxAmount?: boolean; + /** + * @description Use instant execution for exit (faster but may have fees) + * @example true + */ + useInstantExecution?: boolean; + /** + * @description Skip pre-flight balance and rent checks + * @example false + */ + skipPrechecks?: boolean; + /** + * @description Minimum price bound for concentrated liquidity pools (as decimal string). Must be non-negative (can be 0) and less than rangeMax. + * @example 0.0 + */ + rangeMin?: string; + /** + * @description Maximum price bound for concentrated liquidity pools (as decimal string). Must be positive and greater than rangeMin. + * @example 1.0 + */ + rangeMax?: string; + /** + * @description Percentage of liquidity to exit (0-100). Required for partial exits from liquidity positions. + * @example 50 + */ + percentage?: number; + /** + * @description NFT token ID for concentrated liquidity positions. Required for exiting specific positions. + * @example 12345 + */ + tokenId?: string; + }; + ActionDto: { + /** + * @description Unique action identifier + * @example action_123abc + */ + id: string; + /** + * @description High-level action intent + * @example manage + * @enum {string} + */ + intent: "enter" | "manage" | "exit"; + /** + * @description Specific action type + * @example CLAIM_REWARDS + * @enum {string} + */ + type: + | "STAKE" + | "UNSTAKE" + | "CLAIM_REWARDS" + | "RESTAKE_REWARDS" + | "WITHDRAW" + | "WITHDRAW_ALL" + | "RESTAKE" + | "CLAIM_UNSTAKED" + | "UNLOCK_LOCKED" + | "STAKE_LOCKED" + | "VOTE" + | "REVOKE" + | "VOTE_LOCKED" + | "REVOTE" + | "REBOND" + | "MIGRATE" + | "VERIFY_WITHDRAW_CREDENTIALS" + | "DELEGATE"; + /** + * @description Yield ID this action belongs to + * @example ethereum-eth-lido-staking + */ + yieldId: string; + /** + * @description User wallet address + * @example 0x1234... + */ + address: string; + /** + * @description Amount involved in the action + * @example 1000000000000000000 + */ + amount: string | null; + /** + * @description Raw wei amount (full precision) + * @example 1000000000000000000 + */ + amountRaw: string | null; + /** + * @description USD value of the amount + * @example 1500.50 + */ + amountUsd: string | null; + /** @description Array of transactions for this action */ + transactions: components["schemas"]["TransactionDto"][]; + /** + * @description Transaction execution pattern - synchronous (submit one by one, wait for each), asynchronous (submit all at once), or batch (single transaction with multiple operations) + * @example synchronous + * @enum {string} + */ + executionPattern: "synchronous" | "asynchronous" | "batch"; + /** @description Raw arguments exactly as submitted by the user for this action */ + rawArguments: components["schemas"]["ActionArgumentsDto"] | null; + /** + * Format: date-time + * @description When the action was created + */ + createdAt: string; + /** + * Format: date-time + * @description When the action was completed + */ + completedAt: string | null; + /** + * @description Current status of the action + * @enum {string} + */ + status: + | "CANCELED" + | "CREATED" + | "WAITING_FOR_NEXT" + | "PROCESSING" + | "FAILED" + | "SUCCESS" + | "STALE"; + }; + PaginatedResponseDto: { + /** + * @description Total number of items available + * @example 100 + */ + total: number; + /** + * @description Offset of the current page + * @example 0 + */ + offset: number; + /** + * @description Limit of the current page + * @example 20 + */ + limit: number; + }; + YieldQueryDto: { + /** + * @description Offset for pagination + * @default 0 + * @example 0 + */ + offset: number; + /** + * @description Maximum number of items to return + * @default 20 + * @example 20 + */ + limit: number; + /** + * @description Filter by network + * @enum {string} + */ + network?: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Filter by EVM chain ID (Ethereum: 1, Polygon: 137, etc) + * @example 1 + */ + chainId?: string; + /** @description Filter by multiple networks (comma separated) */ + networks?: string; + /** @example optimism-usdt-aave-v3-lending */ + yieldId?: string; + /** + * @example [ + * "optimism-usdt-aave-v3-lending" + * ] + */ + yieldIds?: string[]; + /** + * @description Filter by yield type + * @enum {string} + */ + type?: + | "staking" + | "restaking" + | "lending" + | "vault" + | "fixed_yield" + | "real_world_asset" + | "concentrated_liquidity_pool" + | "liquidity_pool"; + /** @description Filter by multiple yield types (comma separated) */ + types?: ( + | "staking" + | "restaking" + | "lending" + | "vault" + | "fixed_yield" + | "real_world_asset" + | "concentrated_liquidity_pool" + | "liquidity_pool" + )[]; + /** @description Filter by cooldown period */ + hasCooldownPeriod?: boolean; + /** @description Filter by warmup period */ + hasWarmupPeriod?: boolean; + /** @description Filter by token symbol or address */ + token?: string; + /** @description Filter by input token symbol or address */ + inputToken?: string; + /** @description Filter by multiple input token symbol or address (comma separated) */ + inputTokens?: string[]; + /** @description Filter by provider ID */ + provider?: string; + /** @description Filter by multiple provider IDs (comma separated) */ + providers?: string[]; + /** @description Search by yield name */ + search?: string; + /** + * @description Sort by yield status + * @enum {string} + */ + sort?: + | "statusEnterAsc" + | "statusEnterDesc" + | "statusExitAsc" + | "statusExitDesc"; + }; + PaginationQueryDto: { + /** + * @description Offset for pagination + * @default 0 + * @example 0 + */ + offset: number; + /** + * @description Maximum number of items to return + * @default 20 + * @example 20 + */ + limit: number; + }; + RiskParameterDto: { + id: string; + category: string; + item: string; + isDynamic: boolean; + value?: Record; + network?: components["schemas"]["Networks"]; + asset?: Record; + protocol?: Record; + integrationId?: Record; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + }; + CreateActionDto: { + /** + * @description Yield ID to perform the action on + * @example ethereum-eth-lido-staking + */ + yieldId: string; + /** + * @description User wallet address + * @example 0x1234... + */ + address: string; + /** @description Arguments for the action */ + arguments?: components["schemas"]["ActionArgumentsDto"]; + }; + CreateManageActionDto: { + /** + * @description Yield ID to perform the action on + * @example ethereum-eth-lido-staking + */ + yieldId: string; + /** + * @description User wallet address + * @example 0x1234... + */ + address: string; + /** @description Arguments for the action */ + arguments?: components["schemas"]["ActionArgumentsDto"]; + /** + * @description Pending action type (required for manage actions) + * @example CLAIM_REWARDS + * @enum {string} + */ + action: + | "STAKE" + | "UNSTAKE" + | "CLAIM_REWARDS" + | "RESTAKE_REWARDS" + | "WITHDRAW" + | "WITHDRAW_ALL" + | "RESTAKE" + | "CLAIM_UNSTAKED" + | "UNLOCK_LOCKED" + | "STAKE_LOCKED" + | "VOTE" + | "REVOKE" + | "VOTE_LOCKED" + | "REVOTE" + | "REBOND" + | "MIGRATE" + | "VERIFY_WITHDRAW_CREDENTIALS" + | "DELEGATE"; + /** + * @description Server-generated passthrough from the balances endpoint (required for manage actions) + * @example eyJhZGRyZXNzZXMiOnsiYWRkcmVzcyI6ImNvc21vczF5ZXk... + */ + passthrough: string; + }; + ActionsQueryDto: { + /** + * @description Offset for pagination + * @default 0 + * @example 0 + */ + offset: number; + /** + * @description Maximum number of items to return + * @default 20 + * @example 20 + */ + limit: number; + /** + * @description User wallet address to filter actions for + * @example 0x742d35Cc6634C0532925a3b844Bc454e4438f44e + */ + address: string; + /** + * @description Filter by action status + * @enum {string} + */ + status?: + | "CANCELED" + | "CREATED" + | "WAITING_FOR_NEXT" + | "PROCESSING" + | "FAILED" + | "SUCCESS" + | "STALE"; + /** + * @description Filter by action intent + * @enum {string} + */ + intent?: "enter" | "manage" | "exit"; + /** + * @description Filter by action type + * @enum {string} + */ + type?: + | "STAKE" + | "UNSTAKE" + | "CLAIM_REWARDS" + | "RESTAKE_REWARDS" + | "WITHDRAW" + | "WITHDRAW_ALL" + | "RESTAKE" + | "CLAIM_UNSTAKED" + | "UNLOCK_LOCKED" + | "STAKE_LOCKED" + | "VOTE" + | "REVOKE" + | "VOTE_LOCKED" + | "REVOTE" + | "REBOND" + | "MIGRATE" + | "VERIFY_WITHDRAW_CREDENTIALS" + | "DELEGATE"; + /** + * @description Filter by yield ID + * @example ethereum-eth-lido-staking + */ + yieldId?: string; + }; + SubmitHashDto: { + /** + * @description Transaction hash from the blockchain + * @example 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef + */ + hash: string; + }; + SubmitTransactionDto: { + /** + * @description Encoded signed transaction to submit to the blockchain + * @example 0aba010aa0010a232f636f736d6f732e7374616b696e672e763162657461312e4d736744656c656761746512790a2a696e6a316a61366664646e6e33727272677137646d6a757a6b71363279376d68346675346b6e656d37791231696e6a76616c6f7065723167346436646d766e706737773779756779366b706c6e6470376a70666d66336b7274736368701a180a03696e6a121131303030303030303030303030303030301215766961205374616b654b6974204349442d31303039129e010a7e0a740a2d2f696e6a6563746976652e63727970746f2e763162657461312e657468736563703235366b312e5075624b657912430a41042aec99dce37ea3d8f11b44da62bce0e885f0ba5b309382954babec76eb138cb0bb84f4f24b9f63143f2ce66923b2dd3ee55475e680a7b992b9cbc17941f6486312040a0208011802121c0a160a03696e6a120f31383732303030303030303030303010d0b4471a0b696e6a6563746976652d312092c35b + */ + signedTransaction: string; + }; + NetworkDto: { + /** + * @description The network identifier + * @example ethereum + * @enum {string} + */ + id: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Human-readable display name of the network + * @example Ethereum + */ + name: string; + /** + * @description The category of the network + * @example evm + * @enum {string} + */ + category: "evm" | "cosmos" | "substrate" | "misc"; + /** + * @description Logo URI for the network + * @example https://assets.stakek.it/networks/ethereum.svg + */ + logoURI: string; + }; + ProviderDto: { + /** + * @description Provider name + * @example Morpho + */ + name: string; + /** + * @description Provider ID + * @example morpho + */ + id: string; + /** + * @description Provider logo URI + * @example https://morpho.xyz/logo.png + */ + logoURI: string; + /** + * @description Short description of the provider + * @example A peer-to-peer DeFi lending protocol + */ + description: string; + /** + * @description Provider website + * @example https://morpho.xyz + */ + website: string; + /** + * @description Total TVL across the entire provider in USD + * @example 10,200,000 + */ + tvlUsd: Record | null; + /** + * @description Type of provider (protocol or validator provider) + * @example protocol + * @enum {string} + */ + type: "protocol" | "validator_provider"; + /** @description Optional social/media references or audit links */ + references?: string[] | null; + }; + /** + * @description The health status of the service + * @enum {string} + */ + HealthStatus: "OK" | "FAIL"; + HealthStatusDto: { + /** @example OK */ + status: components["schemas"]["HealthStatus"]; + /** + * Format: date-time + * @description Timestamp when the health check was performed + * @example 2024-01-15T10:30:00.000Z + */ + timestamp: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + YieldsController_getYields: { + parameters: { + query?: { + /** + * @description Offset for pagination + * @example 0 + */ + offset?: number; + /** + * @description Number of items per page + * @example 20 + */ + limit?: number; + /** @description Filter by network */ + network?: + | "ethereum" + | "ethereum-goerli" + | "ethereum-holesky" + | "ethereum-sepolia" + | "ethereum-hoodi" + | "arbitrum" + | "base" + | "base-sepolia" + | "gnosis" + | "optimism" + | "polygon" + | "polygon-amoy" + | "starknet" + | "zksync" + | "linea" + | "unichain" + | "monad-testnet" + | "monad" + | "avalanche-c" + | "avalanche-c-atomic" + | "avalanche-p" + | "binance" + | "celo" + | "fantom" + | "harmony" + | "moonriver" + | "okc" + | "viction" + | "core" + | "sonic" + | "plasma" + | "katana" + | "hyperevm" + | "agoric" + | "akash" + | "axelar" + | "band-protocol" + | "bitsong" + | "canto" + | "chihuahua" + | "comdex" + | "coreum" + | "cosmos" + | "crescent" + | "cronos" + | "cudos" + | "desmos" + | "dydx" + | "evmos" + | "fetch-ai" + | "gravity-bridge" + | "injective" + | "irisnet" + | "juno" + | "kava" + | "ki-network" + | "mars-protocol" + | "nym" + | "okex-chain" + | "onomy" + | "osmosis" + | "persistence" + | "quicksilver" + | "regen" + | "secret" + | "sentinel" + | "sommelier" + | "stafi" + | "stargaze" + | "stride" + | "teritori" + | "tgrade" + | "umee" + | "sei" + | "mantra" + | "celestia" + | "saga" + | "zetachain" + | "dymension" + | "humansai" + | "neutron" + | "polkadot" + | "kusama" + | "westend" + | "bittensor" + | "aptos" + | "binancebeacon" + | "cardano" + | "near" + | "solana" + | "solana-devnet" + | "stellar" + | "stellar-testnet" + | "sui" + | "tezos" + | "tron" + | "ton" + | "ton-testnet" + | "hyperliquid"; + /** + * @description Filter by EVM chain ID (Ethereum: 1, Polygon: 137) + * @example 1 + */ + chainId?: string; + /** @description Filter by multiple networks (comma separated) */ + networks?: string; + /** @example optimism-usdt-aave-v3-lending */ + yieldId?: string; + /** + * @example [ + * "optimism-usdt-aave-v3-lending" + * ] + */ + yieldIds?: string[]; + /** @description Filter by yield type */ + type?: + | "staking" + | "restaking" + | "lending" + | "vault" + | "fixed_yield" + | "real_world_asset" + | "concentrated_liquidity_pool" + | "liquidity_pool"; + /** @description Filter by multiple yield types (comma separated) */ + types?: ( + | "staking" + | "restaking" + | "lending" + | "vault" + | "fixed_yield" + | "real_world_asset" + | "concentrated_liquidity_pool" + | "liquidity_pool" + )[]; + /** + * @description Filter by cooldown period + * @example true + */ + hasCooldownPeriod?: boolean; + /** + * @description Filter by warmup period + * @example true + */ + hasWarmupPeriod?: boolean; + /** @description Filter by token symbol or address */ + token?: string; + /** @description Filter by input token symbol or address */ + inputToken?: string; + /** @description Filter by multiple input token symbol or address (comma separated) */ + inputTokens?: string[]; + /** @description Filter by provider ID */ + provider?: string; + /** @description Filter by multiple provider IDs (comma separated) */ + providers?: string[]; + /** @description Search by yield name */ + search?: string; + /** @description Sort by yield status */ + sort?: + | "statusEnterAsc" + | "statusEnterDesc" + | "statusExitAsc" + | "statusExitDesc"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns a paginated list of yield opportunities */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponseDto"] & { + items?: components["schemas"]["YieldDto"][]; + }; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + YieldsController_getAggregateBalances: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Request containing an array of balance queries. Each query contains: yieldId (optional), address (required), network (required), and arguments (optional). When yieldId is omitted, all yields for that network will be scanned. You can mix chain scans with specific yield queries - duplicates are automatically deduplicated with specific queries taking precedence. */ + requestBody: { + content: { + "application/json": components["schemas"]["BalancesRequestDto"]; + }; + }; + responses: { + /** @description Returns balances grouped by yield with detailed error information for failed yields. Only yields with non-zero balances are included in the response. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BalancesResponseDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + YieldsController_getYield: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the yield + * @example ethereum-eth-lido-staking + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns yield metadata including network, APR, TVL, and token information */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["YieldDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + YieldsController_getYieldRisk: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the yield + * @example ethereum-eth-lido-staking + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Risk metadata retrieved successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RiskParameterDto"][]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + YieldsController_getYieldBalances: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the yield opportunity + * @example ethereum-eth-lido-staking + */ + yieldId: string; + }; + cookie?: never; + }; + /** @description Balance request with address and optional arguments for advanced balance queries */ + requestBody: { + content: { + "application/json": components["schemas"]["YieldBalancesRequestDto"]; + }; + }; + responses: { + /** @description Returns balance information including different balance types (active, entering, exiting, withdrawable, claimable, locked) with amounts and available actions */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["YieldBalancesDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + YieldsController_getYieldValidators: { + parameters: { + query?: { + /** + * @description Offset for pagination + * @example 0 + */ + offset?: number; + /** + * @description Number of items per page + * @example 20 + */ + limit?: number; + /** @description Filter by validator name (case-insensitive, partial match) */ + name?: string; + /** @description Filter by validator address */ + address?: string; + /** @description Filter by provider ID */ + provider?: string; + /** @description Filter by validator status */ + status?: string; + /** @description Filter by preferred flag */ + preferred?: boolean; + }; + header?: never; + path: { + /** + * @description The unique identifier of the yield + * @example cosmos-staking + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns paginated list of available validators with detailed information */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponseDto"] & { + items?: components["schemas"]["ValidatorDto"][]; + }; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ActionsController_getActions: { + parameters: { + query: { + /** + * @description Offset for pagination + * @example 0 + */ + offset?: number; + /** + * @description Maximum number of items to return + * @example 20 + */ + limit?: number; + /** + * @description User wallet address to get actions for + * @example 0x742d35Cc6634C0532925a3b844Bc454e4438f44e + */ + address: string; + /** @description Filter actions by status */ + status?: "pending" | "completed" | "failed"; + /** @description Filter actions by intent */ + intent?: "enter" | "exit" | "manage"; + /** @description Filter by action type */ + type?: + | "STAKE" + | "UNSTAKE" + | "CLAIM_REWARDS" + | "RESTAKE_REWARDS" + | "WITHDRAW" + | "WITHDRAW_ALL" + | "RESTAKE" + | "CLAIM_UNSTAKED" + | "UNLOCK_LOCKED" + | "STAKE_LOCKED" + | "VOTE" + | "REVOKE" + | "VOTE_LOCKED" + | "REVOTE" + | "REBOND" + | "MIGRATE" + | "VERIFY_WITHDRAW_CREDENTIALS" + | "DELEGATE"; + /** + * @description Filter actions by specific yield + * @example ethereum-eth-lido-staking + */ + yieldId?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns a paginated list of user actions */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponseDto"] & { + items?: components["schemas"]["ActionDto"][]; + }; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ActionsController_getAction: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the action + * @example action_123abc + */ + actionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Action details retrieved successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ActionDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Action not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ActionsController_enterYield: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateActionDto"]; + }; + }; + responses: { + /** @description Returns action with array of transactions to execute */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ActionDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Access denied due to geolocation restrictions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Access denied from US (US-CA) */ + message?: string; + /** @example Forbidden */ + error?: string; + /** @example 403 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ActionsController_exitYield: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateActionDto"]; + }; + }; + responses: { + /** @description Returns action with array of transactions to execute */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ActionDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Access denied due to geolocation restrictions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Access denied from US (US-CA) */ + message?: string; + /** @example Forbidden */ + error?: string; + /** @example 403 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ActionsController_manageYield: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateManageActionDto"]; + }; + }; + responses: { + /** @description Returns action with array of transactions to execute */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ActionDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Access denied due to geolocation restrictions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Access denied from US (US-CA) */ + message?: string; + /** @example Forbidden */ + error?: string; + /** @example 403 */ + statusCode?: number; + }; + }; + }; + /** @description Yield not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + TransactionsController_submitTransactionHash: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the transaction + * @example 21f5fd15-cf80-4b9a-804f-062c40dc3740 + */ + transactionId: unknown; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SubmitHashDto"]; + }; + }; + responses: { + /** @description Transaction successfully updated with hash */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TransactionDto"]; + }; + }; + /** @description Invalid transaction hash format */ + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Transaction not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + TransactionsController_submitTransaction: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the transaction + * @example 21f5fd15-cf80-4b9a-804f-062c40dc3740 + */ + transactionId: unknown; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SubmitTransactionDto"]; + }; + }; + responses: { + /** @description Transaction successfully submitted */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TransactionDto"]; + }; + }; + /** @description Invalid transaction format */ + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Transaction not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + TransactionsController_getTransaction: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The unique identifier of the transaction + * @example 21f5fd15-cf80-4b9a-804f-062c40dc3740 + */ + transactionId: unknown; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Transaction details retrieved successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TransactionDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Transaction not found with the specified ID */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + NetworksController_getNetworks: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns a list of all available networks */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NetworkDto"][]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ProvidersController_getProviders: { + parameters: { + query?: { + /** + * @description Offset for pagination + * @example 0 + */ + offset?: number; + /** + * @description Number of items per page + * @example 20 + */ + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of providers */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponseDto"] & { + items?: components["schemas"]["ProviderDto"][]; + }; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + ProvidersController_getProvider: { + parameters: { + query?: never; + header?: never; + path: { + providerId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Provider details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderDto"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Validation failed */ + message?: string; + /** @example Bad Request */ + error?: string; + /** @example 400 */ + statusCode?: number; + }; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Invalid API key */ + message?: string; + /** @example Unauthorized */ + error?: string; + /** @example 401 */ + statusCode?: number; + }; + }; + }; + /** @description Rate limit exceeded */ + 429: { + headers: { + /** @description Request limit per window */ + "x-ratelimit-limit"?: string; + /** @description Remaining requests (will be 0) */ + "x-ratelimit-remaining"?: string; + /** @description Unix timestamp when window resets */ + "x-ratelimit-reset"?: string; + /** @description Seconds to wait before retrying */ + "retry-after"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Rate limit exceeded */ + message?: string; + /** @example Too Many Requests */ + error?: string; + /** @example 429 */ + statusCode?: number; + /** @example 30 */ + retryAfter?: number; + }; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Internal server error */ + message?: string; + /** @example Internal Server Error */ + error?: string; + /** @example 500 */ + statusCode?: number; + }; + }; + }; + }; + }; + HealthController_health: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Health check status with timestamp */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HealthStatusDto"]; + }; + }; + }; + }; +} diff --git a/packages/widget/src/utils/formatters.ts b/packages/widget/src/utils/formatters.ts index 82d8dc56..81b4c5dc 100644 --- a/packages/widget/src/utils/formatters.ts +++ b/packages/widget/src/utils/formatters.ts @@ -3,6 +3,7 @@ import type BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { getTokenPriceInUSD } from "../domain"; import { Prices } from "../domain/types/price"; +import type { YieldTokenDto } from "../providers/yield-api-client-provider/types"; import { APToPercentage, defaultFormattedNumber, formatNumber } from "."; export const formatCountryCode = ({ @@ -79,7 +80,7 @@ export const getFeesInUSD = ({ token, }: { amount: Maybe; - token: Maybe; + token: Maybe; prices: Maybe; }) => Maybe.fromRecord({ token, amount }) diff --git a/packages/widget/tests/use-cases/deep-links-flow/setup.ts b/packages/widget/tests/use-cases/deep-links-flow/setup.ts index abc739d7..044baf3b 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -120,6 +120,19 @@ export const setup = async (opts?: { }, ]; + const avaxLiquidStakingBalancesV2 = [ + { + address: account, + type: "claimable", + amount: "0.019258000000000000", + amountRaw: "19258000000000000", + pendingActions: avaxLiquidStakingBalances[0].pendingActions, + token: avaxLiquidStaking.token, + amountUsd: "0.84", + isEarning: false, + }, + ]; + const pendingAction = Just(pendingActionFixture()) .map((def): typeof def => ({ ...def, @@ -202,6 +215,18 @@ export const setup = async (opts?: { }, ]); }), + http.post("*/v1/yields/balances", async () => { + await delay(); + return HttpResponse.json({ + items: [ + { + yieldId: avaxLiquidStaking.id, + balances: avaxLiquidStakingBalancesV2, + }, + ], + errors: [], + }); + }), http.post(`*/v1/yields/${avaxNativeStaking.id}/balances/scan`, async () => { await delay(); return HttpResponse.json({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6db09c3a..05be648b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -320,6 +320,15 @@ importers: msw: specifier: ^2.12.4 version: 2.12.4(@types/node@25.0.2)(typescript@5.9.3) + openapi-fetch: + specifier: ^0.17.0 + version: 0.17.0 + openapi-react-query: + specifier: ^0.5.4 + version: 0.5.4(@tanstack/react-query@5.90.12(react@19.2.3))(openapi-fetch@0.17.0) + openapi-typescript: + specifier: ^7.13.0 + version: 7.13.0(typescript@5.9.3) playwright: specifier: ^1.57.0 version: 1.57.0 @@ -3140,6 +3149,16 @@ packages: '@types/react': optional: true + '@redocly/ajv@8.17.4': + resolution: {integrity: sha512-BieiCML/IgP6x99HZByJSt7fJE4ipgzO7KAFss92Bs+PEI35BhY7vGIysFXLT+YmS7nHtQjZjhOQyPPEf7xGHA==} + + '@redocly/config@0.22.2': + resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==} + + '@redocly/openapi-core@1.34.7': + resolution: {integrity: sha512-gn2P0OER6qxF/+f4GqNv9XsnU5+6oszD/0SunulOvPYJDhrNkNVrVZV5waX25uqw5UDn2+roViWlRDHKFfHH0g==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + '@reown/appkit-common@1.7.2': resolution: {integrity: sha512-DZkl3P5+Iw3TmsitWmWxYbuSCox8iuzngNp/XhbNDJd7t4Cj4akaIUxSEeCajNDiGHlu4HZnfyM1swWsOJ0cOw==} @@ -5915,6 +5934,9 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} @@ -6011,6 +6033,9 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -6871,7 +6896,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} @@ -7085,6 +7110,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -7373,6 +7402,10 @@ packages: js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} @@ -7797,6 +7830,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.7: + resolution: {integrity: sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==} + engines: {node: '>=10'} + minimatch@7.4.6: resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} engines: {node: '>=10'} @@ -8165,9 +8202,27 @@ packages: openapi-fetch@0.13.8: resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} + openapi-fetch@0.17.0: + resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==} + + openapi-react-query@0.5.4: + resolution: {integrity: sha512-V9lRiozjHot19/BYSgXYoyznDxDJQhEBSdi26+SJ0UqjMANLQhkni4XG+Z7e3Ag7X46ZLMrL9VxYkghU3QvbWg==} + peerDependencies: + '@tanstack/react-query': ^5.80.0 + openapi-fetch: ^0.17.0 + openapi-typescript-helpers@0.0.15: resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} + openapi-typescript-helpers@0.1.0: + resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} + + openapi-typescript@7.13.0: + resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==} + hasBin: true + peerDependencies: + typescript: ^5.x + ora@8.2.0: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} @@ -8301,6 +8356,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} @@ -8433,6 +8492,10 @@ packages: please-upgrade-node@3.2.0: resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} @@ -9340,6 +9403,10 @@ packages: resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} engines: {node: '>=14.0.0'} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -9564,6 +9631,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-fest@5.3.1: resolution: {integrity: sha512-VCn+LMHbd4t6sF3wfU/+HKT63C9OoyrSIf4b+vtWHpt2U7/4InZG467YDNMFMR70DdHjAdpPWmw2lzRdg0Xqqg==} engines: {node: '>=20'} @@ -10091,6 +10162,7 @@ packages: whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} @@ -10289,6 +10361,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -10474,7 +10549,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -10644,7 +10719,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -13022,7 +13097,7 @@ snapshots: '@scure/base': 1.2.6 '@types/debug': 4.1.12 '@types/lodash': 4.17.21 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) lodash: 4.17.21 pony-cause: 2.1.11 semver: 7.7.3 @@ -13035,7 +13110,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) semver: 7.7.3 superstruct: 1.0.4 transitivePeerDependencies: @@ -13048,7 +13123,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) pony-cause: 2.1.11 semver: 7.7.3 uuid: 9.0.1 @@ -13062,7 +13137,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) pony-cause: 2.1.11 semver: 7.7.3 uuid: 9.0.1 @@ -14028,7 +14103,7 @@ snapshots: '@react-native/community-cli-plugin@0.81.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@react-native/dev-middleware': 0.81.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) invariant: 2.2.4 metro: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) metro-config: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -14048,7 +14123,7 @@ snapshots: chrome-launcher: 0.15.2 chromium-edge-launcher: 0.2.0 connect: 3.7.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) invariant: 2.2.4 nullthrows: 1.1.1 open: 7.4.2 @@ -14074,6 +14149,29 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 + '@redocly/ajv@8.17.4': + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + '@redocly/config@0.22.2': {} + + '@redocly/openapi-core@1.34.7(supports-color@10.2.2)': + dependencies: + '@redocly/ajv': 8.17.4 + '@redocly/config': 0.22.2 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + js-levenshtein: 1.1.6 + js-yaml: 4.1.1 + minimatch: 5.1.7 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + '@reown/appkit-common@1.7.2(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4)': dependencies: big.js: 6.2.2 @@ -17206,7 +17304,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.13.1 '@typescript-eslint/visitor-keys': 7.13.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -20108,6 +20206,8 @@ snapshots: chalk@5.6.2: {} + change-case@5.4.4: {} + chardet@2.1.1: {} charenc@0.0.2: @@ -20212,6 +20312,8 @@ snapshots: color-convert: 2.0.1 color-string: 1.9.1 + colorette@1.4.0: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -20463,9 +20565,11 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.3: + debug@4.4.3(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 decamelize@1.2.0: {} @@ -20528,7 +20632,7 @@ snapshots: callsite: 1.0.0 camelcase: 6.3.0 cosmiconfig: 7.1.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) deps-regex: 0.2.0 findup-sync: 5.0.0 ignore: 5.3.2 @@ -21315,17 +21419,17 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color optional: true https-browserify@1.0.0: {} - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -21392,6 +21496,8 @@ snapshots: imurmurhash@0.1.4: {} + index-to-position@1.2.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -21692,6 +21798,8 @@ snapshots: js-base64@3.7.8: {} + js-levenshtein@1.1.6: {} + js-sha3@0.8.0: {} js-tokens@4.0.0: {} @@ -21725,7 +21833,7 @@ snapshots: decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 parse5: 8.0.0 saxes: 6.0.0 @@ -22056,7 +22164,7 @@ snapshots: dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) metro-core: 0.83.3 transitivePeerDependencies: - supports-color @@ -22084,7 +22192,7 @@ snapshots: metro-file-map@0.83.3: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fb-watchman: 2.0.2 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -22180,7 +22288,7 @@ snapshots: chalk: 4.1.2 ci-info: 2.0.0 connect: 3.7.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -22256,6 +22364,10 @@ snapshots: dependencies: brace-expansion: 1.1.11 + minimatch@5.1.7: + dependencies: + brace-expansion: 2.0.2 + minimatch@7.4.6: dependencies: brace-expansion: 2.0.2 @@ -22565,9 +22677,31 @@ snapshots: openapi-typescript-helpers: 0.0.15 optional: true + openapi-fetch@0.17.0: + dependencies: + openapi-typescript-helpers: 0.1.0 + + openapi-react-query@0.5.4(@tanstack/react-query@5.90.12(react@19.2.3))(openapi-fetch@0.17.0): + dependencies: + '@tanstack/react-query': 5.90.12(react@19.2.3) + openapi-fetch: 0.17.0 + openapi-typescript-helpers: 0.1.0 + openapi-typescript-helpers@0.0.15: optional: true + openapi-typescript-helpers@0.1.0: {} + + openapi-typescript@7.13.0(typescript@5.9.3): + dependencies: + '@redocly/openapi-core': 1.34.7(supports-color@10.2.2) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.2.2 + typescript: 5.9.3 + yargs-parser: 21.1.1 + ora@8.2.0: dependencies: chalk: 5.6.2 @@ -22783,6 +22917,12 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.27.1 + index-to-position: 1.2.0 + type-fest: 4.41.0 + parse-node-version@1.0.1: optional: true @@ -22916,6 +23056,8 @@ snapshots: dependencies: semver-compare: 1.0.0 + pluralize@8.0.0: {} + pngjs@5.0.0: {} pngjs@7.0.0: {} @@ -23835,7 +23977,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -24001,6 +24143,8 @@ snapshots: superstruct@2.0.2: {} + supports-color@10.2.2: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -24202,6 +24346,8 @@ snapshots: type-fest@2.19.0: {} + type-fest@4.41.0: {} + type-fest@5.3.1: dependencies: tagged-tag: 1.0.0 @@ -24569,7 +24715,7 @@ snapshots: vite-node@3.2.4(@types/node@25.0.2)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.2.7(@types/node@25.0.2)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -24912,6 +25058,8 @@ snapshots: yallist@3.1.1: {} + yaml-ast-parser@0.0.43: {} + yaml@1.10.2: {} yaml@2.8.2: {} From 4ae72e93365240377cdc6183acf63cda37816ecd Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 4 Mar 2026 14:02:00 +0100 Subject: [PATCH 2/2] fix: tests --- packages/widget/tests/use-cases/deep-links-flow/setup.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/widget/tests/use-cases/deep-links-flow/setup.ts b/packages/widget/tests/use-cases/deep-links-flow/setup.ts index 044baf3b..69028ec8 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -236,7 +236,10 @@ export const setup = async (opts?: { }), http.post(`*/v1/yields/${avaxLiquidStaking.id}/balances`, async () => { await delay(); - return HttpResponse.json(avaxLiquidStakingBalances); + return HttpResponse.json({ + yieldId: avaxLiquidStaking.id, + balances: avaxLiquidStakingBalancesV2, + }); }), http.post("*/v1/actions/pending", async (info) => { const data = (await info.request.json()) as { integrationId: string };