From efeafe7bfe026d8a3efd20aa6541f01a8a7e806b Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 4 Mar 2026 11:50:39 +0100 Subject: [PATCH 01/23] 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 02/23] 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 }; From a412e92c068182ef4e4533ab3275dbbb8e62c460 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 5 Mar 2026 13:02:45 +0100 Subject: [PATCH 03/23] fix: position size --- packages/widget/src/domain/types/positions.ts | 47 +++++++++++++++++-- packages/widget/src/hooks/use-summary.tsx | 8 +++- .../positions/hooks/use-position-list-item.ts | 6 +-- .../components/positions-list-item.tsx | 3 +- .../hooks/use-position-list-item.ts | 16 +++++-- 5 files changed, 65 insertions(+), 15 deletions(-) diff --git a/packages/widget/src/domain/types/positions.ts b/packages/widget/src/domain/types/positions.ts index f91b63fc..0f597209 100644 --- a/packages/widget/src/domain/types/positions.ts +++ b/packages/widget/src/domain/types/positions.ts @@ -1,3 +1,4 @@ +import type { TokenDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import type { YieldBalanceDto, @@ -5,6 +6,7 @@ import type { YieldBalanceType, } from "../../providers/yield-api-client-provider/types"; import type { components } from "../../types/yield-api-schema"; +import { equalTokens } from ".."; export type PositionBalancesByType = Map< YieldBalanceType, @@ -49,15 +51,50 @@ export const getPositionBalanceDataKey = ( return "default"; }; -export const getPositionTotalAmount = (balances: YieldBalanceDto[]) => - balances.reduce( +export const getPositionTotalAmount = ( + balances: YieldBalanceDto[], + baseToken: TokenDto +) => { + const baseTokenBalance = balances.find((b) => + equalTokens(b.token, baseToken) + ); + + const baseTokenPriceInUsd = (() => { + if (!baseTokenBalance?.amountUsd) return null; + + const amount = BigNumber(baseTokenBalance.amount); + if (amount.lte(0)) return null; + + return BigNumber(baseTokenBalance.amountUsd).dividedBy(amount); + })(); + + return 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); + if (baseTokenBalance && equalTokens(b.token, baseTokenBalance.token)) { + return { + amount: acc.amount.plus(b.amount), + amountUsd: acc.amountUsd.plus(b.amountUsd ?? 0), + }; + } + + const balanceAmountUsd = BigNumber(b.amountUsd ?? 0); - return acc; + if (baseTokenPriceInUsd && !baseTokenPriceInUsd.isZero()) { + return { + amount: acc.amount.plus( + balanceAmountUsd.dividedBy(baseTokenPriceInUsd) + ), + amountUsd: acc.amountUsd.plus(balanceAmountUsd), + }; + } + + return { + amount: acc.amount.plus(b.amount), + amountUsd: acc.amountUsd.plus(balanceAmountUsd), + }; }, { amount: new BigNumber(0), amountUsd: new BigNumber(0) } ); +}; diff --git a/packages/widget/src/hooks/use-summary.tsx b/packages/widget/src/hooks/use-summary.tsx index 1177a648..2b880a3c 100644 --- a/packages/widget/src/hooks/use-summary.tsx +++ b/packages/widget/src/hooks/use-summary.tsx @@ -104,7 +104,10 @@ export const SummaryProvider = ({ if (!yieldDto) return []; - const positionTotalAmount = getPositionTotalAmount(p.balancesWithAmount); + const positionTotalAmount = getPositionTotalAmount( + p.balancesWithAmount, + getBaseToken(yieldDto) + ); const yields = [...multiYieldsMapQuery.data.values()]; @@ -236,7 +239,8 @@ export const SummaryProvider = ({ if (!yieldDto) return acc; const positionTotalAmount = getPositionTotalAmount( - p.balancesWithAmount + p.balancesWithAmount, + getBaseToken(yieldDto) ); const usdAmount = positionTotalAmount.amountUsd; 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 0470f59d..f58a28b5 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 @@ -50,9 +50,9 @@ export const usePositionListItem = ( const totalAmountPriceFormatted = useMemo( () => - totalAmountUsd.gt(0) - ? Maybe.of(defaultFormattedNumber(totalAmountUsd)) - : (Maybe.empty() as Maybe), + totalAmountUsd + .filter((v) => v.isGreaterThan(0)) + .map(defaultFormattedNumber), [totalAmountUsd] ); 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 85a0f079..44f23441 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,6 +160,7 @@ export const PositionsListItem = memo( {Maybe.fromRecord({ token: item.token, rewardRateAverage, + totalAmountFormatted, }) .map((val) => ( - {totalAmountFormatted} {val.token.symbol} + {val.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 f4e95899..3ea1c9cb 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,13 +63,21 @@ export const usePositionListItem = ( [integrationData] ); - const { amount: totalAmount, amountUsd: totalAmountUsd } = useMemo( - () => getPositionTotalAmount(item.balancesWithAmount), - [item.balancesWithAmount] + const amounts = useMemo( + () => + baseToken.map((b) => getPositionTotalAmount(item.balancesWithAmount, b)), + [item.balancesWithAmount, baseToken] + ); + + const totalAmount = useMemo(() => amounts.map((v) => v.amount), [amounts]); + + const totalAmountUsd = useMemo( + () => amounts.map((v) => v.amountUsd), + [amounts] ); const totalAmountFormatted = useMemo( - () => (totalAmount.gt(0) ? defaultFormattedNumber(totalAmount) : "-"), + () => totalAmount.map(defaultFormattedNumber), [totalAmount] ); From 3be73d98c6e370d24e9dbb05de37949fcf1c6ac6 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 5 Mar 2026 14:17:34 +0100 Subject: [PATCH 04/23] fix: misc --- .../components/positions-list-item.tsx | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) 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 e695915e..9b204254 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,20 +165,28 @@ export const PositionsListItem = memo( {/* Staked */} - {totalAmountFormatted} - - {totalAmountPriceFormatted + {totalAmountFormatted .map((v) => ( - - {v}$ - + <> + {v} + + {totalAmountPriceFormatted + .map((v) => ( + + {v}$ + + )) + .orDefaultLazy(() => ( + - + ))} + )) - .orDefaultLazy(() => ( + .orDefault( - - ))} + )} From f02120ec21a78984ce86d8be03d84df7d87cacaf Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Fri, 20 Mar 2026 15:23:47 +0100 Subject: [PATCH 05/23] feat: migrate --- .gitignore | 5 +- AGENTS.md | 23 + .../widget/src/common/delay-api-requests.ts | 4 +- packages/widget/src/common/private-api.ts | 23 - .../molecules/select-validator/index.tsx | 2 +- packages/widget/src/domain/types/stake.ts | 7 +- packages/widget/src/domain/types/yields.ts | 76 +-- .../src/hooks/api/use-activity-actions.ts | 58 +- .../widget/src/hooks/api/use-multi-yields.ts | 9 +- .../src/hooks/api/use-yield-balances-scan.ts | 19 + .../get-yield-opportunity.ts | 79 ++- .../hooks/api/use-yield-opportunity/index.ts | 21 +- .../src/hooks/api/use-yield-validators.ts | 104 ++++ packages/widget/src/hooks/use-geo-block.ts | 46 +- .../widget/src/hooks/use-handle-deep-links.ts | 2 +- packages/widget/src/hooks/use-init-params.ts | 13 +- .../widget/src/hooks/use-positions-data.ts | 7 +- .../widget/src/hooks/use-provider-details.ts | 51 +- packages/widget/src/hooks/use-rich-errors.ts | 35 +- .../utila-select-validator-section.tsx | 2 +- .../components/position-details-actions.tsx | 3 +- .../complete/pages/pending-complete.page.tsx | 4 +- .../complete/pages/stake-complete.page.tsx | 11 +- .../complete/pages/unstake-complete.page.tsx | 4 +- .../select-validator-section/index.tsx | 2 +- .../select-yield-section/staked-via.tsx | 3 +- .../earn-page/state/earn-page-context.tsx | 98 +++- .../state/earn-page-state-context.tsx | 4 - .../details/earn-page/state/use-init-token.ts | 5 +- .../details/earn-page/state/use-init-yield.ts | 5 +- .../state/use-pending-action-deep-link.ts | 11 +- .../state/use-stake-enter-request-dto.ts | 53 +- .../pages/details/earn-page/state/utils.ts | 13 +- .../positions-page/hooks/use-positions.ts | 7 - .../hooks/use-position-details.ts | 16 +- .../hooks/use-stake-exit-request-dto.ts | 47 +- .../hooks/use-unstake-machine.ts | 77 ++- .../src/pages/position-details/hooks/utils.ts | 17 +- .../position-details.page.tsx | 3 +- .../widget/src/pages/review/hooks/use-fees.ts | 84 ++- .../review/hooks/use-pending-review.hook.ts | 69 ++- .../review/hooks/use-stake-review.hook.ts | 113 ++-- .../review/hooks/use-unstake-review.hook.ts | 48 +- .../steps/hooks/use-steps-machine.hook.ts | 255 ++++----- .../src/providers/enter-stake-store/index.tsx | 6 +- .../src/providers/exit-stake-store/index.tsx | 10 +- .../providers/pending-action-store/index.tsx | 8 +- packages/widget/src/providers/wagmi/index.ts | 26 +- .../yield-api-client-provider/actions.ts | 173 ++++++ .../yield-api-client-provider/compat.ts | 519 ++++++++++++++++++ .../yield-api-client-provider/index.tsx | 73 ++- .../request-helpers.ts | 34 ++ .../yield-api-client-provider/types.ts | 11 + packages/widget/tests/fixtures/index.ts | 74 +++ ...ould-work-with-ton-external-provider-1.png | Bin 0 -> 2802 bytes .../tests/use-cases/deep-links-flow/setup.ts | 119 ++-- .../use-cases/external-provider/setup.ts | 25 +- ...gas-token-Txs-gas---gas-token-amount-1.png | Bin 0 -> 27548 bytes ...gas-token-Txs-gas---gas-token-amount-2.png | Bin 0 -> 29975 bytes ...gas-token-Txs-gas---gas-token-amount-3.png | Bin 0 -> 27461 bytes ...gas-token-Txs-gas---gas-token-amount-1.png | Bin 0 -> 29451 bytes ...gas-token-Txs-gas---gas-token-amount-2.png | Bin 0 -> 29594 bytes ...gas-token-Txs-gas---gas-token-amount-3.png | Bin 0 -> 29755 bytes .../tests/use-cases/gas-warning-flow/setup.ts | 90 ++- .../Staking-flow-Works-as-expected-1.png | Bin 0 -> 29246 bytes .../Staking-flow-Works-as-expected-2.png | Bin 0 -> 29246 bytes .../Staking-flow-Works-as-expected-3.png | Bin 0 -> 29246 bytes .../tests/use-cases/staking-flow/setup.ts | 92 ++-- 68 files changed, 2119 insertions(+), 679 deletions(-) create mode 100644 packages/widget/src/hooks/api/use-yield-validators.ts create mode 100644 packages/widget/src/providers/yield-api-client-provider/actions.ts create mode 100644 packages/widget/src/providers/yield-api-client-provider/compat.ts create mode 100644 packages/widget/src/providers/yield-api-client-provider/request-helpers.ts create mode 100644 packages/widget/tests/use-cases/__screenshots__/sk-wallet.test.tsx/SK-Wallet-should-work-with-ton-external-provider-1.png create mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-1.png create mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-2.png create mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-3.png create mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-1.png create mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-2.png create mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-3.png create mode 100644 packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-1.png create mode 100644 packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-2.png create mode 100644 packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-3.png diff --git a/.gitignore b/.gitignore index 69017cc3..70f97094 100644 --- a/.gitignore +++ b/.gitignore @@ -266,4 +266,7 @@ next-env.d.ts *.p12 *.pfx *.crt -*.cer \ No newline at end of file +*.cer + +# opensrc - source code for packages +opensrc/ diff --git a/AGENTS.md b/AGENTS.md index 371e1c6f..bcdce047 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,3 +44,26 @@ - Test bootstrapping + MSW worker setup: - `packages/widget/tests/utils/setup.ts` - `packages/widget/tests/mocks/worker.ts` + + + +## Source Code Reference + +Source code for dependencies is available in `opensrc/` for deeper understanding of implementation details. + +See `opensrc/sources.json` for the list of available packages and their versions. + +Use this source code when you need to understand how a package works internally, not just its types/interface. + +### Fetching Additional Source Code + +To fetch source code for a package or repository you need to understand, run: + +```bash +npx opensrc # npm package (e.g., npx opensrc zod) +npx opensrc pypi: # Python package (e.g., npx opensrc pypi:requests) +npx opensrc crates: # Rust crate (e.g., npx opensrc crates:serde) +npx opensrc / # GitHub repo (e.g., npx opensrc vercel/ai) +``` + + \ No newline at end of file diff --git a/packages/widget/src/common/delay-api-requests.ts b/packages/widget/src/common/delay-api-requests.ts index df2c5ece..9e543ada 100644 --- a/packages/widget/src/common/delay-api-requests.ts +++ b/packages/widget/src/common/delay-api-requests.ts @@ -26,9 +26,11 @@ const checkDelay = () => { }).then(() => unsub()); }; +export const waitForDelayedApiRequests = () => checkDelay(); + export const attachDelayInterceptor = (apiClient: AxiosInstance) => apiClient.interceptors.response.use(async (response) => { - await checkDelay(); + await waitForDelayedApiRequests(); return response; }); diff --git a/packages/widget/src/common/private-api.ts b/packages/widget/src/common/private-api.ts index 4a657f0d..7eea9757 100644 --- a/packages/widget/src/common/private-api.ts +++ b/packages/widget/src/common/private-api.ts @@ -2,7 +2,6 @@ import { customFetch, type TokenBalanceScanDto, type TokenBalanceScanResponseDto, - type ValidatorSearchResultDto, type YieldDto, } from "@stakekit/api-hooks"; @@ -40,25 +39,3 @@ export const yieldYieldOpportunity = ( signal, }); }; - -export type YieldFindValidatorsParams = { - ledgerWalletAPICompatible?: boolean; - network?: string; - query?: string; -}; - -/** - * Returns a list of available validators to specify when providing a `validatorAddress` property. - * @summary Get validators - */ -export const yieldFindValidators = ( - params?: YieldFindValidatorsParams, - signal?: AbortSignal -) => { - return customFetch({ - url: "/v1/yields/validators", - method: "GET", - params, - signal, - }); -}; diff --git a/packages/widget/src/components/molecules/select-validator/index.tsx b/packages/widget/src/components/molecules/select-validator/index.tsx index 47acd9bd..aa69bffe 100644 --- a/packages/widget/src/components/molecules/select-validator/index.tsx +++ b/packages/widget/src/components/molecules/select-validator/index.tsx @@ -12,7 +12,7 @@ type SelectValidatorProps = PropsWithChildren< selectedValidators: Set; onItemClick: (item: ValidatorDto) => void; onViewMoreClick?: () => void; - validators: YieldDto["validators"]; + validators: ValidatorDto[]; selectedStake: YieldDto; multiSelect: boolean; } & ( diff --git a/packages/widget/src/domain/types/stake.ts b/packages/widget/src/domain/types/stake.ts index ce1a343a..0f5a0fb7 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -1,6 +1,7 @@ import type { AmountArgumentOptionsDto, TokenBalanceScanResponseDto, + ValidatorDto, YieldDto, } from "@stakekit/api-hooks"; import { Networks } from "@stakekit/common"; @@ -126,7 +127,7 @@ const balanceValidForYield = ({ export const getInitSelectedValidators = (args: { initQueryParams: Maybe; - yieldDto: YieldDto; + validators: ValidatorDto[]; }) => args.initQueryParams .chainNullable((params) => params.validator) @@ -135,10 +136,10 @@ export const getInitSelectedValidators = (args: { (val) => val.name?.toLowerCase() === initV.toLowerCase() || val.address === initV, - args.yieldDto.validators + args.validators ) ) - .altLazy(() => List.head(args.yieldDto.validators)) + .altLazy(() => List.head(args.validators)) .map((v) => new Map([[v.address, v]])) .orDefault(new Map()); diff --git a/packages/widget/src/domain/types/yields.ts b/packages/widget/src/domain/types/yields.ts index e33c0169..1cd9b66a 100644 --- a/packages/widget/src/domain/types/yields.ts +++ b/packages/widget/src/domain/types/yields.ts @@ -1,4 +1,4 @@ -import type { YieldDto, YieldType } from "@stakekit/api-hooks"; +import type { ValidatorDto, YieldDto, YieldType } from "@stakekit/api-hooks"; import { EvmNetworks } from "@stakekit/common"; import BigNumber from "bignumber.js"; import type { TFunction } from "i18next"; @@ -27,45 +27,55 @@ export type ValidatorsConfig = Map< } >; -export const filterMapValidators = ( - validatorsConfig: ValidatorsConfig, - yieldDto: YieldDto -): YieldDto => { +export const filterValidators = ({ + validatorsConfig, + validators, + network, + yieldId, +}: { + validatorsConfig: ValidatorsConfig; + validators: ValidatorDto[]; + network: YieldDto["token"]["network"]; + yieldId?: YieldDto["id"]; +}) => { const valConfig = Maybe.fromNullable( - validatorsConfig.get(yieldDto.token.network as SupportedSKChains) + validatorsConfig.get(network as SupportedSKChains) ) .altLazy(() => Maybe.fromNullable(validatorsConfig.get("*"))) .extractNullable(); - if (!valConfig) { - return yieldDto; + const filtered = !valConfig + ? validators + : (() => { + const { + allowed, + blocked, + preferred, + mergePreferredWithDefault, + preferredOnly, + } = valConfig; + + return validators.flatMap((v) => { + if (allowed && !allowed.has(v.address)) return []; + if (blocked?.has(v.address)) return []; + + const isPreferred = + preferred?.has(v.address) || + !!(mergePreferredWithDefault && v.preferred); + + if (preferredOnly) { + return isPreferred ? [{ ...v, preferred: true }] : []; + } + + return [{ ...v, preferred: isPreferred }]; + }); + })(); + + if (yieldId && isBittensorStaking(yieldId)) { + return filtered.filter((validator) => validator.name?.match(/yuma/i)); } - const { - allowed, - blocked, - preferred, - mergePreferredWithDefault, - preferredOnly, - } = valConfig; - - return { - ...yieldDto, - validators: yieldDto.validators.flatMap((v) => { - if (allowed && !allowed.has(v.address)) return []; - if (blocked?.has(v.address)) return []; - - const isPreferred = - preferred?.has(v.address) || - !!(mergePreferredWithDefault && v.preferred); - - if (preferredOnly) { - return isPreferred ? [{ ...v, preferred: true }] : []; - } - - return [{ ...v, preferred: isPreferred }]; - }), - }; + return filtered; }; export const getExtendedYieldType = (yieldDto: YieldDto) => diff --git a/packages/widget/src/hooks/api/use-activity-actions.ts b/packages/widget/src/hooks/api/use-activity-actions.ts index b577c9a2..7aa74c46 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -1,58 +1,57 @@ -import { - type ActionDto, - type ActionList200, - ActionStatus, - actionList, - getActionListQueryKey, -} from "@stakekit/api-hooks"; +import { type ActionDto, ActionStatus } from "@stakekit/api-hooks"; import { useInfiniteQuery } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; import { useMemo } from "react"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; -import { useValidatorsConfig } from "../use-validators-config"; +import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; +import { listActions } from "../../providers/yield-api-client-provider/actions"; +import { adaptActionDto } from "../../providers/yield-api-client-provider/compat"; import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportunity"; +const PAGE_SIZE = 20; + export const useActivityActions = () => { - const { address, network, isLedgerLive } = useSKWallet(); + const { address, isLedgerLive } = useSKWallet(); const queryClient = useSKQueryClient(); - - const validatorsConfig = useValidatorsConfig(); + const yieldApiFetchClient = useYieldApiFetchClient(); const query = useInfiniteQuery({ - enabled: !!address && !!network, - queryKey: getActionListQueryKey({ - network: network!, - walletAddress: address!, - }), - queryFn: async ({ pageParam = 1 }) => { + enabled: !!address, + queryKey: ["activity-actions", address], + queryFn: async ({ pageParam = 0 }) => { return ( await EitherAsync(() => - actionList({ - page: pageParam, - walletAddress: address!, - network: network!, - sort: "createdAtDesc", + listActions({ + address: address!, + fetchClient: yieldApiFetchClient, + limit: PAGE_SIZE, + offset: pageParam, }) ) .mapLeft(() => new Error("Could not get action list")) .map((actionList) => ({ ...actionList, - data: actionList.data.filter( + data: (actionList.items ?? []).filter( (x) => x.status !== ActionStatus.CREATED ), })) .chain(async (actionList) => EitherAsync.all( - (actionList.data as ActionList200["data"]).map((action) => + actionList.data.map((action) => getYieldOpportunity({ - yieldId: action.integrationId, + yieldId: action.yieldId, queryClient, isLedgerLive, - validatorsConfig, + yieldApiFetchClient, }) .map((yieldData) => ({ - actionData: action as typeof action & ActionDto, + actionData: adaptActionDto({ + actionDto: action, + addresses: { address: action.address }, + gasFeeToken: yieldData.metadata.gasFeeToken, + yieldDto: yieldData, + }) as ActionDto, yieldData, })) .chainLeft(() => EitherAsync(() => Promise.resolve(null))) @@ -64,9 +63,10 @@ export const useActivityActions = () => { ) ).unsafeCoerce(); }, - initialPageParam: 1, + initialPageParam: 0, getNextPageParam: (lastPage) => { - return lastPage.hasNextPage ? lastPage.page + 1 : undefined; + const nextOffset = (lastPage.offset ?? 0) + (lastPage.limit ?? PAGE_SIZE); + return nextOffset < (lastPage.total ?? 0) ? nextOffset : undefined; }, }); diff --git a/packages/widget/src/hooks/api/use-multi-yields.ts b/packages/widget/src/hooks/api/use-multi-yields.ts index b2b263bb..dd99de4b 100644 --- a/packages/widget/src/hooks/api/use-multi-yields.ts +++ b/packages/widget/src/hooks/api/use-multi-yields.ts @@ -37,6 +37,7 @@ import { } from "../../domain/types/yields"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; +import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; import { useSavedRef } from "../use-saved-ref"; import { useValidatorsConfig } from "../use-validators-config"; import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportunity"; @@ -61,10 +62,12 @@ const multiYieldsStore = createStore({ export const useStreamMultiYields = (yieldIds: string[]) => { const { network, isConnected, isLedgerLive } = useSKWallet(); + const yieldApiFetchClient = useYieldApiFetchClient(); const argsRef = useSavedRef({ isLedgerLive, queryClient: useSKQueryClient(), + yieldApiFetchClient, network, isConnected, }); @@ -106,12 +109,14 @@ export const useMultiYields = ( } ) => { const { network, isConnected, isLedgerLive } = useSKWallet(); + const yieldApiFetchClient = useYieldApiFetchClient(); const validatorsConfig = useValidatorsConfig(); const argsRef = useSavedRef({ isLedgerLive, queryClient: useSKQueryClient(), + yieldApiFetchClient, network, isConnected, }); @@ -147,6 +152,7 @@ export const getFirstEligibleYield = ( const multipleYields$ = (args: { isLedgerLive: boolean; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; isConnected: boolean; network: SKWallet["network"]; yieldIds: string[]; @@ -159,7 +165,7 @@ const multipleYields$ = (args: { isLedgerLive: args.isLedgerLive, yieldId: v, queryClient: args.queryClient, - validatorsConfig: args.validatorsConfig, + yieldApiFetchClient: args.yieldApiFetchClient, }) ) ) @@ -182,6 +188,7 @@ const multipleYields$ = (args: { const firstEligibleYield$ = (args: { isLedgerLive: boolean; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; isConnected: boolean; network: SKWallet["network"]; yieldIds: string[]; 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 99ad6117..a9063875 100644 --- a/packages/widget/src/hooks/api/use-yield-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-yield-balances-scan.ts @@ -6,6 +6,7 @@ import { useSKWallet } from "../../providers/sk-wallet"; import { useActionHistoryData } from "../../providers/stake-history"; import { useYieldApiClient } from "../../providers/yield-api-client-provider"; import type { + YieldBalanceDto, YieldBalancesByYieldDto, YieldBalancesRequestDto, } from "../../providers/yield-api-client-provider/types"; @@ -103,3 +104,21 @@ export const useInvalidateYieldBalances = () => { const getYieldYieldBalancesScanQueryKey = () => ["post", "/v1/yields/balances"] as const; + +const normalizeYieldBalanceForPosition = ( + balance: YieldBalanceDto +): YieldBalanceDto => ({ + ...balance, + amount: balance.shareAmount ?? balance.amount, +}); + +export const normalizeYieldBalancesForPosition = ( + balances: YieldBalancesByYieldDto[] +): YieldBalancesByYieldDto[] => + balances.map((balanceByYield) => ({ + ...balanceByYield, + balances: balanceByYield.balances.map(normalizeYieldBalanceForPosition), + outputTokenBalance: balanceByYield.outputTokenBalance + ? normalizeYieldBalanceForPosition(balanceByYield.outputTokenBalance) + : balanceByYield.outputTokenBalance, + })); diff --git a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts index 0c248f47..425c7d59 100644 --- a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts +++ b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts @@ -1,19 +1,17 @@ import type { YieldDto } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; +import type { Client } from "openapi-fetch"; import { EitherAsync } from "purify-ts"; import { yieldYieldOpportunity } from "../../../common/private-api"; -import { - filterMapValidators, - getComputedRewardRate, - isBittensorStaking, - isEthenaUsdeStaking, - type ValidatorsConfig, -} from "../../../domain/types/yields"; +import { isEthenaUsdeStaking } from "../../../domain/types/yields"; +import { adaptYieldDto } from "../../../providers/yield-api-client-provider/compat"; +import { getResponseData } from "../../../providers/yield-api-client-provider/request-helpers"; +import type { paths } from "../../../types/yield-api-schema"; type Params = { yieldId: string; isLedgerLive: boolean; - validatorsConfig: ValidatorsConfig; + yieldApiFetchClient: Client; signal?: AbortSignal; }; @@ -50,38 +48,65 @@ const fn = ({ isLedgerLive, yieldId, signal, - validatorsConfig, + yieldApiFetchClient, }: Params & { signal?: AbortSignal; -}) => - EitherAsync(() => - yieldYieldOpportunity( - yieldId, - { - ledgerWalletAPICompatible: isLedgerLive, - }, - signal - ) - ) - .map((y) => filterMapValidators(validatorsConfig, y)) +}) => { + const stripValidators = (yieldDto: YieldDto): YieldDto => ({ + ...yieldDto, + validators: [], + }); + + return EitherAsync(async () => { + const [newYieldResult, legacyYieldResult] = await Promise.allSettled([ + getResponseData( + yieldApiFetchClient.GET("/v1/yields/{yieldId}", { + params: { + path: { + yieldId, + }, + }, + signal, + }) + ), + yieldYieldOpportunity( + yieldId, + { ledgerWalletAPICompatible: isLedgerLive }, + signal + ), + ]); + + if (newYieldResult.status === "rejected") { + if (legacyYieldResult.status === "fulfilled") { + return stripValidators(legacyYieldResult.value); + } + + throw newYieldResult.reason; + } + + const merged = adaptYieldDto({ + yieldDto: newYieldResult.value, + legacyYieldDto: + legacyYieldResult.status === "fulfilled" + ? legacyYieldResult.value + : null, + }); + + return stripValidators(merged); + }) .map((y) => isEthenaUsdeStaking(y.id) ? ({ ...y, - rewardRate: getComputedRewardRate(y), metadata: { ...y.metadata, name: y.metadata.name.replace(/staking/i, ""), }, } satisfies YieldDto) - : isBittensorStaking(y.id) - ? { - ...y, - validators: y.validators.filter((v) => v.name?.match(/yuma/i)), - } - : y + : y ) .mapLeft((e) => { console.log(e); return new Error("Could not get yield opportunity"); }); +}; diff --git a/packages/widget/src/hooks/api/use-yield-opportunity/index.ts b/packages/widget/src/hooks/api/use-yield-opportunity/index.ts index b8a7ba38..4160f0c2 100644 --- a/packages/widget/src/hooks/api/use-yield-opportunity/index.ts +++ b/packages/widget/src/hooks/api/use-yield-opportunity/index.ts @@ -1,13 +1,12 @@ import { useQuery } from "@tanstack/react-query"; -import type { ValidatorsConfig } from "../../../domain/types/yields"; import { useSKWallet } from "../../../providers/sk-wallet"; -import { useValidatorsConfig } from "../../use-validators-config"; +import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; import { queryFn } from "./get-yield-opportunity"; type Params = { yieldId: string; isLedgerLive: boolean; - validatorsConfig: ValidatorsConfig; + yieldApiFetchClient: ReturnType; signal?: AbortSignal; }; @@ -20,16 +19,24 @@ const getKey = (params: Params) => [ export const useYieldOpportunity = (integrationId: string | undefined) => { const { isLedgerLive } = useSKWallet(); - - const validatorsConfig = useValidatorsConfig(); + const yieldApiFetchClient = useYieldApiFetchClient(); const yieldId = integrationId ?? ""; return useQuery({ - queryKey: getKey({ yieldId, isLedgerLive, validatorsConfig }), + queryKey: getKey({ + yieldId, + isLedgerLive, + yieldApiFetchClient, + }), enabled: !!integrationId, staleTime, queryFn: ({ signal }) => - queryFn({ yieldId, isLedgerLive, signal, validatorsConfig }), + queryFn({ + yieldId, + isLedgerLive, + signal, + yieldApiFetchClient, + }), }); }; diff --git a/packages/widget/src/hooks/api/use-yield-validators.ts b/packages/widget/src/hooks/api/use-yield-validators.ts new file mode 100644 index 00000000..7a19a8af --- /dev/null +++ b/packages/widget/src/hooks/api/use-yield-validators.ts @@ -0,0 +1,104 @@ +import type { ValidatorDto, YieldDto } from "@stakekit/api-hooks"; +import { useQuery } from "@tanstack/react-query"; +import type { ValidatorsConfig } from "../../domain/types/yields"; +import { filterValidators } from "../../domain/types/yields"; +import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; +import { adaptValidatorDto } from "../../providers/yield-api-client-provider/compat"; +import { getResponseData } from "../../providers/yield-api-client-provider/request-helpers"; +import { useValidatorsConfig } from "../use-validators-config"; + +const PAGE_SIZE = 100; +const staleTime = 1000 * 60 * 2; + +type Params = { + yieldId: string; + network?: YieldDto["token"]["network"]; + validatorsConfig: ValidatorsConfig; + yieldApiFetchClient: ReturnType; + signal?: AbortSignal; +}; + +const getYieldValidatorsQueryKey = ({ yieldId }: Pick) => [ + "yield-validators", + yieldId, +]; + +export const getYieldValidatorsQueryFn = async ({ + yieldId, + network, + validatorsConfig, + yieldApiFetchClient, + signal, +}: Params): Promise => { + const fetchPage = (offset: number) => + getResponseData( + yieldApiFetchClient.GET("/v1/yields/{yieldId}/validators", { + params: { + path: { + yieldId, + }, + query: { + offset, + limit: PAGE_SIZE, + }, + }, + signal, + }) + ); + + const firstPage = await fetchPage(0); + + const remainingOffsets = Array.from( + { length: Math.ceil(firstPage.total / PAGE_SIZE) - 1 }, + (_, index) => (index + 1) * PAGE_SIZE + ); + + const remainingPages = await Promise.all( + remainingOffsets.map((offset) => + fetchPage(offset).catch(() => ({ items: [] })) + ) + ); + + const validators = [firstPage, ...remainingPages] + .flatMap((page) => page.items ?? []) + .map(adaptValidatorDto); + + return network + ? filterValidators({ + validatorsConfig, + validators, + network, + yieldId, + }) + : validators; +}; + +const getYieldValidatorsQueryOptions = (params: Params) => ({ + queryKey: getYieldValidatorsQueryKey(params), + staleTime, + queryFn: ({ signal }: { signal?: AbortSignal }) => + getYieldValidatorsQueryFn({ ...params, signal }), +}); + +export const useYieldValidators = ({ + enabled = true, + yieldId, + network, +}: { + enabled?: boolean; + yieldId?: string; + network?: YieldDto["token"]["network"]; +}) => { + const yieldApiFetchClient = useYieldApiFetchClient(); + const validatorsConfig = useValidatorsConfig(); + + return useQuery({ + ...getYieldValidatorsQueryOptions({ + yieldId: yieldId ?? "", + network, + validatorsConfig, + yieldApiFetchClient, + }), + enabled: enabled && !!yieldId, + }); +}; diff --git a/packages/widget/src/hooks/use-geo-block.ts b/packages/widget/src/hooks/use-geo-block.ts index 6b55e5df..f18ceb6a 100644 --- a/packages/widget/src/hooks/use-geo-block.ts +++ b/packages/widget/src/hooks/use-geo-block.ts @@ -19,23 +19,39 @@ const subscribe = (callback: (val: typeof _isGeoBlocked) => void) => { return () => subs.delete(callback); }; +const isGeoLocationError = (data: unknown): data is GeolocationError => + typeof data === "object" && + data !== null && + "type" in data && + data.type === GeolocationErrorType.GEO_LOCATION; + +export const handleGeoBlockResponse = ({ + data, + status, +}: { + data: unknown; + status?: number; +}) => { + if (status !== 403 || !isGeoLocationError(data)) { + return; + } + + const regionCode = (data.regionCode as unknown as string) ?? ""; // wrong type in API + + _isGeoBlocked = { + tags: new Set(data.tags ?? []), + countryCode: data.countryCode ?? "", + regionCode, + }; + notify(); +}; + export const attachGeoBlockInterceptor = (apiClient: AxiosInstance) => apiClient.interceptors.response.use(undefined, (error) => { - if ( - error?.response?.status === 403 && - error.response.data?.type === GeolocationErrorType.GEO_LOCATION - ) { - const geoLocationErr = error.response.data as GeolocationError; - - const regionCode = (geoLocationErr.regionCode as unknown as string) ?? ""; // wrong type in API - - _isGeoBlocked = { - tags: new Set(geoLocationErr.tags ?? []), - countryCode: geoLocationErr.countryCode ?? "", - regionCode, - }; - notify(); - } + handleGeoBlockResponse({ + data: error?.response?.data, + status: error?.response?.status, + }); return Promise.reject(error); }); diff --git a/packages/widget/src/hooks/use-handle-deep-links.ts b/packages/widget/src/hooks/use-handle-deep-links.ts index 4ab9dfc2..82b1ce27 100644 --- a/packages/widget/src/hooks/use-handle-deep-links.ts +++ b/packages/widget/src/hooks/use-handle-deep-links.ts @@ -63,7 +63,7 @@ export const useHandleDeepLinks = () => { gasFeeToken: val.pendingActionDto.gasFeeToken, integrationData: val.pendingActionDto.integrationData, interactedToken: val.balance.token, - pendingActionType: val.pendingActionDto.requestDto.type, + pendingActionType: val.pendingActionDto.requestDto.action, }, }); navigateRef.current( diff --git a/packages/widget/src/hooks/use-init-params.ts b/packages/widget/src/hooks/use-init-params.ts index 449a02cb..db7f1147 100644 --- a/packages/widget/src/hooks/use-init-params.ts +++ b/packages/widget/src/hooks/use-init-params.ts @@ -3,14 +3,13 @@ import { useQuery } from "@tanstack/react-query"; import { EitherAsync, Right } from "purify-ts"; import type { SupportedSKChains } from "../domain/types/chains"; import type { InitParams } from "../domain/types/init-params"; -import type { ValidatorsConfig } from "../domain/types/yields"; import { useSKQueryClient } from "../providers/query-client"; import { useSettings } from "../providers/settings"; import type { SettingsContextType } from "../providers/settings/types"; import { useSKWallet } from "../providers/sk-wallet"; +import { useYieldApiFetchClient } from "../providers/yield-api-client-provider"; import { getYieldOpportunity } from "./api/use-yield-opportunity/get-yield-opportunity"; import { getAndValidateInitParams } from "./use-init-query-params"; -import { useValidatorsConfig } from "./use-validators-config"; const queryKey = ["init-params"]; const staleTime = 0; @@ -22,7 +21,7 @@ export const useInitParams = (opts?: { const { isLedgerLive } = useSKWallet(); const { externalProviders } = useSettings(); const queryClient = useSKQueryClient(); - const validatorsConfig = useValidatorsConfig(); + const yieldApiFetchClient = useYieldApiFetchClient(); return useQuery({ queryKey, @@ -32,8 +31,8 @@ export const useInitParams = (opts?: { queryFn({ isLedgerLive, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }), select: opts?.select, }); @@ -60,13 +59,13 @@ const queryFn = async (params: Parameters[0]) => const fn = ({ isLedgerLive, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }: { isLedgerLive: boolean; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; externalProviders: SettingsContextType["externalProviders"]; - validatorsConfig: ValidatorsConfig; }): EitherAsync => EitherAsync.liftEither( getAndValidateInitParams({ @@ -80,7 +79,7 @@ const fn = ({ isLedgerLive, yieldId: yId, queryClient, - validatorsConfig, + yieldApiFetchClient, }) .map((yieldData) => ({ ...val, diff --git a/packages/widget/src/hooks/use-positions-data.ts b/packages/widget/src/hooks/use-positions-data.ts index 7ced4b88..0cc75198 100644 --- a/packages/widget/src/hooks/use-positions-data.ts +++ b/packages/widget/src/hooks/use-positions-data.ts @@ -9,7 +9,10 @@ import type { YieldBalanceDto, YieldBalancesByYieldDto, } from "../providers/yield-api-client-provider/types"; -import { useYieldBalancesScan } from "./api/use-yield-balances-scan"; +import { + normalizeYieldBalancesForPosition, + useYieldBalancesScan, +} from "./api/use-yield-balances-scan"; export const usePositionsData = () => { const { data, ...rest } = useYieldBalancesScan({ @@ -27,7 +30,7 @@ export const usePositionsData = () => { const positionsDataSelector = createSelector( (balancesData: YieldBalancesByYieldDto[]) => balancesData, (balancesData) => - balancesData.reduce((acc, val) => { + normalizeYieldBalancesForPosition(balancesData).reduce((acc, val) => { acc.set(val.yieldId, { yieldId: val.yieldId, balanceData: [...val.balances] diff --git a/packages/widget/src/hooks/use-provider-details.ts b/packages/widget/src/hooks/use-provider-details.ts index 9975dee5..e0150a09 100644 --- a/packages/widget/src/hooks/use-provider-details.ts +++ b/packages/widget/src/hooks/use-provider-details.ts @@ -8,6 +8,7 @@ import { import type { GetMaybeJust } from "../types/utils"; import { getRewardRateFormatted } from "../utils/formatters"; import { useMultiYields } from "./api/use-multi-yields"; +import { useYieldValidators } from "./api/use-yield-validators"; type Res = Maybe<{ logo: string | undefined; @@ -29,11 +30,13 @@ export const getProviderDetails = ({ validatorAddress, yields, selectedProviderYieldId, + validatorsData, }: { integrationData: Maybe; validatorAddress: Maybe; yields: Maybe; selectedProviderYieldId: Maybe; + validatorsData?: ValidatorDto[]; }): Res => { const def = integrationData.chain((val) => { const rewardRate = val.rewardRate; @@ -70,7 +73,7 @@ export const getProviderDetails = ({ .chain>((addr) => List.find( (v) => v.address === addr || v.providerId === addr, - yieldDto.validators + validatorsData ?? [] ).map((validator) => { const { rewardRate, rewardType } = Maybe.fromRecord({ _: Maybe.fromFalsy(isYieldWithProviderOptions(yieldDto)), @@ -115,15 +118,51 @@ export const useProvidersDetails = ({ integrationData, validatorsAddresses, selectedProviderYieldId, + validatorsData, }: { integrationData: Maybe; validatorsAddresses: Maybe>; selectedProviderYieldId: Maybe; + validatorsData?: Maybe; }) => { const yields = useMultiYields( integrationData.map(getYieldProviderYieldIds).orDefault([]) ); + const shouldFetchValidators = validatorsAddresses + .filter((val): val is string[] => !(val instanceof Map)) + .map((val) => val.length > 0) + .chain((val) => + validatorsData?.isJust() ? Maybe.of(false) : Maybe.of(val) + ) + .orDefault(false); + + const yieldValidators = useYieldValidators({ + enabled: shouldFetchValidators, + yieldId: + integrationData.map((val) => val.id).extractNullable() ?? undefined, + network: + integrationData.map((val) => val.token.network).extractNullable() ?? + undefined, + }); + + const resolvedValidatorsData = useMemo( + () => + validatorsData?.altLazy(() => + validatorsAddresses.chain((val) => + val instanceof Map + ? Maybe.of([...val.values()]) + : Maybe.fromNullable(yieldValidators.data) + ) + ) ?? + validatorsAddresses.chain((val) => + val instanceof Map + ? Maybe.of([...val.values()]) + : Maybe.fromNullable(yieldValidators.data) + ), + [validatorsAddresses, validatorsData, yieldValidators.data] + ); + return useMemo>[]>>( () => validatorsAddresses.chain((val) => @@ -137,6 +176,8 @@ export const useProvidersDetails = ({ validatorAddress: Maybe.of(v), yields: Maybe.fromNullable(yields.data), selectedProviderYieldId, + validatorsData: + resolvedValidatorsData.extractNullable() ?? undefined, }) ) ).chain((val) => @@ -150,6 +191,12 @@ export const useProvidersDetails = ({ }).map((v) => [v]) ) ), - [integrationData, validatorsAddresses, yields.data, selectedProviderYieldId] + [ + integrationData, + validatorsAddresses, + yields.data, + selectedProviderYieldId, + resolvedValidatorsData, + ] ); }; diff --git a/packages/widget/src/hooks/use-rich-errors.ts b/packages/widget/src/hooks/use-rich-errors.ts index fe75657e..71d8076e 100644 --- a/packages/widget/src/hooks/use-rich-errors.ts +++ b/packages/widget/src/hooks/use-rich-errors.ts @@ -10,17 +10,40 @@ interface RichError { const $richError = new BehaviorSubject(null); +const isRichError = (error: unknown): error is RichError => + typeof error === "object" && + error !== null && + "message" in error && + typeof error.message === "string"; + +export const handleRichErrorResponse = ({ + data, + i18n, + url, +}: { + data: unknown; + i18n: i18n; + url?: string; +}) => { + if (!isRichError(data)) { + return; + } + + if (i18n.exists(`errors.${data.message}`) && !url?.includes("gas-estimate")) { + $richError.next(data); + } +}; + export const attachRichErrorsInterceptor = ( apiClient: AxiosInstance, i18n: i18n ) => apiClient.interceptors.response.use(undefined, (error) => { - if ( - i18n.exists(`errors.${error?.response?.data?.message}`) && - !error?.config?.url.includes("gas-estimate") // temp ignore gas estimate errors - ) { - $richError.next(error.response.data); - } + handleRichErrorResponse({ + data: error?.response?.data, + i18n, + url: error?.config?.url, + }); return Promise.reject(error); }); diff --git a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx index 5c2e2981..4e34baae 100644 --- a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx +++ b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx @@ -25,7 +25,7 @@ export const UtilaSelectValidatorSection = () => { ) : ( Maybe.fromRecord({ selectedStake, validatorsData }) - .filter((val) => !!val.selectedStake.validators.length) + .filter((val) => !!val.validatorsData.length) .map((val) => { const selectedValidatorsArr = [...selectedValidators.values()]; diff --git a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx index 70e67d52..31472e3c 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx @@ -34,6 +34,7 @@ export const PositionDetailsActions = () => { const { isLoading, integrationData, + validatorsData, positionBalancesByType, unstakeToken, providersDetails, @@ -173,7 +174,7 @@ export const PositionDetailsActions = () => { onValidatorsSubmit([val.address]); }} selectedStake={v.integrationData} - validators={v.integrationData.validators} + validators={validatorsData} multiSelect={validatorAddressesHandling.multiSelect} state={validatorAddressesHandling.modalState} > diff --git a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx index b47d1c11..fa1c17b2 100644 --- a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx @@ -48,10 +48,10 @@ export const PendingCompletePage = () => { const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( () => - Maybe.fromNullable(pendingRequest.requestDto.args?.amount) + Maybe.fromNullable(pendingRequest.requestDto.arguments?.amount) .map((val) => new BigNumber(val ?? 0)) .mapOrDefault((v) => formatNumber(v), ""), - [pendingRequest.requestDto.args?.amount] + [pendingRequest.requestDto.arguments?.amount] ); const yieldType = useYieldType(integrationData).map((v) => v.type); diff --git a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx index 04c70429..efd8003c 100644 --- a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx @@ -32,15 +32,18 @@ export const StakeCompletePage = () => { const network = selectedToken.mapOrDefault((y) => y.symbol, ""); const amount = useMemo( - () => formatNumber(new BigNumber(enterRequest.requestDto.args.amount)), - [enterRequest.requestDto.args.amount] + () => + formatNumber( + new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0) + ), + [enterRequest.requestDto.arguments?.amount] ); const yieldType = useYieldType(selectedStake).map((v) => v.type); const selectedProviderYieldId = useMemo( - () => Maybe.fromNullable(enterRequest.requestDto.args.providerId), - [enterRequest.requestDto.args.providerId] + () => Maybe.fromNullable(enterRequest.requestDto.arguments?.providerId), + [enterRequest.requestDto.arguments?.providerId] ); const providerDetails = useProvidersDetails({ diff --git a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx index b6f5b2cd..79b2a927 100644 --- a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx @@ -45,8 +45,8 @@ export const UnstakeCompletePage = () => { const metadata = integrationData.map((d) => d.metadata); const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( - () => formatNumber(exitRequest.requestDto.args.amount), - [exitRequest.requestDto.args.amount] + () => formatNumber(exitRequest.requestDto.arguments?.amount ?? 0), + [exitRequest.requestDto.arguments?.amount] ); const yieldType = useYieldType(integrationData).map((v) => v.type); diff --git a/packages/widget/src/pages/details/earn-page/components/select-validator-section/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-validator-section/index.tsx index def33fb8..0fde334a 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-validator-section/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-validator-section/index.tsx @@ -27,7 +27,7 @@ export const SelectValidatorSection = () => { ) : ( Maybe.fromRecord({ selectedStake, validatorsData }) - .filter((val) => !!val.selectedStake.validators.length) + .filter((val) => !!val.validatorsData.length) .map((val) => { const selectedValidatorsArr = [...selectedValidators.values()]; diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx index da8af183..ad9c5c51 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx @@ -15,7 +15,8 @@ export const StakedVia = () => { (val) => !!( val.metadata.type === "staking" && - !val.validators.length && + !val.args.enter.args?.validatorAddress?.required && + !val.args.enter.args?.validatorAddresses?.required && val.metadata.provider ) ) diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx index b08348e3..21860e2a 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx @@ -16,6 +16,7 @@ import { useDeferredValue, useEffect, useMemo, + useRef, useState, } from "react"; import { useTranslation } from "react-i18next"; @@ -26,6 +27,7 @@ import { stakeTokenSameAsGasToken, tokenString, } from "../../../../domain"; +import { getInitSelectedValidators } from "../../../../domain/types/stake"; import { type ExtendedYieldType, getExtendedYieldType, @@ -39,11 +41,13 @@ import { useStreamMultiYields } from "../../../../hooks/api/use-multi-yields"; import { useTokenBalancesScan } from "../../../../hooks/api/use-token-balances-scan"; import { useTokensPrices } from "../../../../hooks/api/use-tokens-prices"; import { useYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity"; +import { useYieldValidators } from "../../../../hooks/api/use-yield-validators"; import { useNavigateWithScrollToTop } from "../../../../hooks/navigation/use-navigate-with-scroll-to-top"; import { useTrackEvent } from "../../../../hooks/tracking/use-track-event"; import { useAddLedgerAccount } from "../../../../hooks/use-add-ledger-account"; import { useBaseToken } from "../../../../hooks/use-base-token"; import { useEstimatedRewards } from "../../../../hooks/use-estimated-rewards"; +import { useInitParams } from "../../../../hooks/use-init-params"; import { useMaxMinYieldAmount } from "../../../../hooks/use-max-min-yield-amount"; import { usePositionsData } from "../../../../hooks/use-positions-data"; import { useProvidersDetails } from "../../../../hooks/use-provider-details"; @@ -92,6 +96,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const dispatch = useEarnPageDispatch(); const { t } = useTranslation(); + const initParams = useInitParams(); const baseToken = useBaseToken(selectedStake); @@ -314,19 +319,89 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { [deferredStakeSearch, multiYields, t] ); + const yieldValidators = useYieldValidators({ + enabled: selectedStake.isJust(), + yieldId: selectedStake.extract()?.id, + network: selectedStake.extract()?.token.network, + }); + + const initialValidatorSelectionYieldIdRef = useRef(null); + + useEffect(() => { + const currentYieldId = selectedStake.map((val) => val.id).extractNullable(); + + if (!currentYieldId) { + initialValidatorSelectionYieldIdRef.current = null; + return; + } + + if (selectedValidators.size > 0) { + initialValidatorSelectionYieldIdRef.current = currentYieldId; + return; + } + + if (initialValidatorSelectionYieldIdRef.current === currentYieldId) { + return; + } + + if (!yieldValidators.isFetched && !yieldValidators.isError) { + return; + } + + initialValidatorSelectionYieldIdRef.current = currentYieldId; + + const shouldSelectDefaultValidator = selectedStake + .map( + (stake) => + !!( + stake.metadata.isIntegrationAggregator || + stake.args.enter.args?.validatorAddress?.required || + stake.args.enter.args?.validatorAddresses?.required + ) + ) + .orDefault(false); + + if (!shouldSelectDefaultValidator) { + return; + } + + const nextValidator = List.head([ + ...getInitSelectedValidators({ + initQueryParams: Maybe.fromNullable(initParams.data), + validators: yieldValidators.data ?? [], + }).values(), + ]).extractNullable(); + + if (nextValidator) { + dispatch({ type: "validator/select", data: nextValidator }); + } + }, [ + dispatch, + initParams.data, + selectedStake, + selectedValidators, + yieldValidators.data, + yieldValidators.isError, + yieldValidators.isFetched, + ]); + const validatorsData = useMemo( () => - selectedStake.chain((ss) => - Maybe.fromNullable(deferredValidatorSearch) - .map((val) => val.toLowerCase()) - .map((searchInput) => - ss.validators.filter( + selectedStake.chain(() => + Maybe.fromNullable(yieldValidators.data) + .map((validators) => { + const searchInput = deferredValidatorSearch.toLowerCase(); + + if (!searchInput) { + return validators; + } + + return validators.filter( (validator) => validator.name?.toLowerCase().includes(searchInput) || validator.address.toLowerCase().includes(searchInput) - ) - ) - .alt(Maybe.of(ss.validators)) + ); + }) .map((validators) => { if (variant === "utila" || variant === "porto") { return [...validators].sort( @@ -337,7 +412,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { return validators; }) ), - [deferredValidatorSearch, selectedStake, variant] + [deferredValidatorSearch, selectedStake, variant, yieldValidators.data] ); const onYieldSearch: SelectModalProps["onSearch"] = (val) => @@ -395,6 +470,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { enterStakeStore.send({ type: "initFlow", data: { + addresses: val.stakeEnterRequestDto.addresses, requestDto: val.stakeEnterRequestDto.dto, selectedToken: val.selectedToken, gasFeeToken: val.stakeEnterRequestDto.gasFeeToken, @@ -583,7 +659,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { defaultTokensIsLoading || tokenBalancesScanLoading || initYieldRes.isLoading || - yieldOpportunityLoading; + yieldOpportunityLoading || + yieldValidators.isLoading || + yieldValidators.isFetching; const footerIsLoading = defaultTokensIsLoading || diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx index 1b12b3a8..371a6c7c 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx @@ -14,7 +14,6 @@ import { import { equalTokens } from "../../../../domain"; import { isNetworkWithEnterMinBasedOnPosition } from "../../../../domain/types/stake"; import { useYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity"; -import { useInitParams } from "../../../../hooks/use-init-params"; import { useMaxMinYieldAmount } from "../../../../hooks/use-max-min-yield-amount"; import { usePositionsData } from "../../../../hooks/use-positions-data"; import { useSavedRef } from "../../../../hooks/use-saved-ref"; @@ -56,7 +55,6 @@ const getInitialState = (): State => ({ }); export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { - const initParams = useInitParams(); const { network, isConnected } = useSKWallet(); const getInitYield = useGetInitYield(); @@ -75,7 +73,6 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { getInitYield({ selectedToken: action.data }) .map | null>((val) => onYieldSelectState({ - initParams: Maybe.fromNullable(initParams.data), yieldDto: val, positionsData: positionsData.data, }) @@ -96,7 +93,6 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { ) .map(() => onYieldSelectState({ - initParams: Maybe.fromNullable(initParams.data), yieldDto: action.data, positionsData: positionsData.data, }) diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts index d89e7f41..9e46d597 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts @@ -11,6 +11,7 @@ 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 { useGetTokenBalancesMap } from "./use-get-token-balances-map"; /** @@ -28,6 +29,7 @@ export const useInitToken = () => { isConnecting, } = useSKWallet(); const queryClient = useSKQueryClient(); + const yieldApiFetchClient = useYieldApiFetchClient(); const { data: positionsData } = usePositionsData(); const { @@ -60,8 +62,8 @@ export const useInitToken = () => { getInitParams({ isLedgerLive, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }).chain((initParams) => EitherAsync.liftEither( getInitialToken({ @@ -83,6 +85,7 @@ export const useInitToken = () => { isConnected, isLedgerLive, queryClient, + yieldApiFetchClient, network, yieldIds: tokenBalance.availableYields, initParams: initParams, diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts index de775868..1e2e3c06 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts @@ -11,6 +11,7 @@ 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 { useGetTokenBalancesMap } from "./use-get-token-balances-map"; export const useInitYield = ({ @@ -28,6 +29,7 @@ export const useInitYield = ({ isConnecting, } = useSKWallet(); const queryClient = useSKQueryClient(); + const yieldApiFetchClient = useYieldApiFetchClient(); const { externalProviders, tokensForEnabledYieldsOnly, @@ -71,13 +73,14 @@ export const useInitYield = ({ getInitParams({ isLedgerLive, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }).chain((initParams) => getFirstEligibleYield({ isConnected, isLedgerLive, queryClient, + yieldApiFetchClient, network, yieldIds: val.availableYields, initParams: initParams, 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 f5ff1c08..348e8e45 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 @@ -10,10 +10,8 @@ import { 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"; -import { useValidatorsConfig } from "../../../../hooks/use-validators-config"; import { useSKQueryClient } from "../../../../providers/query-client"; import { useSettings } from "../../../../providers/settings"; import { useSKWallet } from "../../../../providers/sk-wallet"; @@ -34,8 +32,6 @@ export const usePendingActionDeepLink = () => { const { externalProviders } = useSettings(); - const validatorsConfig = useValidatorsConfig(); - return useQuery({ staleTime: Number.POSITIVE_INFINITY, gcTime: Number.POSITIVE_INFINITY, @@ -55,7 +51,6 @@ export const usePendingActionDeepLink = () => { queryClient, yieldApiFetchClient, externalProviders, - validatorsConfig, }) ) ).unsafeCoerce(), @@ -69,7 +64,6 @@ const fn = ({ queryClient, yieldApiFetchClient, externalProviders, - validatorsConfig, }: { isLedgerLive: boolean; address: string; @@ -77,13 +71,12 @@ const fn = ({ queryClient: QueryClient; yieldApiFetchClient: ReturnType; externalProviders: ReturnType["externalProviders"]; - validatorsConfig: ValidatorsConfig; }) => getInitParams({ isLedgerLive, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }).chain((val) => { const initQueryParams = Maybe.of(val) .filter( @@ -163,7 +156,7 @@ const fn = ({ isLedgerLive, yieldId: data.yieldId, queryClient, - validatorsConfig, + yieldApiFetchClient, }).map((yieldOp) => ({ ...val, yieldOp })) ) .chain< diff --git a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts index 235ddd5f..ac884270 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts @@ -1,11 +1,9 @@ -import type { - ActionRequestDto, - ValidatorDto, - YieldDto, -} from "@stakekit/api-hooks"; +import type { AddressesDto, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import { Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { useSKWallet } from "../../../../providers/sk-wallet"; +import { withAdditionalAddresses } from "../../../../providers/yield-api-client-provider/request-helpers"; +import type { YieldCreateActionDto } from "../../../../providers/yield-api-client-provider/types"; import { useEarnPageState } from "./earn-page-state-context"; export const useStakeEnterRequestDto = () => { @@ -26,16 +24,23 @@ export const useStakeEnterRequestDto = () => { selectedStake, selectedToken, }).map<{ + addresses: AddressesDto; gasFeeToken: YieldDto["token"]; - dto: ActionRequestDto; + dto: YieldCreateActionDto; selectedValidators: Map; selectedStake: YieldDto; }>(({ address, selectedStake, selectedToken }) => { const validatorsOrProvider = Just(selectedStake) .chain< - | Pick - | Pick - | Pick + | Pick< + NonNullable, + "validatorAddresses" + > + | Pick< + NonNullable, + "validatorAddress" | "subnetId" + > + | Pick, "providerId"> >((val) => { const validators = [...selectedValidators.values()]; @@ -64,20 +69,24 @@ export const useStakeEnterRequestDto = () => { selectedValidators, selectedStake: selectedStake, gasFeeToken: selectedStake.metadata.gasFeeToken, + addresses: { + address, + additionalAddresses: additionalAddresses ?? undefined, + }, dto: { - addresses: { - address: address, - additionalAddresses: additionalAddresses ?? undefined, - }, - integrationId: selectedStake.id, - args: { - inputToken: selectedToken, - ledgerWalletAPICompatible: isLedgerLive ?? undefined, - tronResource: tronResource.extract(), - amount: stakeAmount.toString(10), - providerId: selectedProviderYieldId.extract(), - ...validatorsOrProvider, - }, + address, + yieldId: selectedStake.id, + arguments: withAdditionalAddresses({ + additionalAddresses, + argumentsDto: { + inputToken: selectedToken.address, + ledgerWalletApiCompatible: isLedgerLive ?? undefined, + tronResource: tronResource.extract(), + amount: stakeAmount.toString(10), + providerId: selectedProviderYieldId.extract(), + ...validatorsOrProvider, + }, + }), }, }; }), diff --git a/packages/widget/src/pages/details/earn-page/state/utils.ts b/packages/widget/src/pages/details/earn-page/state/utils.ts index 146eff39..7edf87c5 100644 --- a/packages/widget/src/pages/details/earn-page/state/utils.ts +++ b/packages/widget/src/pages/details/earn-page/state/utils.ts @@ -1,21 +1,15 @@ import type { YieldDto } from "@stakekit/api-hooks"; import { List, Maybe } from "purify-ts"; -import type { InitParams } from "../../../../domain/types/init-params"; import type { PositionsData } from "../../../../domain/types/positions"; -import { - getInitSelectedValidators, - getMinStakeAmount, -} from "../../../../domain/types/stake"; +import { getMinStakeAmount } from "../../../../domain/types/stake"; import type { State } from "./types"; export const onYieldSelectState = ({ yieldDto, positionsData, - initParams, }: { yieldDto: YieldDto; positionsData: PositionsData; - initParams: Maybe; }): Pick< State, | "selectedStakeId" @@ -26,10 +20,7 @@ export const onYieldSelectState = ({ > => ({ selectedStakeId: Maybe.of(yieldDto.id), stakeAmount: getMinStakeAmount(yieldDto, positionsData), - selectedValidators: getInitSelectedValidators({ - initQueryParams: initParams, - yieldDto: yieldDto, - }), + selectedValidators: new Map(), tronResource: Maybe.fromFalsy( yieldDto.args.enter.args?.tronResource?.required ).map(() => "ENERGY"), 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 bc4e379f..c1dec633 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 @@ -3,7 +3,6 @@ import BigNumber from "bignumber.js"; import { compare, Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { createSelector } from "reselect"; -import type { YieldFindValidatorsParams } from "../../../../common/private-api"; import { usePositionsData } from "../../../../hooks/use-positions-data"; import { useSettings } from "../../../../providers/settings"; import type { SettingsContextType } from "../../../../providers/settings/types"; @@ -140,9 +139,3 @@ const priorityOrder: Record = { claimable: 5, locked: 6, }; - -export const getYieldFindValidatorsQueryKey = ( - params?: YieldFindValidatorsParams -) => { - return ["/v1/yields/validators", ...(params ? [params] : [])] as const; -}; 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 4e1a5a9b..602c21e2 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 @@ -6,6 +6,7 @@ import { useMemo } from "react"; import { useNavigate } from "react-router"; import { equalTokens } from "../../../domain"; import { isForceMaxAmount } from "../../../domain/types/stake"; +import { useYieldValidators } from "../../../hooks/api/use-yield-validators"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useBaseToken } from "../../../hooks/use-base-token"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; @@ -72,6 +73,7 @@ export const usePositionDetails = () => { exitStore.send({ type: "initFlow", data: { + addresses: val.stakeExitRequestDto.addresses, gasFeeToken: val.stakeExitRequestDto.gasFeeToken, integrationData: val.integrationData, requestDto: val.stakeExitRequestDto.dto, @@ -96,12 +98,22 @@ export const usePositionDetails = () => { const baseToken = useBaseToken(integrationData); + const yieldValidators = useYieldValidators({ + enabled: integrationData.isJust(), + yieldId: + integrationData.map((val) => val.id).extractNullable() ?? undefined, + network: + integrationData.map((val) => val.token.network).extractNullable() ?? + undefined, + }); + const providersDetails = useProvidersDetails({ integrationData, validatorsAddresses: positionBalances.data.map((b) => { return b.type === "validators" ? b.validatorsAddresses : []; }), selectedProviderYieldId: Maybe.empty(), + validatorsData: Maybe.fromNullable(yieldValidators.data), }); const canUnstake = integrationData.filter((d) => !!d.args.exit).isJust(); @@ -173,10 +185,12 @@ export const usePositionDetails = () => { const isLoading = positionBalances.isLoading || positionBalancePrices.isLoading || - yieldOpportunity.isLoading; + yieldOpportunity.isLoading || + yieldValidators.isLoading; return { integrationData, + validatorsData: yieldValidators.data ?? [], reducedStakedOrLiquidBalance, positionBalancesByType, canUnstake, 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 324fe22b..adf7ec3f 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 @@ -1,7 +1,9 @@ -import type { ActionRequestDto, YieldDto } from "@stakekit/api-hooks"; +import type { AddressesDto, YieldDto } from "@stakekit/api-hooks"; import { Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { useSKWallet } from "../../../providers/sk-wallet"; +import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; +import type { YieldCreateActionDto } from "../../../providers/yield-api-client-provider/types"; import { useUnstakeOrPendingActionState } from "../state"; export const useStakeExitRequestDto = () => { @@ -16,14 +18,21 @@ export const useStakeExitRequestDto = () => { integrationData, stakedOrLiquidBalances, }).map<{ + addresses: AddressesDto; gasFeeToken: YieldDto["token"]; - dto: ActionRequestDto; + dto: YieldCreateActionDto; }>((val) => { const validatorsOrProvider = Just(null) .chain< - | Pick - | Pick - | Pick + | Pick< + NonNullable, + "validatorAddresses" + > + | Pick< + NonNullable, + "validatorAddress" | "subnetId" + > + | Pick, "providerId"> >(() => { if (val.integrationData.metadata.isIntegrationAggregator) { return List.find( @@ -55,11 +64,7 @@ export const useStakeExitRequestDto = () => { const subnetId = Maybe.fromNullable( val.integrationData.args.exit?.args?.subnetId?.required ) - .chainNullable(() => - val.integrationData.validators.find( - (v) => v.address === b.validator?.address - ) - ) + .chainNullable(() => b.validator) .map((validator) => validator.subnetId) .extract(); @@ -76,16 +81,20 @@ export const useStakeExitRequestDto = () => { return { gasFeeToken: val.integrationData.metadata.gasFeeToken, + addresses: { + address: val.address, + additionalAddresses: additionalAddresses ?? undefined, + }, dto: { - addresses: { - address: val.address, - additionalAddresses: additionalAddresses ?? undefined, - }, - integrationId: val.integrationData.id, - args: { - amount: unstakeAmount.toString(10), - ...validatorsOrProvider, - }, + address: val.address, + yieldId: val.integrationData.id, + arguments: withAdditionalAddresses({ + additionalAddresses, + argumentsDto: { + amount: unstakeAmount.toString(10), + ...validatorsOrProvider, + }, + }), }, }; }), diff --git a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts index 82b76cb8..ee6a412e 100644 --- a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts +++ b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts @@ -1,8 +1,5 @@ import type { TransactionVerificationMessageDto } from "@stakekit/api-hooks"; -import { - actionExit, - transactionGetTransactionVerificationMessageForNetwork, -} from "@stakekit/api-hooks"; +import { transactionGetTransactionVerificationMessageForNetwork } from "@stakekit/api-hooks"; import { useMachine } from "@xstate/react"; import type { SnapshotFromStore } from "@xstate/store"; import { useSelector } from "@xstate/store/react"; @@ -15,6 +12,8 @@ import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useSavedRef } from "../../../hooks/use-saved-ref"; import { useExitStakeStore } from "../../../providers/exit-stake-store"; import { useSKWallet } from "../../../providers/sk-wallet"; +import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; +import { createExitAction } from "../../../providers/yield-api-client-provider/actions"; import type { GetMaybeJust } from "../../../types/utils"; export const useUnstakeMachine = ({ onDone }: { onDone: () => void }) => { @@ -26,20 +25,31 @@ export const useUnstakeMachine = ({ onDone }: { onDone: () => void }) => { (state) => state.context.data ).unsafeCoerce(); + const yieldApiFetchClient = useYieldApiFetchClient(); const { network, address, additionalAddresses, signMessage } = useSKWallet(); const machineParams = useSavedRef({ onDone, trackEvent, exitStore, - actionExit, + yieldApiFetchClient, signMessage, transactionGetTransactionVerificationMessageForNetwork, getData: () => Maybe.fromRecord({ network: Maybe.fromNullable(network), address: Maybe.fromNullable(address), - }).map((val) => ({ ...val, ...exitRequest, additionalAddresses })), + }).map((val) => ({ + ...val, + ...exitRequest, + addresses: { + ...exitRequest.addresses, + additionalAddresses: + exitRequest.addresses.additionalAddresses ?? + additionalAddresses ?? + undefined, + }, + })), }); return useMachine(useState(() => getMachine(machineParams))[0]); @@ -52,6 +62,7 @@ const getMachine = ( exitStore: ReturnType; signMessage: ReturnType["signMessage"]; trackEvent: ReturnType; + yieldApiFetchClient: ReturnType; getData: () => Maybe< GetMaybeJust< SnapshotFromStore< @@ -60,7 +71,6 @@ const getMachine = ( > & { network: NonNullable; address: NonNullable; - additionalAddresses: SKWallet["additionalAddresses"]; } >; }> @@ -126,7 +136,7 @@ const getMachine = ( Just: (val) => { ref.current.trackEvent("unstakeClicked", { yieldId: val.integrationData.id, - amount: val.requestDto.args.amount, + amount: val.requestDto.arguments?.amount, }); if ( @@ -172,9 +182,9 @@ const getMachine = ( val.network, { addresses: { - address: val.address, + address: val.addresses.address, additionalAddresses: - val.additionalAddresses ?? undefined, + val.addresses.additionalAddresses ?? undefined, }, } ) @@ -270,40 +280,55 @@ const getMachine = ( }) => EitherAsync.liftEither( data - .map((val) => val.requestDto) - .map((requestDto) => + .map((val) => Maybe.fromRecord({ + data: Maybe.of(val), + requestDto: Maybe.of(val.requestDto), transactionVerificationMessageDto, signedMessage, }) - .map( + .map( (val) => ({ - ...requestDto, - args: { - ...requestDto.args, - signatureVerification: { - message: - val.transactionVerificationMessageDto.message, - signed: val.signedMessage, - }, + ...val.data, + requestDto: { + ...val.requestDto, + address: val.data.addresses.address, + arguments: { + ...(val.requestDto.arguments ?? {}), + // The backend still accepts this legacy verification bag + // even though the checked-in schema does not expose it yet. + signatureVerification: { + message: + val.transactionVerificationMessageDto + .message, + signed: val.signedMessage, + }, + } as typeof val.requestDto.arguments, }, - }) satisfies typeof requestDto + }) as typeof val.data ) - .orDefault(requestDto) + .orDefault(val) ) .toEither(new Error("Missing params")) ) .chain((val) => - EitherAsync(() => actionExit(val)) + EitherAsync(() => + createExitAction({ + addresses: val.addresses, + fetchClient: ref.current.yieldApiFetchClient, + requestDto: val.requestDto, + yieldDto: val.integrationData, + }) + ) .mapLeft(() => new Error("Stake exit error")) .chain((actionDto) => EitherAsync.liftEither(getValidStakeSessionTx(actionDto)) ) - .ifRight((val) => + .ifRight((result) => ref.current.exitStore.send({ type: "setActionDto", - data: val, + data: result, }) ) ) diff --git a/packages/widget/src/pages/position-details/hooks/utils.ts b/packages/widget/src/pages/position-details/hooks/utils.ts index 80fa3e12..08609efc 100644 --- a/packages/widget/src/pages/position-details/hooks/utils.ts +++ b/packages/widget/src/pages/position-details/hooks/utils.ts @@ -1,7 +1,6 @@ import type { PendingActionDto as LegacyPendingActionDto, YieldBalanceDto as LegacyYieldBalanceDto, - PendingActionRequestDto, ValidatorDto, YieldDto, } from "@stakekit/api-hooks"; @@ -14,8 +13,10 @@ import { isPendingActionValidatorAddressRequired, } from "../../../domain/types/pending-action"; import type { SKWallet } from "../../../domain/types/wallet"; +import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; import type { YieldBalanceDto, + YieldCreateManageActionDto, YieldTokenDto, } from "../../../providers/yield-api-client-provider/types"; import type { State } from "../state/types"; @@ -46,7 +47,7 @@ export const preparePendingActionRequestDto = ({ }): Either< Error, { - requestDto: PendingActionRequestDto; + requestDto: YieldCreateManageActionDto; integrationData: YieldDto; gasFeeToken: YieldDto["token"]; address: NonNullable; @@ -58,7 +59,7 @@ export const preparePendingActionRequestDto = ({ Maybe.fromNullable(address) .toEither(new Error("missing address")) .map((val) => { - const args: PendingActionRequestDto["args"] = { + const args: NonNullable = { amount: Maybe.fromPredicate( Boolean, isPendingActionAmountRequired(pendingActionDto) @@ -88,10 +89,14 @@ export const preparePendingActionRequestDto = ({ return { requestDto: { - args, - integrationId: integration.id, + action: pendingActionDto.type as LegacyPendingActionDto["type"], + address: val, + arguments: withAdditionalAddresses({ + additionalAddresses, + argumentsDto: args, + }), passthrough: pendingActionDto.passthrough, - type: pendingActionDto.type as LegacyPendingActionDto["type"], + yieldId: integration.id, }, 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 5565896e..95b09e53 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -23,6 +23,7 @@ const PositionDetails = () => { const { onPendingActionAmountChange, integrationData, + validatorsData, isLoading, reducedStakedOrLiquidBalance, positionBalancesByType, @@ -262,7 +263,7 @@ const PositionDetails = () => { onValidatorsSubmit([val.address]); }} selectedStake={integrationData} - validators={integrationData.validators} + validators={validatorsData} multiSelect={validatorAddressesHandling.multiSelect} state={validatorAddressesHandling.modalState} > diff --git a/packages/widget/src/pages/review/hooks/use-fees.ts b/packages/widget/src/pages/review/hooks/use-fees.ts index b2d70b89..f77e3988 100644 --- a/packages/widget/src/pages/review/hooks/use-fees.ts +++ b/packages/widget/src/pages/review/hooks/use-fees.ts @@ -12,6 +12,7 @@ import type { FeesBps } from "../types"; export const useFees = ({ amount, feeConfigDto, + yieldFee, prices, token, }: { @@ -19,6 +20,11 @@ export const useFees = ({ token: Maybe; amount: BigNumber; feeConfigDto: Maybe; + yieldFee?: { + deposit?: string; + management?: string; + performance?: string; + } | null; }): { depositFee: Maybe; managementFee: Maybe; @@ -41,6 +47,21 @@ export const useFees = ({ [] ); + const getPercentAmount = useCallback( + (val: string) => amount.multipliedBy(val).dividedBy(100), + [amount] + ); + + const getPercentInUsd = useCallback( + (val: string) => + getFeesInUSD({ + amount: Just(getPercentAmount(val)), + prices, + token, + }), + [getPercentAmount, prices, token] + ); + const depositFee = useMemo( () => feeConfigDto @@ -50,8 +71,25 @@ export const useFees = ({ inPercentage: getBpsInPercentage(val), explanation: t("review.deposit_fee_explanation"), label: t("review.deposit_fee"), - })), - [feeConfigDto, getFeeInUSD, getBpsInPercentage, t] + })) + .altLazy(() => + Just(yieldFee?.deposit) + .chainNullable((v) => v) + .map((val) => ({ + inUSD: getPercentInUsd(val), + inPercentage: `${val}%`, + explanation: t("review.deposit_fee_explanation"), + label: t("review.deposit_fee"), + })) + ), + [ + feeConfigDto, + getFeeInUSD, + getBpsInPercentage, + getPercentInUsd, + t, + yieldFee, + ] ); const managementFee = useMemo( @@ -63,8 +101,25 @@ export const useFees = ({ inPercentage: getBpsInPercentage(val), explanation: t("review.management_fee_explanation"), label: t("review.management_fee"), - })), - [feeConfigDto, getFeeInUSD, getBpsInPercentage, t] + })) + .altLazy(() => + Just(yieldFee?.management) + .chainNullable((v) => v) + .map((val) => ({ + inUSD: getPercentInUsd(val), + inPercentage: `${val}%`, + explanation: t("review.management_fee_explanation"), + label: t("review.management_fee"), + })) + ), + [ + feeConfigDto, + getFeeInUSD, + getBpsInPercentage, + getPercentInUsd, + t, + yieldFee, + ] ); const performanceFee = useMemo( @@ -76,8 +131,25 @@ export const useFees = ({ inPercentage: getBpsInPercentage(val), explanation: t("review.performance_fee_explanation"), label: t("review.performance_fee"), - })), - [feeConfigDto, getFeeInUSD, getBpsInPercentage, t] + })) + .altLazy(() => + Just(yieldFee?.performance) + .chainNullable((v) => v) + .map((val) => ({ + inUSD: getPercentInUsd(val), + inPercentage: `${val}%`, + explanation: t("review.performance_fee_explanation"), + label: t("review.performance_fee"), + })) + ), + [ + feeConfigDto, + getFeeInUSD, + getBpsInPercentage, + getPercentInUsd, + t, + yieldFee, + ] ); return { depositFee, managementFee, performanceFee }; diff --git a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts index c9a63cbb..148d6aa1 100644 --- a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts @@ -1,23 +1,20 @@ -import { - type ActionTypes, - actionPending, - useActionPendingGasEstimate, -} from "@stakekit/api-hooks"; -import { useMutation } from "@tanstack/react-query"; +import type { ActionTypes } from "@stakekit/api-hooks"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; -import { EitherAsync, Maybe } from "purify-ts"; +import { Maybe } from "purify-ts"; import type { ComponentProps } from "react"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import type { RewardTokenDetails } from "../../../components/molecules/reward-token-details"; -import { getValidStakeSessionTx } from "../../../domain"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; import { getRewardTokenSymbols } from "../../../hooks/use-reward-token-details/get-reward-token-symbols"; import { useSavedRef } from "../../../hooks/use-saved-ref"; import { usePendingActionStore } from "../../../providers/pending-action-store"; +import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; +import { createManageAction } from "../../../providers/yield-api-client-provider/actions"; import { formatNumber } from "../../../utils"; import { getGasFeeInUSD } from "../../../utils/formatters"; import { useRegisterFooterButton } from "../../components/footer-outlet/context"; @@ -25,26 +22,44 @@ import type { MetaInfoProps } from "../pages/common-page/common.page"; export const usePendingActionReview = () => { const pendingActionStore = usePendingActionStore(); + const yieldApiFetchClient = useYieldApiFetchClient(); const pendingRequest = useSelector( pendingActionStore, (state) => state.context.data ).unsafeCoerce(); - const actionPendingGasEstimate = useActionPendingGasEstimate( - pendingRequest.requestDto, - { query: { staleTime: 0, gcTime: 0 } } - ); + const actionPreviewQuery = useQuery({ + enabled: !!pendingRequest, + queryKey: ["pending-review-action-preview", pendingRequest.requestDto], + retry: false, + queryFn: () => + createManageAction({ + addresses: pendingRequest.addresses, + fetchClient: yieldApiFetchClient, + requestDto: pendingRequest.requestDto, + yieldDto: pendingRequest.integrationData, + }), + }); const pendingTxGas = useMemo( () => - Maybe.fromNullable(actionPendingGasEstimate.data?.amount).map(BigNumber), - [actionPendingGasEstimate.data] + Maybe.fromNullable(actionPreviewQuery.data) + .map((actionDto) => + actionDto.transactions.reduce( + (acc, transaction) => + acc.plus(transaction.gasEstimate?.amount ?? 0), + new BigNumber(0) + ) + ) + .map((value) => (value.isZero() ? null : value)) + .chainNullable((value) => value), + [actionPreviewQuery.data] ); const amount = useMemo( - () => new BigNumber(pendingRequest.requestDto.args?.amount ?? 0), - [pendingRequest.requestDto.args?.amount] + () => new BigNumber(pendingRequest.requestDto.arguments?.amount ?? 0), + [pendingRequest.requestDto.arguments?.amount] ); const interactedToken = useMemo( @@ -77,11 +92,11 @@ export const usePendingActionReview = () => { Maybe.of( t( `position_details.pending_action_button.${ - pendingRequest.requestDto.type.toLowerCase() as Lowercase + pendingRequest.requestDto.action.toLowerCase() as Lowercase }` as const ) ), - [pendingRequest.requestDto.type, t] + [pendingRequest.requestDto.action, t] ); const navigate = useNavigate(); @@ -98,13 +113,9 @@ export const usePendingActionReview = () => { const actionPendingMutation = useMutation({ mutationFn: async () => - ( - await EitherAsync(() => actionPending(pendingRequest.requestDto)) - .mapLeft(() => new Error("Pending actions error")) - .chain((actionDto) => - EitherAsync.liftEither(getValidStakeSessionTx(actionDto)) - ) - ).unsafeCoerce(), + actionPreviewQuery.data ?? + (await actionPreviewQuery.refetch()).data ?? + Promise.reject(new Error("Pending actions error")), onSuccess: (data) => { pendingActionStore.send({ type: "setActionDto", data }); navigate("../steps", { relative: "path" }); @@ -131,11 +142,11 @@ export const usePendingActionReview = () => { return { type: "pendingAction", - pendingAction: pendingRequest.requestDto.type, + pendingAction: pendingRequest.requestDto.action, rewardToken, } satisfies ComponentProps; }), - [integrationData, pendingRequest.requestDto.type] + [integrationData, pendingRequest.requestDto.action] ); const onClickRef = useSavedRef(onClick); @@ -166,6 +177,8 @@ export const usePendingActionReview = () => { metaInfo, isGasCheckWarning: !!gasWarningCheck.data, gasCheckLoading: - actionPendingGasEstimate.isLoading || gasWarningCheck.isLoading, + actionPreviewQuery.isLoading || + actionPreviewQuery.isFetching || + gasWarningCheck.isLoading, }; }; diff --git a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts index f400daba..da7f9d2e 100644 --- a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts @@ -1,17 +1,10 @@ -import { - actionEnter, - useActionEnterGasEstimation, - useYieldGetFeeConfiguration, -} from "@stakekit/api-hooks"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { useSelector } from "@xstate/store/react"; -import { isAxiosError } from "axios"; import BigNumber from "bignumber.js"; -import { EitherAsync, Maybe } from "purify-ts"; +import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; -import { getValidStakeSessionTx } from "../../../domain"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useEstimatedRewards } from "../../../hooks/use-estimated-rewards"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; @@ -20,6 +13,8 @@ import { useSavedRef } from "../../../hooks/use-saved-ref"; import { useYieldType } from "../../../hooks/use-yield-type"; import { useEnterStakeStore } from "../../../providers/enter-stake-store"; import { useSettings } from "../../../providers/settings"; +import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; +import { createEnterAction } from "../../../providers/yield-api-client-provider/actions"; import { APToPercentage, formatNumber } from "../../../utils"; import { getGasFeeInUSD } from "../../../utils/formatters"; import { useRegisterFooterButton } from "../../components/footer-outlet/context"; @@ -34,30 +29,47 @@ export const useStakeReview = () => { (state) => state.context.data ).unsafeCoerce(); - const integrationId = enterRequest.requestDto.integrationId; - const feeConfigDto = useYieldGetFeeConfiguration(integrationId); + const yieldApiFetchClient = useYieldApiFetchClient(); const stakeAmount = useMemo( - () => new BigNumber(enterRequest.requestDto.args.amount), + () => new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0), [enterRequest] ); - const actionEnterGasEstimation = useActionEnterGasEstimation( - enterRequest.requestDto, - { query: { staleTime: 0, gcTime: 0 } } - ); + const actionPreviewQuery = useQuery({ + enabled: !!enterRequest, + queryKey: ["stake-review-action-preview", enterRequest.requestDto], + retry: false, + queryFn: () => + createEnterAction({ + addresses: enterRequest.addresses, + fetchClient: yieldApiFetchClient, + inputToken: enterRequest.selectedToken, + requestDto: enterRequest.requestDto, + yieldDto: enterRequest.selectedStake, + }), + }); const stakeEnterTxGas = useMemo( () => - Maybe.fromNullable(actionEnterGasEstimation.data?.amount).map(BigNumber), - [actionEnterGasEstimation.data] + Maybe.fromNullable(actionPreviewQuery.data) + .map((actionDto) => + actionDto.transactions.reduce( + (acc, transaction) => + acc.plus(transaction.gasEstimate?.amount ?? 0), + new BigNumber(0) + ) + ) + .map((value) => (value.isZero() ? null : value)) + .chainNullable((value) => value), + [actionPreviewQuery.data] ); const gasCheckWarning = useGasWarningCheck({ gasAmount: stakeEnterTxGas, gasFeeToken: enterRequest.gasFeeToken, - address: enterRequest.requestDto.addresses.address, - additionalAddresses: enterRequest.requestDto.addresses.additionalAddresses, + address: enterRequest.addresses.address, + additionalAddresses: enterRequest.addresses.additionalAddresses, isStake: true, stakeAmount, stakeToken: enterRequest.selectedToken, @@ -73,8 +85,8 @@ export const useStakeReview = () => { ); const selectedProviderYieldId = useMemo( - () => Maybe.fromNullable(enterRequest.requestDto.args.providerId), - [enterRequest.requestDto.args.providerId] + () => Maybe.fromNullable(enterRequest.requestDto.arguments?.providerId), + [enterRequest.requestDto.arguments?.providerId] ); const rewardToken = useRewardTokenDetails(selectedStake); @@ -113,9 +125,21 @@ export const useStakeReview = () => { const { depositFee, managementFee, performanceFee } = useFees({ amount: stakeAmount, token: selectedToken, - feeConfigDto: useMemo( - () => Maybe.fromNullable(feeConfigDto.data), - [feeConfigDto.data] + feeConfigDto: Maybe.empty(), + yieldFee: useMemo( + () => + ( + enterRequest.selectedStake as typeof enterRequest.selectedStake & { + mechanics?: { + fee?: { + deposit?: string; + management?: string; + performance?: string; + }; + }; + } + ).mechanics?.fee ?? null, + [enterRequest.selectedStake] ), prices: useMemo( () => Maybe.fromNullable(pricesState.data), @@ -129,24 +153,9 @@ export const useStakeReview = () => { const enterMutation = useMutation({ mutationFn: async () => - ( - await EitherAsync(() => actionEnter(enterRequest.requestDto)) - .mapLeft((e) => { - if ( - isAxiosError(e) && - StakingNotAllowedError.isStakingNotAllowedErrorDto( - e.response?.data - ) - ) { - return new StakingNotAllowedError(); - } - - return new Error("Stake enter error"); - }) - .chain((actionDto) => - EitherAsync.liftEither(getValidStakeSessionTx(actionDto)) - ) - ).unsafeCoerce(), + actionPreviewQuery.data ?? + (await actionPreviewQuery.refetch()).data ?? + Promise.reject(new Error("Stake enter error")), onSuccess: (data) => { enterStore.send({ type: "setActionDto", data }); navigate("/steps"); @@ -210,23 +219,13 @@ export const useStakeReview = () => { metaInfo, isGasCheckWarning: !!gasCheckWarning.data, gasCheckLoading: - actionEnterGasEstimation.isLoading || gasCheckWarning.isLoading, + actionPreviewQuery.isLoading || + actionPreviewQuery.isFetching || + gasCheckWarning.isLoading, depositFee, managementFee, performanceFee, - feeConfigLoading: feeConfigDto.isPending, + feeConfigLoading: actionPreviewQuery.isLoading, commissionFee, }; }; - -class StakingNotAllowedError extends Error { - static isStakingNotAllowedErrorDto = (e: unknown) => { - const dto = e as undefined | { type: string; code: number }; - - return dto && dto.code === 422 && dto.type === "STAKING_ERROR"; - }; - - constructor() { - super("Staking not allowed, needs unstaking and trying again"); - } -} diff --git a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts index 974778b8..02e968e0 100644 --- a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts @@ -1,4 +1,4 @@ -import { useActionExitGasEstimate } from "@stakekit/api-hooks"; +import { useQuery } from "@tanstack/react-query"; import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; @@ -12,6 +12,8 @@ import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; import { getRewardTokenSymbols } from "../../../hooks/use-reward-token-details/get-reward-token-symbols"; import { useSavedRef } from "../../../hooks/use-saved-ref"; import { useExitStakeStore } from "../../../providers/exit-stake-store"; +import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; +import { createExitAction } from "../../../providers/yield-api-client-provider/actions"; import { formatNumber } from "../../../utils"; import { getGasFeeInUSD } from "../../../utils/formatters"; import { useRegisterFooterButton } from "../../components/footer-outlet/context"; @@ -24,14 +26,34 @@ export const useUnstakeActionReview = () => { (state) => state.context.data ).unsafeCoerce(); - const actionExitGasEstimate = useActionExitGasEstimate( - exitRequest.requestDto, - { query: { staleTime: 0, gcTime: 0 } } - ); + const yieldApiFetchClient = useYieldApiFetchClient(); + + const actionPreviewQuery = useQuery({ + enabled: !!exitRequest, + queryKey: ["unstake-review-action-preview", exitRequest.requestDto], + retry: false, + queryFn: () => + createExitAction({ + addresses: exitRequest.addresses, + fetchClient: yieldApiFetchClient, + requestDto: exitRequest.requestDto, + yieldDto: exitRequest.integrationData, + }), + }); const stakeExitTxGas = useMemo( - () => Maybe.fromNullable(actionExitGasEstimate.data?.amount).map(BigNumber), - [actionExitGasEstimate.data] + () => + Maybe.fromNullable(actionPreviewQuery.data) + .map((actionDto) => + actionDto.transactions.reduce( + (acc, transaction) => + acc.plus(transaction.gasEstimate?.amount ?? 0), + new BigNumber(0) + ) + ) + .map((value) => (value.isZero() ? null : value)) + .chainNullable((value) => value), + [actionPreviewQuery.data] ); const interactedToken = useMemo( @@ -50,15 +72,15 @@ export const useUnstakeActionReview = () => { }); const amount = useMemo( - () => new BigNumber(exitRequest.requestDto.args.amount ?? 0), - [exitRequest.requestDto.args.amount] + () => new BigNumber(exitRequest.requestDto.arguments?.amount ?? 0), + [exitRequest.requestDto.arguments?.amount] ); const gasWarningCheck = useGasWarningCheck({ gasAmount: stakeExitTxGas, gasFeeToken: exitRequest.gasFeeToken, - address: exitRequest.requestDto.addresses.address, - additionalAddresses: exitRequest.requestDto.addresses.additionalAddresses, + address: exitRequest.addresses.address, + additionalAddresses: exitRequest.addresses.additionalAddresses, isStake: false, }); @@ -156,7 +178,9 @@ export const useUnstakeActionReview = () => { onCloseUnstakeSignMessage, showUnstakeSignMessagePopup, gasCheckLoading: - actionExitGasEstimate.isLoading || gasWarningCheck.isLoading, + actionPreviewQuery.isLoading || + actionPreviewQuery.isFetching || + gasWarningCheck.isLoading, isGasCheckWarning: !!gasWarningCheck.data, }; }; diff --git a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts index f9fc8019..e081fe80 100644 --- a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts +++ b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts @@ -1,38 +1,28 @@ -import type { - ActionDto, - TransactionDto, - TransactionFormat, -} from "@stakekit/api-hooks"; -import { - transactionConstruct, - transactionGetTransaction, - transactionGetTransactionStatusFromId, - transactionSubmit, - transactionSubmitHash, -} from "@stakekit/api-hooks"; +import type { ActionDto, TransactionDto } from "@stakekit/api-hooks"; import { useMachine } from "@xstate/react"; -import { isAxiosError } from "axios"; import { EitherAsync, Left, List, Maybe, Right } from "purify-ts"; import { type RefObject, useMemo, useState } from "react"; import { assign, emit, setup } from "xstate"; -import { getAverageGasMode } from "../../../common/get-gas-mode-value"; -import { withRequestErrorRetry } from "../../../common/utils"; import { isTxError } from "../../../domain"; import type { ActionMeta } from "../../../domain/types/wallets/generic-wallet"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useSavedRef } from "../../../hooks/use-saved-ref"; -import { useSettings } from "../../../providers/settings"; import { useSKWallet } from "../../../providers/sk-wallet"; import type { SendTransactionError, TransactionDecodeError, } from "../../../providers/sk-wallet/errors"; +import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; +import { + getTransaction, + submitTransaction, + submitTransactionHash, +} from "../../../providers/yield-api-client-provider/actions"; import type { GetStakeSessionError } from "./errors"; import { SignError, SubmitError, SubmitHashError, - TransactionConstructError, TXCheckError, } from "./errors"; @@ -40,11 +30,7 @@ type TxMeta = { url: string | null; signedTx: string | null; broadcasted: boolean | null; - signError: - | SendTransactionError - | TransactionDecodeError - | TransactionConstructError - | null; + signError: SendTransactionError | TransactionDecodeError | null; txCheckError: GetStakeSessionError | null; done: boolean; }; @@ -73,24 +59,23 @@ export const useStepsMachine = ({ integrationId: ActionDto["integrationId"]; actionMeta: ActionMeta; }) => { - const { signTransaction, signMessage, isLedgerLive } = useSKWallet(); - const { preferredTransactionFormat } = useSettings(); + const { signTransaction, signMessage } = useSKWallet(); + const yieldApiFetchClient = useYieldApiFetchClient(); const trackEvent = useTrackEvent(); const sortedTransactions = useMemo( - () => transactions.sort((a, b) => a.stepIndex - b.stepIndex), + () => [...transactions].sort((a, b) => a.stepIndex - b.stepIndex), [transactions] ); const machineParams = useSavedRef({ transactions: sortedTransactions, integrationId, - isLedgerLive, trackEvent, signMessage, signTransaction, actionMeta, - preferredTransactionFormat, + yieldApiFetchClient, }); return useMachine(useState(() => getMachine(machineParams))[0]); @@ -101,22 +86,14 @@ const getMachine = ( RefObject<{ transactions: ActionDto["transactions"]; integrationId: ActionDto["integrationId"]; - isLedgerLive: boolean; trackEvent: ReturnType; signMessage: ReturnType["signMessage"]; signTransaction: ReturnType["signTransaction"]; actionMeta: ActionMeta; - preferredTransactionFormat?: TransactionFormat; + yieldApiFetchClient: ReturnType; }> > ) => { - const txConstruct = (...params: Parameters) => - withRequestErrorRetry({ - fn: () => transactionConstruct(...params), - shouldRetry: (e, retryCount) => - retryCount <= 3 && isAxiosError(e) && e.response?.status === 404, - }).mapLeft(() => new Error("Transaction construct error")); - const initContext = getInitContext( ref.current.transactions, ref.current.integrationId @@ -139,11 +116,7 @@ const getMachine = ( } | { type: "__SIGN_ERROR__"; - val: - | SendTransactionError - | TransactionDecodeError - | TransactionConstructError - | SignError; + val: SendTransactionError | TransactionDecodeError | SignError; } | { type: "__BROADCAST_SUCCESS__" } | { type: "__BROADCAST_ERROR__"; val: Error | SubmitHashError } @@ -237,94 +210,75 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx].tx) - .toEither(new TransactionConstructError("missing tx")) + .toEither(new SignError({ network: "unknown", txId: "unknown" })) ) .chain< - | TransactionConstructError - | SendTransactionError - | TransactionDecodeError - | SignError, + SendTransactionError | TransactionDecodeError | SignError, SignRes - >((tx) => - getAverageGasMode({ network: tx.network }) - .chainLeft(async () => Right(null)) - .chain((gas) => - txConstruct(tx.id, { - gasArgs: gas?.gasArgs, - ledgerWalletAPICompatible: ref.current.isLedgerLive, - ...(!!ref.current.preferredTransactionFormat && { - transactionFormat: ref.current.preferredTransactionFormat, - }), - }).mapLeft(() => new TransactionConstructError()) - ) - .chain< - | TransactionConstructError - | SendTransactionError - | TransactionDecodeError - | SignError, - SignRes - >((constructedTx) => { - if ( - constructedTx.status === "BROADCASTED" || - constructedTx.status === "CONFIRMED" - ) { - return EitherAsync.liftEither( - Right({ type: "broadcasted" }) - ); - } - - if (!constructedTx.unsignedTransaction) { - return EitherAsync.liftEither( - Left(new TransactionConstructError()) - ); - } - - if (constructedTx.isMessage) { - return ref.current - .signMessage(constructedTx.unsignedTransaction) - .map((val) => ({ - type: "regular" as const, - data: { signedTx: val, broadcasted: false }, - })) - .mapLeft( - () => - new SignError({ - network: constructedTx.network, - txId: constructedTx.id, - }) - ); - } + >((tx) => { + if (tx.status === "BROADCASTED" || tx.status === "CONFIRMED") { + return EitherAsync.liftEither(Right({ type: "broadcasted" })); + } - return ref.current - .signTransaction({ - tx: constructedTx.unsignedTransaction, - ledgerHwAppId: constructedTx.ledgerHwAppId, - txMeta: { - ...ref.current.actionMeta, - txId: constructedTx.id, - txType: constructedTx.type, - annotatedTransaction: - constructedTx.annotatedTransaction, - structuredTransaction: - constructedTx.structuredTransaction, - }, - network: constructedTx.network, + if (!tx.unsignedTransaction) { + return EitherAsync.liftEither( + Left( + new SignError({ + network: tx.network, + txId: tx.id, }) - .map((val) => ({ - ...val, - network: constructedTx.network, - txId: constructedTx.id, - })) - .ifRight(() => - ref.current.trackEvent("txSigned", { - txId: constructedTx.id, - network: constructedTx.network, - yieldId: context.yieldId, + ) + ); + } + + if (tx.isMessage) { + return ref.current + .signMessage(tx.unsignedTransaction) + .map((val) => ({ + type: "regular" as const, + data: { signedTx: val, broadcasted: false }, + })) + .mapLeft( + () => + new SignError({ + network: tx.network, + txId: tx.id, }) - ) - .map((val) => ({ type: "regular", data: val })); + ); + } + + const unsignedTransaction = + typeof tx.unsignedTransaction === "string" + ? tx.unsignedTransaction + : JSON.stringify(tx.unsignedTransaction); + + return ref.current + .signTransaction({ + tx: unsignedTransaction, + ledgerHwAppId: null, + txMeta: { + ...ref.current.actionMeta, + txId: tx.id, + txType: tx.type, + annotatedTransaction: tx.annotatedTransaction, + structuredTransaction: tx.structuredTransaction, + }, + network: tx.network, }) - ) + .map((val) => ({ + ...val, + network: tx.network, + txId: tx.id, + })) + .ifRight(() => + ref.current.trackEvent("txSigned", { + txId: tx.id, + network: tx.network, + yieldId: context.yieldId, + }) + ) + .map((val) => ({ type: "regular", data: val })); + }) .caseOf({ Left: (l) => { console.log(l); @@ -401,8 +355,10 @@ const getMachine = ( .chain((currentTx) => { if (currentTx.meta.broadcasted) { return EitherAsync(() => - transactionSubmitHash(currentTx.tx.id, { + submitTransactionHash({ + fetchClient: ref.current.yieldApiFetchClient, hash: currentTx.meta.signedTx!, + transactionId: currentTx.tx.id, }) ) .mapLeft(() => new SubmitHashError()) @@ -417,8 +373,10 @@ const getMachine = ( } return EitherAsync(() => - transactionSubmit(currentTx.tx.id, { + submitTransaction({ + fetchClient: ref.current.yieldApiFetchClient, signedTransaction: currentTx.meta.signedTx!, + transactionId: currentTx.tx.id, }) ) .mapLeft(() => new SubmitError()) @@ -490,23 +448,16 @@ const getMachine = ( .toEither(new Error("missing tx")) ) .chain((currentTx) => - withRequestErrorRetry({ - fn: () => - transactionGetTransactionStatusFromId(currentTx.tx.id), - shouldRetry: (e, retryCount) => - retryCount <= 3 && - isAxiosError(e) && - e.response?.status === 404, - }) - .map((res) => ({ url: res.url, status: res.status })) - .chainLeft(() => - EitherAsync(() => - transactionGetTransaction(currentTx.tx.id) - ).map((res) => ({ - url: res.explorerUrl, - status: res.status, - })) - ) + EitherAsync(() => + getTransaction({ + fetchClient: ref.current.yieldApiFetchClient, + transactionId: currentTx.tx.id, + }) + ) + .map((res) => ({ + url: res.explorerUrl, + status: res.status, + })) .mapLeft(() => new TXCheckError()) .chain((val) => EitherAsync.liftEither( @@ -549,7 +500,7 @@ const getMachine = ( ...val.meta, signError: null, txCheckError: null, - url: v.url, + url: v.url ?? null, done: true, }, } @@ -632,16 +583,28 @@ const getInitContext = ( const txStates = transactions.map((dto) => ({ tx: dto, meta: { - broadcasted: null, + broadcasted: + dto.status === "BROADCASTED" || dto.status === "CONFIRMED" + ? true + : null, signedTx: null, - url: null, + url: dto.explorerUrl ?? null, signError: null, txCheckError: null, - done: false, + done: dto.status === "CONFIRMED" || dto.status === "SKIPPED", }, })); - const currentTxIdx = 0; + const currentTxIdx = txStates.findIndex((txState) => !txState.meta.done); + + if (currentTxIdx === -1) { + return { + enabled: false, + txStates, + currentTxMeta: null, + yieldId: integrationId, + }; + } const currentTxMeta = { idx: currentTxIdx, diff --git a/packages/widget/src/providers/enter-stake-store/index.tsx b/packages/widget/src/providers/enter-stake-store/index.tsx index f1da4d09..c2a0912d 100644 --- a/packages/widget/src/providers/enter-stake-store/index.tsx +++ b/packages/widget/src/providers/enter-stake-store/index.tsx @@ -1,6 +1,6 @@ import type { ActionDto, - ActionRequestDto, + AddressesDto, TokenDto, ValidatorDto, YieldDto, @@ -8,9 +8,11 @@ import type { import { createStore } from "@xstate/store"; import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; +import type { YieldCreateActionDto } from "../yield-api-client-provider/types"; type InitData = { - requestDto: ActionRequestDto; + requestDto: YieldCreateActionDto; + addresses: AddressesDto; gasFeeToken: YieldDto["token"]; selectedStake: YieldDto; selectedValidators: Map; diff --git a/packages/widget/src/providers/exit-stake-store/index.tsx b/packages/widget/src/providers/exit-stake-store/index.tsx index 2d8e13fb..44dde61b 100644 --- a/packages/widget/src/providers/exit-stake-store/index.tsx +++ b/packages/widget/src/providers/exit-stake-store/index.tsx @@ -1,6 +1,6 @@ import type { ActionDto, - ActionRequestDto, + AddressesDto, TokenDto, YieldDto, } from "@stakekit/api-hooks"; @@ -8,10 +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"; +import type { + YieldCreateActionDto, + YieldTokenDto, +} from "../yield-api-client-provider/types"; type InitData = { - requestDto: ActionRequestDto; + requestDto: YieldCreateActionDto; + addresses: AddressesDto; gasFeeToken: YieldDto["token"]; unstakeAmount: BigNumber; integrationData: YieldDto; diff --git a/packages/widget/src/providers/pending-action-store/index.tsx b/packages/widget/src/providers/pending-action-store/index.tsx index 290864f0..80136eca 100644 --- a/packages/widget/src/providers/pending-action-store/index.tsx +++ b/packages/widget/src/providers/pending-action-store/index.tsx @@ -2,17 +2,19 @@ import type { ActionDto, ActionTypes, AddressesDto, - PendingActionRequestDto, TokenDto, YieldDto, } from "@stakekit/api-hooks"; 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"; +import type { + YieldCreateManageActionDto, + YieldTokenDto, +} from "../yield-api-client-provider/types"; type InitData = { - requestDto: PendingActionRequestDto; + requestDto: YieldCreateManageActionDto; addresses: AddressesDto; pendingActionType: ActionTypes; integrationData: YieldDto; diff --git a/packages/widget/src/providers/wagmi/index.ts b/packages/widget/src/providers/wagmi/index.ts index d8edb078..3b09cf5f 100644 --- a/packages/widget/src/providers/wagmi/index.ts +++ b/packages/widget/src/providers/wagmi/index.ts @@ -16,7 +16,8 @@ import { useQuery } from "@tanstack/react-query"; import uniqwith from "lodash.uniqwith"; import { createStore } from "mipd"; import { EitherAsync, Just, Left, Maybe, Right } from "purify-ts"; -import type { RefObject } from "react"; +import { type RefObject, useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { createClient } from "viem"; import { createConfig, http } from "wagmi"; import type { Chain } from "wagmi/chains"; @@ -29,10 +30,8 @@ import type { EvmChainsMap } from "../../domain/types/chains/evm"; import type { MiscChainsMap } from "../../domain/types/chains/misc"; import type { SubstrateChainsMap } from "../../domain/types/chains/substrate"; import type { SKExternalProviders } from "../../domain/types/wallets"; -import type { ValidatorsConfig } from "../../domain/types/yields"; import { getInitParams } from "../../hooks/use-init-params"; import { useSavedRef } from "../../hooks/use-saved-ref"; -import { useValidatorsConfig } from "../../hooks/use-validators-config"; import type { GetEitherAsyncRight } from "../../types/utils"; import { isLedgerDappBrowserProvider } from "../../utils"; import { getEnabledNetworks } from "../api/get-enabled-networks"; @@ -46,6 +45,7 @@ import { getConfig as getSafeConnector } from "../safe/config"; import { useSettings } from "../settings"; import type { SettingsProps, VariantProps } from "../settings/types"; import { getConfig as getSubstrateConfig } from "../substrate/config"; +import { createYieldApiFetchClient } from "../yield-api-client-provider"; const mipdStore = createStore(); @@ -67,9 +67,9 @@ const buildWagmiConfig = async (opts: { forceWalletConnectOnly: boolean; customConnectors?: (chains: Chain[]) => WalletList; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; isLedgerLive: boolean; isSafe: boolean; - validatorsConfig: ValidatorsConfig; chainIconMapping: SettingsProps["chainIconMapping"]; variant: VariantProps["variant"]; solanaWallets: SolanaWallet[]; @@ -113,8 +113,8 @@ const buildWagmiConfig = async (opts: { getInitParams({ isLedgerLive: opts.isLedgerLive, queryClient: opts.queryClient, + yieldApiFetchClient: opts.yieldApiFetchClient, externalProviders: opts.externalProviders?.current, - validatorsConfig: opts.validatorsConfig, }), ]).then(([evm, cosmos, misc, substrate, queryParams]) => evm.chain((e) => @@ -378,14 +378,24 @@ export const useWagmiConfig = () => { variant, mapWalletListFn, tonConnectManifestUrl, + apiKey, + yieldsApiUrl, } = useSettings(); + const { i18n } = useTranslation(); const solanaWallets = useSolanaWallet(); const solanaConnection = useSolanaConnection(); const queryClient = useSKQueryClient(); - - const validatorsConfig = useValidatorsConfig(); + const yieldApiFetchClient = useMemo( + () => + createYieldApiFetchClient({ + apiKey, + i18n, + url: yieldsApiUrl ?? config.env.yieldsApiUrl, + }), + [apiKey, i18n, yieldsApiUrl] + ); const externalProvidersRef = useSavedRef(externalProviders) as | RefObject @@ -401,12 +411,12 @@ export const useWagmiConfig = () => { forceWalletConnectOnly: !!wagmi?.forceWalletConnectOnly, customConnectors: wagmi?.__customConnectors__, queryClient, + yieldApiFetchClient, isLedgerLive: isLedgerDappBrowserProvider(), isSafe: !!isSafe, ...(externalProvidersRef.current && { externalProviders: externalProvidersRef, }), - validatorsConfig, chainIconMapping, variant, solanaWallets: solanaWallets.wallets, diff --git a/packages/widget/src/providers/yield-api-client-provider/actions.ts b/packages/widget/src/providers/yield-api-client-provider/actions.ts new file mode 100644 index 00000000..487a8538 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/actions.ts @@ -0,0 +1,173 @@ +import type { + AddressesDto, + ActionDto as LegacyActionDto, + TokenDto, + YieldDto, +} from "@stakekit/api-hooks"; +import type { Client } from "openapi-fetch"; +import type { paths } from "../../types/yield-api-schema"; +import { adaptActionDto } from "./compat"; +import { getResponseData } from "./request-helpers"; +import type { YieldCreateActionDto, YieldCreateManageActionDto } from "./types"; + +export const createEnterAction = async ({ + addresses, + fetchClient, + inputToken, + requestDto, + yieldDto, +}: { + addresses: AddressesDto; + fetchClient: Client; + inputToken: TokenDto; + requestDto: YieldCreateActionDto; + yieldDto: YieldDto; +}): Promise => { + const actionDto = await getResponseData( + fetchClient.POST("/v1/actions/enter", { + body: requestDto, + }) + ); + + return adaptActionDto({ + actionDto, + addresses, + gasFeeToken: yieldDto.metadata.gasFeeToken, + inputToken, + yieldDto, + }); +}; + +export const createExitAction = async ({ + addresses, + fetchClient, + requestDto, + yieldDto, +}: { + addresses: AddressesDto; + fetchClient: Client; + requestDto: YieldCreateActionDto; + yieldDto: YieldDto; +}): Promise => { + const actionDto = await getResponseData( + fetchClient.POST("/v1/actions/exit", { + body: requestDto, + }) + ); + + return adaptActionDto({ + actionDto, + addresses, + gasFeeToken: yieldDto.metadata.gasFeeToken, + yieldDto, + }); +}; + +export const createManageAction = async ({ + addresses, + fetchClient, + requestDto, + yieldDto, +}: { + addresses: AddressesDto; + fetchClient: Client; + requestDto: YieldCreateManageActionDto; + yieldDto: YieldDto; +}): Promise => { + const actionDto = await getResponseData( + fetchClient.POST("/v1/actions/manage", { + body: requestDto, + }) + ); + + return adaptActionDto({ + actionDto, + addresses, + gasFeeToken: yieldDto.metadata.gasFeeToken, + yieldDto, + }); +}; + +export const listActions = async ({ + address, + fetchClient, + limit, + offset, +}: { + address: string; + fetchClient: Client; + limit: number; + offset: number; +}) => + getResponseData( + fetchClient.GET("/v1/actions", { + params: { + query: { + address, + offset, + limit, + }, + }, + }) + ); + +export const getTransaction = async ({ + fetchClient, + transactionId, +}: { + fetchClient: Client; + transactionId: string; +}) => + getResponseData( + fetchClient.GET("/v1/transactions/{transactionId}", { + params: { + path: { + transactionId, + }, + }, + }) + ); + +export const submitTransaction = async ({ + fetchClient, + signedTransaction, + transactionId, +}: { + fetchClient: Client; + signedTransaction: string; + transactionId: string; +}) => + getResponseData( + fetchClient.POST("/v1/transactions/{transactionId}/submit", { + params: { + path: { + transactionId, + }, + }, + body: { + signedTransaction, + }, + }) + ); + +export const submitTransactionHash = async ({ + fetchClient, + hash, + transactionId, +}: { + fetchClient: Client; + hash: string; + transactionId: string; +}) => + getResponseData( + fetchClient.PUT("/v1/transactions/{transactionId}/submit-hash", { + params: { + path: { + transactionId, + }, + }, + body: { + hash, + }, + }) + ); diff --git a/packages/widget/src/providers/yield-api-client-provider/compat.ts b/packages/widget/src/providers/yield-api-client-provider/compat.ts new file mode 100644 index 00000000..9ae58d8f --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/compat.ts @@ -0,0 +1,519 @@ +import type { + AddressesDto, + ActionDto as LegacyActionDto, + TokenDto as LegacyTokenDto, + TransactionDto as LegacyTransactionDto, + ValidatorDto as LegacyValidatorDto, + YieldDto as LegacyYieldDto, +} from "@stakekit/api-hooks"; +import type { + YieldActionDto, + YieldDto, + YieldTokenDto, + YieldTransactionDto, + YieldValidatorDto, +} from "./types"; + +const NATIVE_TOKEN_PLACEHOLDER = "0x"; + +const toLower = (value: string) => value.toLowerCase(); + +type EncodedGasEstimate = { + amount?: string | null; + gasLimit?: string | null; + token?: YieldTokenDto | LegacyTokenDto | null; +}; + +const mapToken = ( + token: YieldTokenDto | LegacyTokenDto | null | undefined +): LegacyTokenDto | undefined => { + if (!token) return undefined; + + return { ...token } as LegacyTokenDto; +}; + +const uniqTokens = ( + tokens: (YieldTokenDto | LegacyTokenDto | null | undefined)[] +) => { + const seen = new Set(); + + return tokens.flatMap((token) => { + const mapped = mapToken(token); + + if (!mapped) return []; + + const key = `${mapped.network}:${mapped.address?.toLowerCase() ?? ""}:${ + mapped.symbol + }`; + + if (seen.has(key)) { + return []; + } + + seen.add(key); + return [mapped]; + }); +}; + +const secondsToDays = (seconds: number | undefined) => { + if (seconds === undefined) return undefined; + + return { days: Math.round(seconds / 86400) }; +}; + +const getRewardType = ({ + yieldDto, + legacyYieldDto, +}: { + yieldDto: YieldDto; + legacyYieldDto: LegacyYieldDto | null; +}): LegacyYieldDto["rewardType"] => { + const rateType = yieldDto.rewardRate?.rateType?.toLowerCase(); + + if (rateType === "apr" || rateType === "apy") { + return rateType; + } + + return legacyYieldDto?.rewardType ?? "variable"; +}; + +const getArgumentConfig = ( + yieldDto: YieldDto, + legacyYieldDto: LegacyYieldDto | null, + type: "enter" | "exit" +) => { + const fields = yieldDto.mechanics?.arguments?.[type]?.fields ?? []; + const legacyArgs = legacyYieldDto?.args?.[type]?.args ?? {}; + const nextArgs = { ...legacyArgs } as Record; + + for (const field of fields) { + const legacyField = (legacyArgs as Record)[field.name]; + const common = { + required: !!field.required, + ...(field.minimum !== undefined && field.minimum !== null + ? { minimum: Number(field.minimum) } + : {}), + ...(field.maximum !== undefined && field.maximum !== null + ? { maximum: Number(field.maximum) } + : {}), + ...(field.options ? { options: field.options } : {}), + }; + + nextArgs[field.name] = + legacyField && + typeof legacyField === "object" && + !Array.isArray(legacyField) + ? { + ...legacyField, + ...common, + } + : common; + } + + return { + ...(legacyYieldDto?.args?.[type] ?? {}), + args: nextArgs, + }; +}; + +const getRewardTokens = ({ + yieldDto, + legacyYieldDto, +}: { + yieldDto: YieldDto; + legacyYieldDto: LegacyYieldDto | null; +}) => { + if (legacyYieldDto?.metadata.rewardTokens?.length) { + return legacyYieldDto.metadata.rewardTokens; + } + + const seen = new Set(); + const derived = uniqTokens( + yieldDto.rewardRate?.components?.map((component) => component.token) ?? [] + ).filter((token) => { + const key = `${token.network}:${token.address?.toLowerCase() ?? ""}`; + + if (seen.has(key)) { + return false; + } + + seen.add(key); + return true; + }); + + if (derived.length) { + return derived; + } + + return legacyYieldDto?.metadata.rewardTokens; +}; + +const getMetadata = ({ + yieldDto, + legacyYieldDto, +}: { + yieldDto: YieldDto; + legacyYieldDto: LegacyYieldDto | null; +}): LegacyYieldDto["metadata"] => { + const fallbackMetadata = legacyYieldDto?.metadata; + const mechanics = yieldDto.mechanics; + const metadata = yieldDto.metadata; + const yieldTypeMap = { + staking: "staking", + restaking: "restaking", + lending: "lending", + vault: "vault", + liquidity_pool: "vault", + concentrated_liquidity_pool: "vault", + fixed_yield: "vault", + real_world_asset: "vault", + } as const; + const type = + fallbackMetadata?.type ?? + yieldTypeMap[(mechanics?.type ?? "vault") as keyof typeof yieldTypeMap] ?? + "vault"; + + const token = mapToken(yieldDto.token) ?? fallbackMetadata?.token; + const tokens = uniqTokens([ + ...(yieldDto.tokens ?? []), + ...(yieldDto.inputTokens ?? []), + ...(fallbackMetadata?.tokens ?? []), + ]); + const gasFeeToken = + mapToken(mechanics?.gasFeeToken) ?? fallbackMetadata?.gasFeeToken; + + return { + ...(fallbackMetadata ?? {}), + name: metadata?.name ?? fallbackMetadata?.name ?? "", + description: metadata?.description ?? fallbackMetadata?.description ?? "", + documentation: + metadata?.documentation ?? fallbackMetadata?.documentation ?? "", + logoURI: metadata?.logoURI ?? fallbackMetadata?.logoURI ?? "", + type, + token: + token ?? + fallbackMetadata?.token ?? + (mapToken(yieldDto.token) as LegacyTokenDto), + tokens: tokens.length ? tokens : fallbackMetadata?.tokens, + rewardTokens: getRewardTokens({ yieldDto, legacyYieldDto }), + rewardSchedule: + mechanics?.rewardSchedule ?? fallbackMetadata?.rewardSchedule, + rewardClaiming: + mechanics?.rewardClaiming ?? fallbackMetadata?.rewardClaiming, + cooldownPeriod: + secondsToDays(mechanics?.cooldownPeriod?.seconds) ?? + fallbackMetadata?.cooldownPeriod, + warmupPeriod: + secondsToDays(mechanics?.warmupPeriod?.seconds) ?? + fallbackMetadata?.warmupPeriod, + gasFeeToken: + gasFeeToken ?? + fallbackMetadata?.gasFeeToken ?? + (mapToken(yieldDto.token) as LegacyTokenDto), + supportsLedgerWalletApi: + mechanics?.supportsLedgerWalletApi ?? + fallbackMetadata?.supportsLedgerWalletApi, + supportsMultipleValidators: + mechanics?.requiresValidatorSelection ?? + fallbackMetadata?.supportsMultipleValidators, + supportedStandards: + metadata?.supportedStandards ?? fallbackMetadata?.supportedStandards, + fee: { + enabled: !!mechanics?.fee || !!fallbackMetadata?.fee?.enabled, + depositFee: + !!mechanics?.fee?.deposit || !!fallbackMetadata?.fee?.depositFee, + managementFee: + !!mechanics?.fee?.management || !!fallbackMetadata?.fee?.managementFee, + performanceFee: + !!mechanics?.fee?.performance || + !!fallbackMetadata?.fee?.performanceFee, + }, + } as LegacyYieldDto["metadata"]; +}; + +export const adaptValidatorDto = ( + validatorDto: YieldValidatorDto | LegacyValidatorDto +): LegacyValidatorDto => { + const legacyValidator = validatorDto as LegacyValidatorDto; + const rewardRate = + "rewardRate" in validatorDto ? validatorDto.rewardRate : undefined; + const providerId = + "provider" in validatorDto + ? (validatorDto.provider?.id ?? validatorDto.providerId) + : validatorDto.providerId; + const image = + "logoURI" in validatorDto ? validatorDto.logoURI : legacyValidator.image; + const stakedBalance = + "tvl" in validatorDto ? validatorDto.tvl : legacyValidator.stakedBalance; + + return { + address: validatorDto.address, + apr: "apr" in validatorDto ? validatorDto.apr : rewardRate?.total, + commission: validatorDto.commission, + image, + minimumStake: validatorDto.minimumStake, + name: validatorDto.name, + nominatorCount: validatorDto.nominatorCount, + preferred: validatorDto.preferred, + pricePerShare: validatorDto.pricePerShare, + providerId, + remainingPossibleStake: validatorDto.remainingPossibleStake, + remainingSlots: validatorDto.remainingSlots, + stakedBalance, + status: validatorDto.status as LegacyValidatorDto["status"], + subnetId: validatorDto.subnetId, + subnetName: + "subnetName" in validatorDto ? validatorDto.subnetName : undefined, + tokenSymbol: validatorDto.tokenSymbol, + votingPower: validatorDto.votingPower, + website: validatorDto.website, + }; +}; + +export const adaptYieldDto = ({ + yieldDto, + legacyYieldDto, +}: { + yieldDto: YieldDto; + legacyYieldDto: LegacyYieldDto | null; +}): LegacyYieldDto => { + const { validators: _legacyValidators, ...legacyYieldDtoWithoutValidators } = + legacyYieldDto ?? {}; + const rewardRate = + yieldDto.rewardRate?.total ?? legacyYieldDto?.rewardRate ?? 0; + const tokens = uniqTokens([ + ...(yieldDto.tokens ?? []), + ...(yieldDto.inputTokens ?? []), + ...(legacyYieldDto?.tokens ?? []), + ]); + + const token = + mapToken(yieldDto.token) ?? + legacyYieldDto?.token ?? + (tokens[0] as LegacyTokenDto); + + return { + ...legacyYieldDtoWithoutValidators, + id: yieldDto.id, + token, + tokens: tokens.length ? tokens : (legacyYieldDto?.tokens ?? [token]), + metadata: getMetadata({ yieldDto, legacyYieldDto }), + rewardRate, + rewardType: getRewardType({ yieldDto, legacyYieldDto }), + status: { + ...(legacyYieldDto?.status ?? {}), + ...(yieldDto.status ?? {}), + }, + args: { + ...(legacyYieldDto?.args ?? {}), + enter: getArgumentConfig(yieldDto, legacyYieldDto, "enter"), + ...(yieldDto.mechanics?.arguments?.exit || legacyYieldDto?.args?.exit + ? { + exit: getArgumentConfig(yieldDto, legacyYieldDto, "exit"), + } + : {}), + } as LegacyYieldDto["args"], + feeConfigurations: legacyYieldDto?.feeConfigurations ?? [], + apy: rewardRate, + inputTokens: yieldDto.inputTokens, + outputToken: yieldDto.outputToken, + network: yieldDto.network, + chainId: yieldDto.chainId, + mechanics: yieldDto.mechanics, + statistics: yieldDto.statistics, + risk: yieldDto.risk, + providerId: yieldDto.providerId, + tags: yieldDto.tags, + state: yieldDto.state, + curator: yieldDto.curator, + } as unknown as LegacyYieldDto; +}; + +const getCurrentStepIndex = (transactions: LegacyTransactionDto[]) => { + const idx = transactions.findIndex( + (transaction) => + transaction.status !== "CONFIRMED" && transaction.status !== "SKIPPED" + ); + + if (idx >= 0) { + return idx; + } + + return Math.max(transactions.length - 1, 0); +}; + +export const toActionInputToken = ({ + inputToken, + yieldDto, + inputTokenValue, +}: { + inputToken?: LegacyTokenDto; + yieldDto?: LegacyYieldDto | null; + inputTokenValue?: string | null; +}) => { + if (inputToken) { + return inputToken; + } + + if (!yieldDto) { + return undefined; + } + + if (!inputTokenValue) { + return yieldDto.token ?? yieldDto.tokens?.[0]; + } + + const needle = toLower(inputTokenValue); + + return ( + [ + yieldDto.token, + ...(yieldDto.tokens ?? []), + ...(yieldDto.metadata.tokens ?? []), + ].find((token) => { + const address = token.address ? toLower(token.address) : null; + return ( + address === needle || + token.symbol.toLowerCase() === needle || + (needle === NATIVE_TOKEN_PLACEHOLDER && !token.address) + ); + }) ?? + yieldDto.token ?? + yieldDto.tokens?.[0] + ); +}; + +export const adaptTransactionDto = ({ + transactionDto, + gasFeeToken, + stakeId, +}: { + transactionDto: YieldTransactionDto; + gasFeeToken?: LegacyTokenDto; + stakeId: string; +}): LegacyTransactionDto => ({ + id: transactionDto.id, + accountAddresses: undefined, + annotatedTransaction: (transactionDto.annotatedTransaction ?? + {}) as unknown as LegacyTransactionDto["annotatedTransaction"], + broadcastedAt: transactionDto.broadcastedAt, + createdAt: transactionDto.createdAt, + error: transactionDto.error ?? null, + explorerUrl: transactionDto.explorerUrl ?? null, + gasEstimate: parseGasEstimate(transactionDto.gasEstimate, gasFeeToken), + hash: transactionDto.hash, + isMessage: transactionDto.isMessage ?? false, + ledgerHwAppId: null, + network: transactionDto.network as LegacyTransactionDto["network"], + signedTransaction: transactionDto.signedTransaction, + stakeId, + status: transactionDto.status as LegacyTransactionDto["status"], + stepIndex: transactionDto.stepIndex ?? 0, + structuredTransaction: (transactionDto.structuredTransaction ?? + {}) as unknown as LegacyTransactionDto["structuredTransaction"], + type: transactionDto.type as LegacyTransactionDto["type"], + unsignedTransaction: + typeof transactionDto.unsignedTransaction === "string" + ? transactionDto.unsignedTransaction + : transactionDto.unsignedTransaction + ? JSON.stringify(transactionDto.unsignedTransaction) + : null, +}); + +const parseGasEstimate = ( + gasEstimate: YieldTransactionDto["gasEstimate"], + gasFeeToken?: LegacyTokenDto +): LegacyTransactionDto["gasEstimate"] => { + if (!gasEstimate) { + return null; + } + + try { + const parsed = JSON.parse(gasEstimate) as EncodedGasEstimate | null; + + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + if (!gasFeeToken) { + return null; + } + + return { + amount: gasEstimate, + token: gasFeeToken, + }; + } + + const amount = parsed.amount ?? null; + const token = mapToken(parsed.token) ?? gasFeeToken; + + if (!amount || !token) { + return null; + } + + return { + amount, + token, + ...(parsed.gasLimit ? { gasLimit: parsed.gasLimit } : {}), + }; + } catch { + if (!gasFeeToken) { + return null; + } + + return { + amount: gasEstimate, + token: gasFeeToken, + }; + } +}; + +export const adaptActionDto = ({ + actionDto, + addresses, + gasFeeToken, + inputToken, + yieldDto, +}: { + actionDto: YieldActionDto; + addresses?: AddressesDto; + gasFeeToken?: LegacyTokenDto; + inputToken?: LegacyTokenDto; + yieldDto?: LegacyYieldDto | null; +}): LegacyActionDto => { + const adaptedTransactions = actionDto.transactions.map((transactionDto) => + adaptTransactionDto({ + transactionDto, + gasFeeToken: + gasFeeToken ?? yieldDto?.metadata.gasFeeToken ?? yieldDto?.token, + stakeId: actionDto.yieldId, + }) + ); + + const rawArguments = actionDto.rawArguments; + const validatorAddresses = + rawArguments?.validatorAddresses ?? + (rawArguments?.validatorAddress ? [rawArguments.validatorAddress] : null); + + return { + id: actionDto.id, + accountAddresses: undefined, + addresses: addresses ?? { address: actionDto.address }, + amount: actionDto.amount, + completedAt: actionDto.completedAt, + createdAt: actionDto.createdAt, + currentStepIndex: getCurrentStepIndex(adaptedTransactions), + inputToken: toActionInputToken({ + inputToken, + yieldDto, + inputTokenValue: rawArguments?.inputToken, + }), + integrationId: actionDto.yieldId, + projectId: null, + status: actionDto.status, + tokenId: rawArguments?.tokenId ?? null, + transactions: adaptedTransactions, + type: actionDto.type as LegacyActionDto["type"], + USDAmount: actionDto.amountUsd, + validatorAddress: rawArguments?.validatorAddress ?? null, + validatorAddresses, + }; +}; diff --git a/packages/widget/src/providers/yield-api-client-provider/index.tsx b/packages/widget/src/providers/yield-api-client-provider/index.tsx index 42b22534..b3bb82df 100644 --- a/packages/widget/src/providers/yield-api-client-provider/index.tsx +++ b/packages/widget/src/providers/yield-api-client-provider/index.tsx @@ -1,10 +1,15 @@ +import type { i18n } from "i18next"; 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 { useTranslation } from "react-i18next"; +import { waitForDelayedApiRequests } from "../../common/delay-api-requests"; import { config } from "../../config"; +import { handleGeoBlockResponse } from "../../hooks/use-geo-block"; +import { handleRichErrorResponse } from "../../hooks/use-rich-errors"; import type { paths } from "../../types/yield-api-schema"; import { useSettings } from "../settings"; @@ -13,23 +18,77 @@ const QueryContext = createContext | undefined>( ); const FetchContext = createContext | undefined>(undefined); +export const createYieldApiFetchClient = ({ + apiKey, + i18n, + url, +}: { + apiKey: string; + i18n?: i18n; + url: string; +}) => { + const client = createFetchClient({ + baseUrl: url, + headers: { + "x-api-key": apiKey, + }, + }); + + client.use({ + onResponse: async ({ request, response }) => { + await waitForDelayedApiRequests(); + + if (!response.ok) { + const data = await readYieldErrorResponse(response); + + handleGeoBlockResponse({ + data, + status: response.status, + }); + + if (i18n) { + handleRichErrorResponse({ + data, + i18n, + url: request.url, + }); + } + } + + return response; + }, + }); + + return client; +}; + +const readYieldErrorResponse = async (response: Response) => { + const text = await response.clone().text(); + + if (!text) { + return undefined; + } + + try { + return JSON.parse(text); + } catch { + return text; + } +}; + export const YieldApiClientProvider = ({ children }: PropsWithChildren) => { const { apiKey, yieldsApiUrl } = useSettings(); + const { i18n } = useTranslation(); const url = yieldsApiUrl ?? config.env.yieldsApiUrl; const clients = useMemo(() => { - const fetchClient = createFetchClient({ - baseUrl: url, - headers: { - "x-api-key": apiKey, - }, - }); + const fetchClient = createYieldApiFetchClient({ apiKey, i18n, url }); return { fetchClient, queryClient: createClient(fetchClient), }; - }, [apiKey, url]); + }, [apiKey, i18n, url]); return ( diff --git a/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts new file mode 100644 index 00000000..6fbe7630 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts @@ -0,0 +1,34 @@ +import type { AddressWithTokenDtoAdditionalAddresses } from "@stakekit/api-hooks"; +import type { YieldActionArgumentsDto } from "./types"; + +export const withAdditionalAddresses = ({ + additionalAddresses, + argumentsDto, +}: { + additionalAddresses: + | AddressWithTokenDtoAdditionalAddresses + | null + | undefined; + argumentsDto: YieldActionArgumentsDto; +}) => + ({ + ...argumentsDto, + ...(additionalAddresses ?? {}), + }) satisfies YieldActionArgumentsDto; + +export const getResponseData = async < + TResponse extends { + data?: unknown; + error?: unknown; + }, +>( + promise: Promise +): Promise> => { + const response = await promise; + + if (response.data !== undefined && response.data !== null) { + return response.data; + } + + throw response.error ?? new Error("Yield API request failed"); +}; diff --git a/packages/widget/src/providers/yield-api-client-provider/types.ts b/packages/widget/src/providers/yield-api-client-provider/types.ts index 9f5a3ec3..6ec565cf 100644 --- a/packages/widget/src/providers/yield-api-client-provider/types.ts +++ b/packages/widget/src/providers/yield-api-client-provider/types.ts @@ -1,5 +1,6 @@ import type { components } from "../../types/yield-api-schema"; +export type YieldDto = components["schemas"]["YieldDto"]; export type YieldBalancesRequestDto = components["schemas"]["BalancesRequestDto"]; export type YieldSingleBalancesRequestDto = @@ -8,10 +9,18 @@ export type YieldBalanceType = components["schemas"]["BalanceType"]; export type YieldPendingActionDto = components["schemas"]["PendingActionDto"]; export type YieldTokenDto = components["schemas"]["TokenDto"]; +export type YieldRewardRateDto = components["schemas"]["RewardRateDto"]; +export type YieldActionArgumentsDto = + components["schemas"]["ActionArgumentsDto"]; +export type YieldCreateActionDto = components["schemas"]["CreateActionDto"]; +export type YieldCreateManageActionDto = + components["schemas"]["CreateManageActionDto"]; export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; export type YieldBalanceDto = components["schemas"]["BalanceDto"]; +export type YieldActionDto = components["schemas"]["ActionDto"]; +export type YieldTransactionDto = components["schemas"]["TransactionDto"]; export type YieldBalancesByYieldDto = components["schemas"]["YieldBalancesDto"]; export type YieldSingleBalancesResponseDto = @@ -19,3 +28,5 @@ export type YieldSingleBalancesResponseDto = export type YieldBalancesResponseDto = components["schemas"]["BalancesResponseDto"]; +export type YieldPaginatedResponseDto = + components["schemas"]["PaginatedResponseDto"]; diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index 4b375bc8..b7f2f888 100644 --- a/packages/widget/tests/fixtures/index.ts +++ b/packages/widget/tests/fixtures/index.ts @@ -7,6 +7,11 @@ import { getYieldV2ControllerGetYieldByIdResponseMock, } from "@stakekit/api-hooks/msw"; import { Just } from "purify-ts"; +import type { + YieldActionArgumentsDto, + YieldActionDto, + YieldTransactionDto, +} from "../../src/providers/yield-api-client-provider/types"; const apyFaker = () => faker.number.float({ min: 0, max: 0.05 }); @@ -35,6 +40,12 @@ export const yieldFixture = (overrides?: Partial) => ) .unsafeCoerce(); +export const yieldValidatorsFixture = (validators?: YieldDto["validators"]) => + (validators ?? yieldFixture().validators).map((validator) => ({ + ...validator, + apr: validator.apr ?? apyFaker(), + })); + export const enterResponseFixture = (overrides?: Partial) => ({ ...getActionControllerEnterResponseMock(), ...overrides, @@ -51,3 +62,66 @@ export const pendingActionFixture = (overrides?: Partial) => ({ ...getActionControllerPendingResponseMock(), ...overrides, }); + +export const yieldApiTransactionFixture = ( + tx: TransactionDto, + overrides?: Partial +) => + ({ + id: tx.id || faker.string.uuid(), + title: tx.type.replaceAll("_", " "), + network: tx.network, + status: tx.status, + type: tx.type, + gasEstimate: tx.gasEstimate?.amount ?? null, + stepIndex: tx.stepIndex, + unsignedTransaction: tx.unsignedTransaction ?? undefined, + signedTransaction: tx.signedTransaction ?? undefined, + explorerUrl: tx.explorerUrl ?? undefined, + hash: tx.hash ?? undefined, + isMessage: tx.isMessage, + createdAt: tx.createdAt, + broadcastedAt: tx.broadcastedAt ?? undefined, + error: tx.error ?? undefined, + annotatedTransaction: (tx.annotatedTransaction ?? null) as never, + structuredTransaction: (tx.structuredTransaction ?? null) as never, + ...overrides, + }) as YieldTransactionDto; + +export const yieldApiActionFixture = ({ + action, + address, + rawArguments, + transactions, + overrides, +}: { + action: ActionDto; + address: string; + rawArguments?: YieldActionArgumentsDto | null; + transactions?: YieldTransactionDto[]; + overrides?: Partial; +}) => + ({ + id: action.id, + intent: + action.type === "STAKE" + ? "enter" + : action.type === "UNSTAKE" + ? "exit" + : "manage", + type: action.type, + yieldId: action.integrationId, + address, + amount: action.amount, + amountRaw: action.amount, + amountUsd: action.USDAmount, + transactions: + transactions ?? + action.transactions.map((tx) => yieldApiTransactionFixture(tx)), + executionPattern: "synchronous", + rawArguments: rawArguments ?? null, + createdAt: action.createdAt, + completedAt: action.completedAt, + status: action.status, + ...overrides, + }) as YieldActionDto; diff --git a/packages/widget/tests/use-cases/__screenshots__/sk-wallet.test.tsx/SK-Wallet-should-work-with-ton-external-provider-1.png b/packages/widget/tests/use-cases/__screenshots__/sk-wallet.test.tsx/SK-Wallet-should-work-with-ton-external-provider-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2506470c187b92a366cbbe259b5bd3508ca2e9aa GIT binary patch literal 2802 zcmeAS@N?(olHy`uVBq!ia0y~yU}^y33mi;9k$%OnW(EeX<(@8%Ar*7pTs355FyJ__ zLH5V?Gcgy`4JY2b%g%6T7CXZnVFLyh79IvhMrH?x0}KKJqukNZ7)=bLd116D7%d4$ zOTy8TaI_>GEeUB|5;pQPF#P}jVbZN8V9S*a)MRC5c+Sdax8(5yMW6_Sr>mdKI;Vst E0BSEwqyPW_ literal 0 HcmV?d00001 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 69028ec8..035fee20 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -2,8 +2,15 @@ import type { TokenDto, YieldBalanceDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { vitest } from "vitest"; +import type { YieldCreateManageActionDto } from "../../../src/providers/yield-api-client-provider/types"; import { waitForMs } from "../../../src/utils"; -import { pendingActionFixture, yieldFixture } from "../../fixtures"; +import { + pendingActionFixture, + yieldApiActionFixture, + yieldApiTransactionFixture, + yieldFixture, + yieldValidatorsFixture, +} from "../../fixtures"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; import { setUrl as _setUrl } from "./utils"; @@ -46,6 +53,24 @@ export const setup = async (opts?: { ) .unsafeCoerce(); + const avaxLiquidStakingValidators: YieldDto["validators"] = + opts?.withValidatorAddressesRequired + ? [ + { + address: "0xe92b7ba8497486e94bb59c51f595b590c4a5f894", + status: "active", + name: "Stakely", + image: "https://assets.stakek.it/validators/stakely.png", + website: "https://stakely.io/", + apr: 0.0393, + commission: 0.1, + stakedBalance: "2263157", + votingPower: 0.0090962642447408, + preferred: true, + }, + ] + : []; + const avaxLiquidStaking = Just(yieldFixture()) .map( (def) => @@ -76,22 +101,7 @@ export const setup = async (opts?: { }, ], }, - validators: opts?.withValidatorAddressesRequired - ? [ - { - address: "0xe92b7ba8497486e94bb59c51f595b590c4a5f894", - status: "active", - name: "Stakely", - image: "https://assets.stakek.it/validators/stakely.png", - website: "https://stakely.io/", - apr: 0.0393, - commission: 0.1, - stakedBalance: "2263157", - votingPower: 0.0090962642447408, - preferred: true, - }, - ] - : [], + validators: avaxLiquidStakingValidators, }) satisfies YieldDto ) .unsafeCoerce(); @@ -206,6 +216,22 @@ export const setup = async (opts?: { await delay(); return HttpResponse.json(avaxLiquidStaking); }), + http.get("*/v1/yields/:yieldId/validators", async (info) => { + await delay(); + + const yieldId = info.params.yieldId as string; + const validators = + yieldId === avaxLiquidStaking.id + ? yieldValidatorsFixture(avaxLiquidStakingValidators) + : yieldId === avaxNativeStaking.id + ? yieldValidatorsFixture(avaxNativeStaking.validators) + : []; + + return HttpResponse.json({ + items: validators, + total: validators.length, + }); + }), http.post("*/v1/yields/balances/scan", async () => { await delay(); return HttpResponse.json([ @@ -241,38 +267,55 @@ export const setup = async (opts?: { balances: avaxLiquidStakingBalancesV2, }); }), - http.post("*/v1/actions/pending", async (info) => { - const data = (await info.request.json()) as { integrationId: string }; + http.post("*/v1/actions/manage", async (info) => { + const data = (await info.request.json()) as YieldCreateManageActionDto; await delay(); + return HttpResponse.json({ - ...pendingAction, - integrationId: data.integrationId, - } satisfies typeof pendingAction); + ...yieldApiActionFixture({ + action: pendingAction, + address: data.address, + rawArguments: data.arguments ?? null, + transactions: [ + yieldApiTransactionFixture(pendingAction.transactions[0], { + type: "CLAIM_REWARDS", + status: "CREATED", + unsignedTransaction: + '{"from":"0xcaA141ece9fEE66D15f0257F5c6C48E26784345C","gasLimit":"0x0193e0","to":"0x7D2382b1f8Af621229d33464340541Db362B4907","data":"0x00f714ce00000000000000000000000000000000000000000000000000037cb07e6e4276000000000000000000000000caa141ece9fee66d15f0257f5c6c48e26784345c","nonce":89,"type":2,"maxFeePerGas":"0xbfa6de","maxPriorityFeePerGas":"0x0f4240","chainId":43114}', + }), + ], + overrides: { + yieldId: data.yieldId, + }, + }), + }); }), - http.patch("*/v1/transactions/:transactionId", async (info) => { + http.put("*/v1/transactions/:transactionId/submit-hash", async (info) => { await delay(); const transactionId = info.params.transactionId as string; return HttpResponse.json({ - ...pendingAction.transactions[0], - type: "CLAIM_REWARDS", - status: "WAITING_FOR_SIGNATURE", - id: transactionId, - unsignedTransaction: - '{"from":"0xcaA141ece9fEE66D15f0257F5c6C48E26784345C","gasLimit":"0x0193e0","to":"0x7D2382b1f8Af621229d33464340541Db362B4907","data":"0x00f714ce00000000000000000000000000000000000000000000000000037cb07e6e4276000000000000000000000000caa141ece9fee66d15f0257f5c6c48e26784345c","nonce":89,"type":2,"maxFeePerGas":"0xbfa6de","maxPriorityFeePerGas":"0x0f4240","chainId":43114}', + ...yieldApiTransactionFixture(pendingAction.transactions[0], { + type: "CLAIM_REWARDS", + status: "BROADCASTED", + id: transactionId, + hash: "transaction_hash", + }), }); }), - http.post("*/v1/transactions/:transactionId/submit_hash", async () => { - await delay(1000); - return new HttpResponse(null, { status: 201 }); - }), - http.get("*/v1/transactions/:transactionId/status", async () => { + http.get("*/v1/transactions/:transactionId", async (info) => { + const transactionId = info.params.transactionId as string; return HttpResponse.json({ - url: "https://snowtrace.dev/tx/0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", - network: avaxLiquidStaking.token.network, - hash: "0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", - status: "CONFIRMED", + ...yieldApiTransactionFixture(pendingAction.transactions[0], { + id: transactionId, + type: "CLAIM_REWARDS", + explorerUrl: + "https://snowtrace.dev/tx/0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", + network: avaxLiquidStaking.token.network, + hash: "0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", + status: "CONFIRMED", + }), }); }) ); diff --git a/packages/widget/tests/use-cases/external-provider/setup.ts b/packages/widget/tests/use-cases/external-provider/setup.ts index 40767911..706816ec 100644 --- a/packages/widget/tests/use-cases/external-provider/setup.ts +++ b/packages/widget/tests/use-cases/external-provider/setup.ts @@ -1,7 +1,7 @@ import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; -import { yieldFixture } from "../../fixtures"; +import { yieldFixture, yieldValidatorsFixture } from "../../fixtures"; import { worker } from "../../mocks/worker"; export const setup = () => { @@ -49,6 +49,7 @@ export const setup = () => { id: "avalanche-avax-native-staking", token: avalancheCToken, tokens: [avalancheCToken], + validators: [], metadata: { ...val.metadata, type: "staking", @@ -66,6 +67,7 @@ export const setup = () => { id: "ethereum-eth-etherfi-staking", token: ether, tokens: [ether], + validators: [], metadata: { ...val.metadata, type: "staking", @@ -83,6 +85,7 @@ export const setup = () => { id: "solana-sol-native-staking", token: solanaToken, tokens: [solanaToken], + validators: [], metadata: { ...val.metadata, type: "staking", @@ -100,6 +103,7 @@ export const setup = () => { id: "ton-native-staking", token: tonToken, tokens: [tonToken], + validators: [], metadata: { ...val.metadata, type: "staking", @@ -179,6 +183,25 @@ export const setup = () => { await delay(); return HttpResponse.json(tonNativeStaking); + }), + http.get("*/v1/yields/:yieldId/validators", async (info) => { + await delay(); + + const yieldId = info.params.yieldId as string; + const validatorsByYieldId = new Map([ + [etherNativeStaking.id, etherNativeStaking.validators], + [avalancheAvaxNativeStaking.id, avalancheAvaxNativeStaking.validators], + [solanaNativeStaking.id, solanaNativeStaking.validators], + [tonNativeStaking.id, tonNativeStaking.validators], + ]); + const validators = yieldValidatorsFixture( + validatorsByYieldId.get(yieldId) ?? [] + ); + + return HttpResponse.json({ + items: validators, + total: validators.length, + }); }) ); }; diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-1.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-1.png new file mode 100644 index 0000000000000000000000000000000000000000..3366fc034499378d6df81243adf81edef6b32888 GIT binary patch literal 27548 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

Q*KPv>H{_QIOKyxu)jbs zjHp1Y@w9sQR7v4(RAgBy-hn!CK(O$HnANtI2kj)8zVt(diaJrn8d#Or7{g77kHIo6 z5OxWp2^RHl{iXJcbWzmW;xu+vo&MCsH8;4lu~@4_O*CmVAsJZ#BpR_6=oWm`?CZhN zk17@Pvuhg{?~yrbMt6Ybg*-ufPKzr+8ieVQ>!v3c47Z#R>ya`-ZBONM%pHuVgl^JC z-q{plD{6tLukCd2Iwirvm#AMv?p@OBpYYwh#mYffVe25ho-l-|Ax2{A`YToDE8AYr zi?kC>06fN;Ket~xdUNH{5cy?#ghgEDCoBFQ;s))=tUY#;sS89bsUVHzC6)DC&Sy{D zA&7A&t51TqFzcLV_g2~1Ed6A!%{EvXBY!N7&OnU_*x8)mNrMO!kLHrLVkSh~mYjKA zs@wAd*}InK=dAZO>4XStuw21G8>OMark@`(_kLKb2&kIWy2AyhPF6ZGvP zv=K!v!cZP{c_iRVl|apU`5x2)HUl4WZj%4|*5B^r_CJJx!sLIU4dcJCwEtq^>i?S` z$9MvG1^QuOuy_f~HNpWhO)#k+jjlOAViPWFc~G+l&Jp%$4BUTkNJwL2W0IQx2s0=j4jj9*{?e8E<7%}cvDWm`r$w>-d$7g1HCOSw@OY?pYyfpe$znKcOM ztZ^!u>FBhADpCNQv%JOU;P_<+x1k^R(WC9WHM;C;AeH3PRVx>U(1?H#v-la88qSwT zsE+SnR6u#zPiJ%cF>)(A*#0gxh_E&$0H!12B8SpJLC}l&hQl3Jl%Ud zcdYg0;fI?JH*b!g>Ns}_syj#~Aws|Y3VfE@kJZ(qUUI#?ZNTb&sINCcY>i|EaGXLd z<(d>|%;EtlfFNTXU`%N9Xt{X-xRY1wWPCs!apJjblR0>~rRo;To^?Y~>I zQ3Y&nC?Zmqrpz|B1D2Bi2~H}qAM7-lU%!6Unt_)?98o`pXsrq}jB+ak42CQ27i9Ru z3xHb#GeC_S|0cG5lQl}7MDxf5ca+{8Dq(jZ18)Oj#WOz|&x$(;38oz{4_|~fLBj0J z%nZDdDWXf5gYV6!w5xkIhstoI@AV69u?o<__niA7$lgEULXmP4RPaSA$>r>r(|&YK zY_;>KYE<4+$6;Ui&9CPJN2;MT^BZl#0OA%bAS+}+_t~Oc2;$K)UnxTytF2HCV~tvr z&zv!iD&{)m_*+fDem0lg0i`rb?!bcsGV}y!$2RTtLO(M|JcSTej5WGU66tm0$}w#Q zYpsC`@`-afy@v zPR^G_JFRQ@pc*T4H#LSGkXebLg_%<`jjqkeHrG64U^(aq=2pHp4PN1)s|C#n4x z^QIVi`-pWi_R9P;;M|q(kQ86DPby27Ek>{dHNOMnxqprDtxzLWgc;A~5~s7BmcdWK zAo+SnVn=Ig(yqze9jzKJ^CrH5<){s}xq+=Fyfu$X1evhE#B~DX4Y~+xGTMsmLR9#a zp?e^7*lD2fYB!#>Na3|Y{1joY4IdvLxK`{|R=%F|&dh~=u)z-$Ig;87G_Y!GGZK zPgf^dxfSWI;)JxU_yNIsp;qB7oCh_%JtW9%AEuU*&oF&`eW`lA!X99OaDe~Oe|jK< zGeQ?{a%f(JWl`Yb07U={-80O|EsnI9yu9CGf8g9_1pLj9lLrrlLUeVYHYe0UUE=DK z-IB5h`})z*YFNpC><|%DvehW(OCz_bR+CaU+dqEt>SD!ry{FcP+St)maAExIh4P>y zyG}F+2gDr*u?iLx#id0ez_kf1j}ZRm;H_yBJa`Z?XG;J#OiL!e`hqgtm99NVaqwcy z7{GrU09pr(ZiW#0eTY3d0o}>oF}TK#NE>DM0l1f|2?7=a+QET=kAzkwV}#Q|(cU8G zQ9lrq07n!e_L_Ds=fJQ6RT+?W%HtYS{;|HkS6Y7zdc9#(e8yFBI3PS5$aP=vpUN0z zpYdJuFgG6p)=U<`u8}+_B0`)2t@Ivn_T0M;zEN_&&0BJiUzib{K8^qiQ{bQRIu@t9KOly zW<%NPL%b!ETzn$(A32?FfHDCQw;>2_g5Am=W=ngSl*hk^Ay_swthP@Tb?xliXl7(Py9T|Xg05&rd6Sn$eH^mk?;6Q`! zgcw|{G_KXMs^xUL50&OLXu=|nSA1_j z3q~b~-Oj-Xoep|o�xQrX~PX93$Dd?Uhsq%$uld(J~ml@4zsN2zn4cpn`l=Rv0*> zQTm-1z9uIlvkye?tM+Ml)cQWCyxWt2oz}smXs8$C{JLs&$^C3GU@_$AFKj7a4hMvyIPCQv3&gr9n{7`_Xr+B98g*t`hv6_q-u-9y4S z!^3Wp)eBJR2n6ph?7t&mdJTjZRCX~;)zh43s%QC4$M}Y>!%1|#HLH{QV;-8rkeFN_ zfM8HvKl!@32O@`A?S|iuzKS%%u7Vj}f(#E_S#+>o)HBmM(8MEsVhOdFYGO9Zk91(c zm-J2_=LkXv>LW3kG4ciktaJgTAr4>WD4t^BM&NYK69RnJ8LVOB&?@4pKF01^uVo2IpdF$VvcFk$i*WYVV)tcDE)F+( z{=kqAZZt^v<*(;+mpylJ-wU_KR9-FaUUx2{6hIkzvGDo;DYHi!dkMs^Y@PtWQWRdCA| zkuS}m4Dp!QrGPc5`LYVH?tIOKm12!&^f;{cc$rT2|~Jb85Z=U!`z<)z^co|IL-jrtB% zt8D?rZ{EB?`5iIVMw-8x|KL3u`|aD)^IVDUq!ibo5?64FUTd3vsFL*(or0%Mw(Dv+ zu$)T8dRI3hLY9}8;UFbQR}s z&~ikD3tY1m_r8t~0=p^F)$0~Ib;Rq5%+hL8I(Y+>b7f;Pn$$yc)UfV+ZP>I+QhN?m2>ev>t(kTdYfj>~s9rb@Wqx@6%-v8c{_kZCX{PW%akum+x8~2}@fc{+G z|E(J7&kOSB1^JJjtp9m2{1iYSe*SdgyP1oC9h(h`w;_B*w2d{(y&u58Ag*54yOevu HI`H2B8~$S@ literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-2.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-2.png new file mode 100644 index 0000000000000000000000000000000000000000..a59ceb285537b89f0e2adc66399b7a72a882e556 GIT binary patch literal 29975 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 literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-1.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-1.png new file mode 100644 index 0000000000000000000000000000000000000000..680029e043b39b6cdd92156ee7ff1278d8c466cb GIT binary patch literal 29451 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 literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-2.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-2.png new file mode 100644 index 0000000000000000000000000000000000000000..464e83397800a50b477c7d5fdd26c03260ccec77 GIT binary patch literal 29594 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 literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-3.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-3.png new file mode 100644 index 0000000000000000000000000000000000000000..5c34b0324d0e5e3c20f73276b424e02214f9805c GIT binary patch literal 29755 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 { id: "avalanche-avax-native-staking", token: avalancheCToken, tokens: [avalancheCToken], + validators: [], metadata: { ...val.metadata, type: "staking", @@ -80,6 +84,7 @@ export const setup = () => { id: "avalanche-c-usdc-aave-v3-lending", token: usdcToken, tokens: [usdcToken], + validators: [], metadata: { ...val.metadata, type: "staking", @@ -197,62 +202,51 @@ export const setup = () => { return HttpResponse.json(yieldWithDifferentGasAndStakeToken.yieldDto); } ), - http.post("*/v1/actions/enter", async (info) => { + http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); - const body = (await info.request.json()) as ActionRequestDto; + const yieldId = info.params.yieldId as string; + const validators = + yieldId === yieldWithSameGasAndStakeToken.yieldDto.id + ? yieldValidatorsFixture( + yieldWithSameGasAndStakeToken.yieldDto.validators + ) + : yieldValidatorsFixture( + yieldWithDifferentGasAndStakeToken.yieldDto.validators + ); return HttpResponse.json({ - ...(body.integrationId === yieldWithSameGasAndStakeToken.yieldDto.id - ? yieldWithSameGasAndStakeToken.actionDto - : yieldWithDifferentGasAndStakeToken.actionDto), - amount: body.args.amount, - } as ActionDto); - }), - http.patch("*/v1/transactions/:transactionId", async (info) => { - const transactionId = info.params.transactionId as string; - - const yieldWithAction = [ - yieldWithSameGasAndStakeToken, - yieldWithDifferentGasAndStakeToken, - ].find((val) => - val.actionDto.transactions.some((tx) => tx.id === transactionId) - ); - - if (!yieldWithAction) { - return new HttpResponse(null, { status: 400 }); - } - - const tx = yieldWithAction.actionDto.transactions.find( - (tx) => tx.id === transactionId - ); - - if (!tx) { - return new HttpResponse(null, { status: 400 }); - } - - await delay(); - - return HttpResponse.json({ - ...tx, - gasEstimate: { - token: yieldWithAction.yieldDto.token, - amount: - yieldsTxGasAmountMap.get( - `${yieldWithAction.yieldDto.id}-${tx.id}` - ) ?? "0", - }, - } satisfies TransactionDto); + items: validators, + total: validators.length, + }); }), - http.post("*/v1/actions/enter/estimate-gas", async (info) => { + http.post("*/v1/actions/enter", async (info) => { await delay(); - const body = (await info.request.json()) as ActionRequestDto; + const body = (await info.request.json()) as YieldCreateActionDto; + const selectedYield = + body.yieldId === yieldWithSameGasAndStakeToken.yieldDto.id + ? yieldWithSameGasAndStakeToken + : yieldWithDifferentGasAndStakeToken; + const gasAmount = yieldsTxGasAmountMap.get(body.yieldId) ?? "0"; return HttpResponse.json({ - amount: yieldsTxGasAmountMap.get(body.integrationId) ?? "0", - token: avalancheCToken, - gasLimit: "", + ...yieldApiActionFixture({ + action: selectedYield.actionDto as ActionDto, + address: body.address, + rawArguments: body.arguments ?? null, + transactions: selectedYield.actionDto.transactions.map((tx, index) => + yieldApiTransactionFixture(tx as TransactionDto, { + gasEstimate: gasAmount, + status: "CREATED", + stepIndex: index, + }) + ), + overrides: { + amount: body.arguments?.amount ?? null, + amountRaw: body.arguments?.amount ?? null, + }, + }), }); }) ); diff --git a/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-1.png b/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d069219e54f9d180e2e6f3288686c9d6ac3020ef GIT binary patch literal 29246 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КBsKhe1@W6apwwALk zXe7!jX3T_f1XPOR!*Vz8WvJ8HNmoopGmEHg&qW4byHl_7pR?uR**jB9OJx*hQzvs5 z=Nit6>k8!~CRMX3(r;Q}w;^lGq?mPJmAT|Lus`$-=>h7ty0@(7LyL*iG8R<{YTR#v z3q-vI4iu5nbQf;VMI62LLP%--!+Fo!yuLdv%e7=Ow;dNxJ)k|$2$oMt{8M*`csnGX zldG4;6r&2fN=&7kNJ(QWqfxCXd@;038Bs>da-!M33lsWlW@(c2S}?a7j#`mKb+}-M zOS;uZv8W59Uw=LI)T&V721J);!Ls~vw4t1<`u!>Fx>bJjHqc~}@AMm47oc-Fm%wnh zz0K)p&;As%OLG78f|B}K?c~Y#+7bm9L!YQh^L~4MhAW19@iC($W}o`KJyzbU5~e65 z4sjUFkpsTl4QYrwHT1Fi8A?9UQ_h$jyYm%z_0PviPlJ>vH$}ypK$V)~ph2o( zikIlA$vtYh?YuIkSChdMrdnkjn;6Q}3{bZxF(1(EG(+?IoM**lK4MsqXa@ait;Q@g zxH*_e@x(n-H%ZT7enT*#z7l0M8+-lx4pIgB>})0TRhmRB z@t6w-?O~PY3Rzn7>Fs9YA=_xjR}fQJJLwoncFF1lUp0bzG$|yFqln?=JfsK*BzZ2i zhIYm#tfd8EpY}&&M}Tt77?S$G)c<)Ta}n(uw}+CMf8FXy1{Q}*bk>v61SV|j&@-1g$g8y45yFz96^ zNq{|#BWjjC^8fg#tbaA?{4Eju?;5KAe1QL4f&W~A|6GB8aRm%j94-(DT+y6o3jP}( z_4l9i>)&_<=9CBo!q1=DpM`O&2#Yzliifbq;L^6l*iJJ3hj2pm)UgZ|(<}c26feV) literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-2.png b/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-2.png new file mode 100644 index 0000000000000000000000000000000000000000..d069219e54f9d180e2e6f3288686c9d6ac3020ef GIT binary patch literal 29246 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КBsKhe1@W6apwwALk zXe7!jX3T_f1XPOR!*Vz8WvJ8HNmoopGmEHg&qW4byHl_7pR?uR**jB9OJx*hQzvs5 z=Nit6>k8!~CRMX3(r;Q}w;^lGq?mPJmAT|Lus`$-=>h7ty0@(7LyL*iG8R<{YTR#v z3q-vI4iu5nbQf;VMI62LLP%--!+Fo!yuLdv%e7=Ow;dNxJ)k|$2$oMt{8M*`csnGX zldG4;6r&2fN=&7kNJ(QWqfxCXd@;038Bs>da-!M33lsWlW@(c2S}?a7j#`mKb+}-M zOS;uZv8W59Uw=LI)T&V721J);!Ls~vw4t1<`u!>Fx>bJjHqc~}@AMm47oc-Fm%wnh zz0K)p&;As%OLG78f|B}K?c~Y#+7bm9L!YQh^L~4MhAW19@iC($W}o`KJyzbU5~e65 z4sjUFkpsTl4QYrwHT1Fi8A?9UQ_h$jyYm%z_0PviPlJ>vH$}ypK$V)~ph2o( zikIlA$vtYh?YuIkSChdMrdnkjn;6Q}3{bZxF(1(EG(+?IoM**lK4MsqXa@ait;Q@g zxH*_e@x(n-H%ZT7enT*#z7l0M8+-lx4pIgB>})0TRhmRB z@t6w-?O~PY3Rzn7>Fs9YA=_xjR}fQJJLwoncFF1lUp0bzG$|yFqln?=JfsK*BzZ2i zhIYm#tfd8EpY}&&M}Tt77?S$G)c<)Ta}n(uw}+CMf8FXy1{Q}*bk>v61SV|j&@-1g$g8y45yFz96^ zNq{|#BWjjC^8fg#tbaA?{4Eju?;5KAe1QL4f&W~A|6GB8aRm%j94-(DT+y6o3jP}( z_4l9i>)&_<=9CBo!q1=DpM`O&2#Yzliifbq;L^6l*iJJ3hj2pm)UgZ|(<}c26feV) literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-3.png b/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-3.png new file mode 100644 index 0000000000000000000000000000000000000000..d069219e54f9d180e2e6f3288686c9d6ac3020ef GIT binary patch literal 29246 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КBsKhe1@W6apwwALk zXe7!jX3T_f1XPOR!*Vz8WvJ8HNmoopGmEHg&qW4byHl_7pR?uR**jB9OJx*hQzvs5 z=Nit6>k8!~CRMX3(r;Q}w;^lGq?mPJmAT|Lus`$-=>h7ty0@(7LyL*iG8R<{YTR#v z3q-vI4iu5nbQf;VMI62LLP%--!+Fo!yuLdv%e7=Ow;dNxJ)k|$2$oMt{8M*`csnGX zldG4;6r&2fN=&7kNJ(QWqfxCXd@;038Bs>da-!M33lsWlW@(c2S}?a7j#`mKb+}-M zOS;uZv8W59Uw=LI)T&V721J);!Ls~vw4t1<`u!>Fx>bJjHqc~}@AMm47oc-Fm%wnh zz0K)p&;As%OLG78f|B}K?c~Y#+7bm9L!YQh^L~4MhAW19@iC($W}o`KJyzbU5~e65 z4sjUFkpsTl4QYrwHT1Fi8A?9UQ_h$jyYm%z_0PviPlJ>vH$}ypK$V)~ph2o( zikIlA$vtYh?YuIkSChdMrdnkjn;6Q}3{bZxF(1(EG(+?IoM**lK4MsqXa@ait;Q@g zxH*_e@x(n-H%ZT7enT*#z7l0M8+-lx4pIgB>})0TRhmRB z@t6w-?O~PY3Rzn7>Fs9YA=_xjR}fQJJLwoncFF1lUp0bzG$|yFqln?=JfsK*BzZ2i zhIYm#tfd8EpY}&&M}Tt77?S$G)c<)Ta}n(uw}+CMf8FXy1{Q}*bk>v61SV|j&@-1g$g8y45yFz96^ zNq{|#BWjjC^8fg#tbaA?{4Eju?;5KAe1QL4f&W~A|6GB8aRm%j94-(DT+y6o3jP}( z_4l9i>)&_<=9CBo!q1=DpM`O&2#Yzliifbq;L^6l*iJJ3hj2pm)UgZ|(<}c26feV) literal 0 HcmV?d00001 diff --git a/packages/widget/tests/use-cases/staking-flow/setup.ts b/packages/widget/tests/use-cases/staking-flow/setup.ts index f3727aed..b4ac4d6b 100644 --- a/packages/widget/tests/use-cases/staking-flow/setup.ts +++ b/packages/widget/tests/use-cases/staking-flow/setup.ts @@ -1,6 +1,5 @@ import type { ActionDto, - ActionRequestDto, AddressesDto, TokenDto, TransactionDto, @@ -9,7 +8,13 @@ import type { import { delay, HttpResponse, http } from "msw"; import { avalanche } from "viem/chains"; import { vitest } from "vitest"; +import type { YieldCreateActionDto } from "../../../src/providers/yield-api-client-provider/types"; import { waitForMs } from "../../../src/utils"; +import { + yieldApiActionFixture, + yieldApiTransactionFixture, + yieldValidatorsFixture, +} from "../../fixtures"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; @@ -245,6 +250,20 @@ export const setup = async () => { await delay(); return HttpResponse.json(yieldOp); }), + http.get("*/v1/yields/:yieldId/validators", async (info) => { + await delay(); + + const yieldId = info.params.yieldId as string; + const validators = + yieldId === yieldOp.id + ? yieldValidatorsFixture(yieldOp.validators) + : []; + + return HttpResponse.json({ + items: validators, + total: validators.length, + }); + }), http.get("*/v1/transactions/gas/avalanche-c", async () => { await delay(); return HttpResponse.json({ @@ -286,46 +305,57 @@ export const setup = async () => { }, }); }), - http.post("*/v1/actions/enter/estimate-gas", async () => { - await delay(); - return HttpResponse.json({ - amount: "0.002828600000000000", - token: { - network: "polygon", - coinGeckoId: "matic-network", - name: "Polygon", - decimals: 18, - symbol: "MATIC", - logoURI: "https://assets.stakek.it/tokens/matic.svg", - }, - gasLimit: "", - }); - }), http.post("*/v1/actions/enter", async (info) => { await delay(); - const body = (await info.request.json()) as ActionRequestDto; + const body = (await info.request.json()) as YieldCreateActionDto; - return HttpResponse.json({ ...enterAction, amount: body.args.amount }); + return HttpResponse.json( + yieldApiActionFixture({ + action: enterAction, + address: body.address, + rawArguments: body.arguments ?? null, + transactions: [ + yieldApiTransactionFixture(transactionConstruct, { + id: enterAction.transactions[0].id, + status: "CREATED", + type: "STAKE", + gasEstimate: + transactionConstruct.gasEstimate?.amount ?? undefined, + stepIndex: 0, + }), + ], + overrides: { + amount: body.arguments?.amount ?? null, + amountRaw: body.arguments?.amount ?? null, + }, + }) + ); }), - http.patch("*/v1/transactions/:transactionId", async (info) => { + http.put("*/v1/transactions/:transactionId/submit-hash", async (info) => { const transactionId = info.params.transactionId as string; await delay(); - return HttpResponse.json({ ...transactionConstruct, id: transactionId }); - }), - http.post("*/v1/transactions/:transactionId/submit_hash", async () => { - await delay(1000); - return new HttpResponse(null, { status: 201 }); + return HttpResponse.json( + yieldApiTransactionFixture(transactionConstruct, { + id: transactionId, + hash: "transaction_hash", + status: "BROADCASTED", + }) + ); }), - http.get("*/v1/transactions/:transactionId/status", async () => { - return HttpResponse.json({ - url: "https://snowtrace.dev/tx/0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", - network: "avalanche-c", - hash: "0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", - status: "CONFIRMED", - }); + http.get("*/v1/transactions/:transactionId", async (info) => { + const transactionId = info.params.transactionId as string; + return HttpResponse.json( + yieldApiTransactionFixture(transactionConstruct, { + id: transactionId, + explorerUrl: + "https://snowtrace.dev/tx/0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", + hash: "0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", + status: "CONFIRMED", + }) + ); }) ); From 93df083f12ea7fa95f90942ba25daf726d00e2ed Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Fri, 20 Mar 2026 16:31:31 +0100 Subject: [PATCH 06/23] feat: campaign --- packages/widget/package.json | 4 +- .../molecules/reward-rate-breakdown/index.tsx | 86 ++ .../molecules/reward-token-details/index.tsx | 6 +- .../select-opportunity-list-item/index.tsx | 35 +- packages/widget/src/domain/types/positions.ts | 2 + .../widget/src/domain/types/reward-rate.ts | 86 ++ .../use-unstake-or-pending-action-params.ts | 4 +- .../widget/src/hooks/use-init-query-params.ts | 8 +- .../widget/src/hooks/use-position-balances.ts | 17 +- .../widget/src/hooks/use-positions-data.ts | 1 + .../components/position-details-actions.tsx | 4 +- .../src/pages/complete/pages/common.page.tsx | 16 +- .../select-yield-reward-details.tsx | 20 +- .../components/provider-details.tsx | 9 +- .../components/static-action-block.tsx | 7 +- .../hooks/use-position-details.ts | 9 + .../position-details.page.tsx | 47 +- .../pages/position-details/state/index.tsx | 4 +- .../src/pages/position-details/state/types.ts | 9 +- .../src/pages/position-details/state/utils.ts | 5 +- .../review/hooks/use-pending-review.hook.ts | 4 +- .../providers/pending-action-store/index.tsx | 4 +- .../yield-api-client-provider/compat.ts | 1 + .../yield-api-client-provider/types.ts | 4 + .../src/translation/English/translations.json | 18 +- .../src/translation/French/translations.json | 18 +- .../widget/src/types/yield-api-schema.d.ts | 818 +++++++++++++++++- packages/widget/tests/fixtures/index.ts | 38 + ...ould-work-with-ton-external-provider-1.png | Bin 2802 -> 0 bytes ...gas-token-Txs-gas---gas-token-amount-1.png | Bin 27548 -> 0 bytes ...gas-token-Txs-gas---gas-token-amount-2.png | Bin 29975 -> 0 bytes ...gas-token-Txs-gas---gas-token-amount-3.png | Bin 27461 -> 0 bytes ...gas-token-Txs-gas---gas-token-amount-1.png | Bin 29451 -> 0 bytes ...gas-token-Txs-gas---gas-token-amount-2.png | Bin 29594 -> 0 bytes ...gas-token-Txs-gas---gas-token-amount-3.png | Bin 29755 -> 0 bytes .../Staking-flow-Works-as-expected-1.png | Bin 29246 -> 0 bytes .../Staking-flow-Works-as-expected-2.png | Bin 29246 -> 0 bytes .../Staking-flow-Works-as-expected-3.png | Bin 29246 -> 0 bytes .../use-cases/trust-incentive-apy/setup.ts | 334 +++++++ .../trust-incentive-apy.test.tsx | 105 +++ 40 files changed, 1644 insertions(+), 79 deletions(-) create mode 100644 packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx create mode 100644 packages/widget/src/domain/types/reward-rate.ts delete mode 100644 packages/widget/tests/use-cases/__screenshots__/sk-wallet.test.tsx/SK-Wallet-should-work-with-ton-external-provider-1.png delete mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-1.png delete mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-2.png delete mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-3.png delete mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-1.png delete mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-2.png delete mode 100644 packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-3.png delete mode 100644 packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-1.png delete mode 100644 packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-2.png delete mode 100644 packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-3.png create mode 100644 packages/widget/tests/use-cases/trust-incentive-apy/setup.ts create mode 100644 packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx diff --git a/packages/widget/package.json b/packages/widget/package.json index 6d351456..3c085ea5 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -56,7 +56,7 @@ "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", - "gen:yield-api": "openapi-typescript https://docs.yield.xyz/openapi/appsyield-apiswagger-docsopenapi.yaml -o src/types/yield-api-schema.d.ts" + "gen:yield-api": "openapi-typescript https://api.stg.yield.xyz/docs.yaml -o src/types/yield-api-schema.d.ts" }, "peerDependencies": { "react": ">=18", @@ -164,4 +164,4 @@ "public" ] } -} \ No newline at end of file +} diff --git a/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx b/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx new file mode 100644 index 00000000..f629003b --- /dev/null +++ b/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx @@ -0,0 +1,86 @@ +import { useTranslation } from "react-i18next"; +import { + getRewardRateBreakdown, + type RewardRateBreakdownItem, +} from "../../../domain/types/reward-rate"; +import type { YieldRewardRateDto } from "../../../providers/yield-api-client-provider/types"; +import { getRewardRateFormatted } from "../../../utils/formatters"; +import { Box } from "../../atoms/box"; +import { Text } from "../../atoms/typography/text"; + +const getLabelKey = (key: RewardRateBreakdownItem["key"]) => { + switch (key) { + case "native": + return "details.apy_composition.native"; + case "protocol_incentive": + return "details.apy_composition.protocol_incentive"; + case "campaign": + return "details.apy_composition.campaign"; + } +}; + +export const RewardRateBreakdown = ({ + rewardRate, + showUpToCampaign = false, + title, + testId, +}: { + rewardRate: YieldRewardRateDto | null | undefined; + showUpToCampaign?: boolean; + title?: string; + testId?: string; +}) => { + const { t } = useTranslation(); + + const items = getRewardRateBreakdown(rewardRate, { + showUpToCampaign, + }); + + if (!items.length) { + return null; + } + + return ( + + {title ? ( + {title} + ) : null} + + {items.map((item) => { + const value = getRewardRateFormatted({ + rewardRate: item.rate, + rewardType: item.rewardType, + }); + + return ( + + + {t(getLabelKey(item.key))} + + + + {item.isUpTo + ? t("details.apy_composition.up_to", { value }) + : value} + + + ); + })} + + ); +}; diff --git a/packages/widget/src/components/molecules/reward-token-details/index.tsx b/packages/widget/src/components/molecules/reward-token-details/index.tsx index ff41a1ae..d982c275 100644 --- a/packages/widget/src/components/molecules/reward-token-details/index.tsx +++ b/packages/widget/src/components/molecules/reward-token-details/index.tsx @@ -1,8 +1,8 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import type { ComponentProps } from "react"; import { Trans } from "react-i18next"; import type { useRewardTokenDetails } from "../../../hooks/use-reward-token-details"; +import type { YieldPendingActionType } from "../../../providers/yield-api-client-provider/types"; import { Box } from "../../atoms/box"; import { MorphoStarsIcon } from "../../atoms/icons/morpho-stars"; import { Image } from "../../atoms/image"; @@ -19,7 +19,7 @@ export const RewardTokenDetails = ({ | { type: "stake" | "unstake"; pendingAction?: never } | { type: "pendingAction"; - pendingAction: ActionTypes; + pendingAction: YieldPendingActionType; } )) => { const i18nKey: ComponentProps["i18nKey"] = (() => { @@ -29,7 +29,7 @@ export const RewardTokenDetails = ({ if (rest.type === "pendingAction") { return `pending_action_review.pending_action_type.${ - rest.pendingAction.toLowerCase() as Lowercase + rest.pendingAction.toLowerCase() as Lowercase }` as const; } diff --git a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx index 7d83af4c..f7b416fe 100644 --- a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx +++ b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx @@ -3,6 +3,10 @@ import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import type { ComponentProps, ReactNode } from "react"; import { useTranslation } from "react-i18next"; +import { + getRewardRateBreakdown, + getYieldRewardRateDetails, +} from "../../../domain/types/reward-rate"; import { APToPercentage, formatNumber, fromWei } from "../../../utils"; import { getRewardRateFormatted } from "../../../utils/formatters"; import { Box } from "../../atoms/box"; @@ -29,6 +33,20 @@ export const SelectOpportunityListItem = ({ const { t } = useTranslation(); + const campaignRate = getRewardRateBreakdown( + getYieldRewardRateDetails(item) + ).find((rewardRate) => rewardRate.key === "campaign"); + + const totalRateFormatted = getRewardRateFormatted({ + rewardRate: item.rewardRate, + rewardType: item.rewardType, + }); + + const primaryRateFormatted = getRewardRateFormatted({ + rewardRate: campaignRate ? item.rewardRate - campaignRate.rate : item.rewardRate, + rewardType: item.rewardType, + }); + return ( @@ -52,13 +70,16 @@ export const SelectOpportunityListItem = ({ - - - {getRewardRateFormatted({ - rewardRate: item.rewardRate, - rewardType: item.rewardType, - })} - + + {primaryRateFormatted} + + {campaignRate ? ( + + {t("details.apy_composition.up_to", { + value: totalRateFormatted, + })} + + ) : null} diff --git a/packages/widget/src/domain/types/positions.ts b/packages/widget/src/domain/types/positions.ts index 0f597209..c9c9a5a0 100644 --- a/packages/widget/src/domain/types/positions.ts +++ b/packages/widget/src/domain/types/positions.ts @@ -4,6 +4,7 @@ import type { YieldBalanceDto, YieldBalancesByYieldDto, YieldBalanceType, + YieldRewardRateDto, } from "../../providers/yield-api-client-provider/types"; import type { components } from "../../types/yield-api-schema"; import { equalTokens } from ".."; @@ -27,6 +28,7 @@ export type PositionsData = Map< YieldBalancesByYieldDto["yieldId"], { yieldId: YieldBalancesByYieldDto["yieldId"]; + rewardRate?: YieldRewardRateDto | null; balanceData: Map< BalanceDataKey, { balances: YieldBalanceDto[] } & ( diff --git a/packages/widget/src/domain/types/reward-rate.ts b/packages/widget/src/domain/types/reward-rate.ts new file mode 100644 index 00000000..29784e65 --- /dev/null +++ b/packages/widget/src/domain/types/reward-rate.ts @@ -0,0 +1,86 @@ +import type { RewardTypes, YieldDto } from "@stakekit/api-hooks"; +import type { + YieldRewardDto, + YieldRewardRateDto, +} from "../../providers/yield-api-client-provider/types"; + +type YieldDtoWithRewardRateDetails = YieldDto & { + rewardRateDetails?: YieldRewardRateDto; +}; + +export type RewardRateBreakdownKey = + | "native" + | "protocol_incentive" + | "campaign"; + +export type RewardRateBreakdownItem = { + key: RewardRateBreakdownKey; + rate: number; + rewardType: RewardTypes; + isUpTo: boolean; +}; + +const breakdownOrder: RewardRateBreakdownKey[] = [ + "native", + "protocol_incentive", + "campaign", +]; + +export const getRewardTypeFromRateType = ( + rateType: string | null | undefined +): RewardTypes => { + const normalized = rateType?.toLowerCase(); + + if (normalized === "apr" || normalized === "apy") { + return normalized; + } + + return "variable"; +}; + +const getBreakdownKey = ( + yieldSource: YieldRewardDto["yieldSource"] +): RewardRateBreakdownKey => + yieldSource === "campaign_incentive" + ? "campaign" + : yieldSource === "protocol_incentive" + ? "protocol_incentive" + : "native"; + +export const getYieldRewardRateDetails = ( + yieldDto: YieldDto | null | undefined +): YieldRewardRateDto | undefined => + (yieldDto as YieldDtoWithRewardRateDetails | null | undefined) + ?.rewardRateDetails; + +export const getRewardRateBreakdown = ( + rewardRate: YieldRewardRateDto | null | undefined, + opts?: { + showUpToCampaign?: boolean; + } +): RewardRateBreakdownItem[] => { + if (!rewardRate?.components?.length) { + return []; + } + + const buckets = rewardRate.components.reduce((acc, component) => { + const key = getBreakdownKey(component.yieldSource); + const prev = acc.get(key); + + acc.set(key, { + key, + rate: (prev?.rate ?? 0) + component.rate, + rewardType: + prev?.rewardType ?? getRewardTypeFromRateType(component.rateType), + isUpTo: key === "campaign" && !!opts?.showUpToCampaign, + }); + + return acc; + }, new Map()); + + return breakdownOrder.flatMap((key) => { + const item = buckets.get(key); + + return item && item.rate > 0 ? [item] : []; + }); +}; diff --git a/packages/widget/src/hooks/navigation/use-unstake-or-pending-action-params.ts b/packages/widget/src/hooks/navigation/use-unstake-or-pending-action-params.ts index e17ea5dd..970bfd6e 100644 --- a/packages/widget/src/hooks/navigation/use-unstake-or-pending-action-params.ts +++ b/packages/widget/src/hooks/navigation/use-unstake-or-pending-action-params.ts @@ -1,6 +1,6 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import type { YieldPendingActionType } from "../../providers/yield-api-client-provider/types"; import { usePendingActionSelectValidatorMatch } from "./use-pending-action-select-validator-match"; import { useUnstakeOrPendingActionMatch } from "./use-unstake-or-pending-action-match"; @@ -16,7 +16,7 @@ export const useUnstakeOrPendingActionParams = () => { {}; const pendingActionType = pendingActionSelectValidatorMatch?.params - .pendingActionType as ActionTypes | undefined; + .pendingActionType as YieldPendingActionType | undefined; return { balanceId: Maybe.fromNullable(balanceId), diff --git a/packages/widget/src/hooks/use-init-query-params.ts b/packages/widget/src/hooks/use-init-query-params.ts index 7f02e4b1..8174f161 100644 --- a/packages/widget/src/hooks/use-init-query-params.ts +++ b/packages/widget/src/hooks/use-init-query-params.ts @@ -1,4 +1,3 @@ -import { ActionTypes } from "@stakekit/api-hooks"; import { Codec, Left, Right, string } from "purify-ts"; import { useMemo } from "react"; import { @@ -7,6 +6,7 @@ import { } from "../domain/types/chains"; import type { TokenString } from "../domain/types/tokens"; import { useSettings } from "../providers/settings"; +import type { YieldPendingActionType } from "../providers/yield-api-client-provider/types"; import { MaybeWindow } from "../utils/maybe-window"; export const useInitQueryParams = () => { @@ -21,13 +21,13 @@ export const useInitQueryParams = () => { ); }; -const pendingActionCodec = Codec.custom({ +const pendingActionCodec = Codec.custom({ decode: (val) => string .decode(val) .chain((v) => - v in ActionTypes - ? Right(v as ActionTypes) + /^[A-Z_]+$/.test(v) + ? Right(v as YieldPendingActionType) : Left("invalid pending action") ), encode: (val) => val, diff --git a/packages/widget/src/hooks/use-position-balances.ts b/packages/widget/src/hooks/use-position-balances.ts index a4d3a6db..613e8641 100644 --- a/packages/widget/src/hooks/use-position-balances.ts +++ b/packages/widget/src/hooks/use-position-balances.ts @@ -17,11 +17,20 @@ export const usePositionBalances = ({ Maybe.fromRecord({ positionData: data, balanceId: Maybe.fromNullable(balanceId as BalanceDataKey), - }).chainNullable( - (val) => + }).chainNullable((val) => { + const balanceData = val.positionData.balanceData.get(val.balanceId) ?? - val.positionData.balanceData.values().next().value - ), + val.positionData.balanceData.values().next().value; + + if (!balanceData) { + return undefined; + } + + return { + ...balanceData, + rewardRate: val.positionData.rewardRate, + }; + }), [balanceId, data] ); diff --git a/packages/widget/src/hooks/use-positions-data.ts b/packages/widget/src/hooks/use-positions-data.ts index 0cc75198..657ecdde 100644 --- a/packages/widget/src/hooks/use-positions-data.ts +++ b/packages/widget/src/hooks/use-positions-data.ts @@ -33,6 +33,7 @@ const positionsDataSelector = createSelector( normalizeYieldBalancesForPosition(balancesData).reduce((acc, val) => { acc.set(val.yieldId, { yieldId: val.yieldId, + rewardRate: val.rewardRate, balanceData: [...val.balances] .sort((a, b) => getPositionBalanceDataKey(a).localeCompare( diff --git a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx index 31472e3c..7cacbed5 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx @@ -1,4 +1,3 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; @@ -8,6 +7,7 @@ import { SelectValidator } from "../../../components/molecules/select-validator" import { AmountBlock } from "../../../pages/position-details/components/amount-block"; import { StaticActionBlock } from "../../../pages/position-details/components/static-action-block"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; +import type { YieldPendingActionType } from "../../../providers/yield-api-client-provider/types"; import { container } from "./styles.css"; export const positionDetailsActionsHasContent = ( @@ -103,7 +103,7 @@ export const PositionDetailsActions = () => { } label={t( `position_details.pending_action_button.${ - val.pendingActionDto.type.toLowerCase() as Lowercase + val.pendingActionDto.type.toLowerCase() as Lowercase }` )} onMaxClick={null} diff --git a/packages/widget/src/pages/complete/pages/common.page.tsx b/packages/widget/src/pages/complete/pages/common.page.tsx index 8704dbad..ed0d1bf8 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -1,9 +1,4 @@ -import type { - ActionTypes, - TokenDto, - YieldDto, - YieldMetadataDto, -} from "@stakekit/api-hooks"; +import type { TokenDto, YieldDto, YieldMetadataDto } from "@stakekit/api-hooks"; import { motion } from "motion/react"; import { Just, Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; @@ -19,7 +14,10 @@ import { isEthenaUsdeStaking, } from "../../../domain/types/yields"; import { AnimationPage } from "../../../navigation/containers/animation-page"; -import type { YieldTokenDto } from "../../../providers/yield-api-client-provider/types"; +import type { + YieldPendingActionType, + 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"; @@ -33,7 +31,7 @@ type Props = { metadata: Maybe; network: string; amount: string; - pendingActionType?: ActionTypes; + pendingActionType?: YieldPendingActionType; providersDetails: Maybe< { logo: string | undefined; @@ -134,7 +132,7 @@ export const CompletePageComponent = ({ tokenNetwork: network, pendingAction: t( `complete.pending_action.${ - pendingActionType?.toLowerCase() as Lowercase + pendingActionType?.toLowerCase() as Lowercase }` as const, { context: isEthenaUsdeStaking(integrationId) diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx index 031a622a..21e7b24e 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx @@ -5,10 +5,12 @@ import { MorphoStarsIcon } from "../../../../../components/atoms/icons/morpho-st import { Image } from "../../../../../components/atoms/image"; import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; +import { RewardRateBreakdown } from "../../../../../components/molecules/reward-rate-breakdown"; import { isMorphoProvider, RewardTokenDetails, } from "../../../../../components/molecules/reward-token-details"; +import { getYieldRewardRateDetails } from "../../../../../domain/types/reward-rate"; import { VerticalDivider } from "../../../../../pages-dashboard/common/components/divider"; import { useSettings } from "../../../../../providers/settings"; import { combineRecipeWithVariant } from "../../../../../utils/styles"; @@ -17,10 +19,15 @@ import { selectYieldRewardsText } from "./styles.css"; export const SelectYieldRewardDetails = () => { const { variant } = useSettings(); + const { t } = useTranslation(); - const { rewardToken, estimatedRewards, rewardsTokenSymbol } = + const { rewardToken, estimatedRewards, rewardsTokenSymbol, selectedStake } = useEarnPageContext(); + const rewardRateDetails = selectedStake.chainNullable( + getYieldRewardRateDetails + ); + const earnYearly = estimatedRewards.mapOrDefault( (e) => `${e.yearly} ${rewardsTokenSymbol}`, "" @@ -113,6 +120,17 @@ export const SelectYieldRewardDetails = () => { earnYearly={earnYearly} /> )} + + {rewardRateDetails + .map((rewardRate) => ( + + )) + .extractNullable()} ); diff --git a/packages/widget/src/pages/position-details/components/provider-details.tsx b/packages/widget/src/pages/position-details/components/provider-details.tsx index 8cbdaa93..e46cf9c4 100644 --- a/packages/widget/src/pages/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages/position-details/components/provider-details.tsx @@ -24,7 +24,10 @@ export const ProviderDetails = ({ integrationData, logo, ...providerDetails -}: { +}: Omit< + GetMaybeJust>[0], + "rewardType" +> & { isFirst: boolean; stakeType: string; integrationData: YieldDto; @@ -32,8 +35,8 @@ export const ProviderDetails = ({ name: string; rewardRateFormatted: string; rewardRate: number | undefined; - rewardType: RewardTypes; -} & GetMaybeJust>[0]) => { + rewardType?: RewardTypes; +}) => { const { t } = useTranslation(); const nameOrAddress = providerDetails.name ?? providerDetails ?? ""; 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 312477c8..61ba274d 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,4 +1,4 @@ -import type { ActionTypes, YieldDto } from "@stakekit/api-hooks"; +import type { YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Trans, useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; @@ -8,6 +8,7 @@ import { isEthenaUsdeStaking } from "../../../domain/types/yields"; import type { YieldBalanceDto, YieldPendingActionDto, + YieldPendingActionType, } from "../../../providers/yield-api-client-provider/types"; import { formatNumber } from "../../../utils"; import type { usePositionDetails } from "../hooks/use-position-details"; @@ -52,7 +53,7 @@ export const StaticActionBlock = ({ symbol: yieldBalance.token.symbol, pendingAction: t( `position_details.pending_action.${ - pendingActionDto.type.toLowerCase() as Lowercase + pendingActionDto.type.toLowerCase() as Lowercase }`, { context: isEthenaUsdeStaking(yieldId) @@ -90,7 +91,7 @@ export const StaticActionBlock = ({ {t( `position_details.pending_action_button.${ - pendingActionDto.type.toLowerCase() as Lowercase + pendingActionDto.type.toLowerCase() as Lowercase }` )} 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 602c21e2..64cb3052 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 @@ -116,6 +116,14 @@ export const usePositionDetails = () => { validatorsData: Maybe.fromNullable(yieldValidators.data), }); + const personalizedRewardRate = useMemo( + () => + positionBalances.data + .map((balanceData) => balanceData.rewardRate) + .extractNullable(), + [positionBalances.data] + ); + const canUnstake = integrationData.filter((d) => !!d.args.exit).isJust(); const onUnstakeAmountChange = (value: BigNumber) => @@ -204,6 +212,7 @@ export const usePositionDetails = () => { isLoading, onPendingActionClick, providersDetails, + personalizedRewardRate, pendingActions, liquidTokensToNativeConversion, validatorAddressesHandling, 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 95b09e53..4b732070 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -1,4 +1,3 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { Just, Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; import { Box } from "../../components/atoms/box"; @@ -7,9 +6,13 @@ import { Spinner } from "../../components/atoms/spinner"; import { TokenIcon } from "../../components/atoms/token-icon"; import { Heading } from "../../components/atoms/typography/heading"; import { Text } from "../../components/atoms/typography/text"; +import { RewardRateBreakdown } from "../../components/molecules/reward-rate-breakdown"; import { SelectValidator } from "../../components/molecules/select-validator"; +import { getRewardTypeFromRateType } from "../../domain/types/reward-rate"; import { useTrackPage } from "../../hooks/tracking/use-track-page"; import { AnimationPage } from "../../navigation/containers/animation-page"; +import type { YieldPendingActionType } from "../../providers/yield-api-client-provider/types"; +import { getRewardRateFormatted } from "../../utils/formatters"; import { PageContainer } from "../components/page-container"; import { AmountBlock } from "./components/amount-block"; import { PositionBalances } from "./components/position-balances"; @@ -46,6 +49,7 @@ const PositionDetails = () => { unstakeMaxAmount, unstakeMinAmount, unstakeIsGreaterOrLessIntegrationLimitError, + personalizedRewardRate, } = usePositionDetails(); useTrackPage("positionDetails", { @@ -107,6 +111,39 @@ const PositionDetails = () => { )) .extractNullable()} + {personalizedRewardRate ? ( + + + + {t("position_details.personalized_apy")} + + + + {getRewardRateFormatted({ + rewardRate: personalizedRewardRate.total, + rewardType: getRewardTypeFromRateType( + personalizedRewardRate.rateType + ), + })} + + + + + + ) : null} + {providersDetails .map((pd) => @@ -115,6 +152,12 @@ const PositionDetails = () => { {...p} key={p.address ?? idx} isFirst={idx === 0} + rewardRate={ + personalizedRewardRate ? undefined : p.rewardRate + } + rewardType={ + personalizedRewardRate ? undefined : p.rewardType + } stakeType={t( `position_details.stake_type.${integrationData.metadata.type}` )} @@ -192,7 +235,7 @@ const PositionDetails = () => { } label={t( `position_details.pending_action_button.${ - val.pendingActionDto.type.toLowerCase() as Lowercase + val.pendingActionDto.type.toLowerCase() as Lowercase }` )} onMaxClick={null} diff --git a/packages/widget/src/pages/position-details/state/index.tsx b/packages/widget/src/pages/position-details/state/index.tsx index c8d9621b..92010387 100644 --- a/packages/widget/src/pages/position-details/state/index.tsx +++ b/packages/widget/src/pages/position-details/state/index.tsx @@ -1,4 +1,3 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import type { Dispatch, PropsWithChildren } from "react"; @@ -24,6 +23,7 @@ import { useStakedOrLiquidBalance } from "../../../hooks/use-staked-or-liquid-ba import type { YieldBalanceDto, YieldPendingActionDto, + YieldPendingActionType, YieldTokenDto, } from "../../../providers/yield-api-client-provider/types"; import type { @@ -198,7 +198,7 @@ export const UnstakeOrPendingActionProvider = ({ state: State["pendingActions"]; balanceType: YieldBalanceDto["type"]; token: YieldTokenDto; - actionType: ActionTypes; + actionType: YieldPendingActionType; amount: BigNumber; }) => { const key = getBalanceTokenActionType({ actionType, balanceType, token }); diff --git a/packages/widget/src/pages/position-details/state/types.ts b/packages/widget/src/pages/position-details/state/types.ts index 9ebb9560..cba4dba7 100644 --- a/packages/widget/src/pages/position-details/state/types.ts +++ b/packages/widget/src/pages/position-details/state/types.ts @@ -1,4 +1,4 @@ -import type { ActionTypes, TokenDto, YieldDto } from "@stakekit/api-hooks"; +import type { 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"; @@ -10,6 +10,7 @@ import type { usePositionBalances } from "../../../hooks/use-position-balances"; import type { useStakedOrLiquidBalance } from "../../../hooks/use-staked-or-liquid-balance"; import type { YieldBalanceType, + YieldPendingActionType, YieldTokenDto, } from "../../../providers/yield-api-client-provider/types"; import type { Action } from "../../../types/utils"; @@ -18,14 +19,14 @@ type UnstakeAmountChange = Action<"unstake/amount/change", BigNumber>; type UnstakeAmountMax = Action<"unstake/amount/max">; export type BalanceTokenActionType = - `${YieldBalanceType}-${TokenString}-${ActionTypes}`; + `${YieldBalanceType}-${TokenString}-${YieldPendingActionType}`; export type PendingActionAmountChange = Action< "pendingAction/amount/change", { balanceType: YieldBalanceType; token: TokenDto | YieldTokenDto; - actionType: ActionTypes; + actionType: YieldPendingActionType; amount: BigNumber; } >; @@ -41,7 +42,7 @@ export type State = { }; export type ExtraData = { - pendingActionType: Maybe; + pendingActionType: Maybe; integrationData: Maybe; positionBalances: ReturnType; yieldOpportunity: ReturnType; diff --git a/packages/widget/src/pages/position-details/state/utils.ts b/packages/widget/src/pages/position-details/state/utils.ts index d5fdda4c..bec195ba 100644 --- a/packages/widget/src/pages/position-details/state/utils.ts +++ b/packages/widget/src/pages/position-details/state/utils.ts @@ -1,7 +1,8 @@ -import type { ActionTypes, TokenDto } from "@stakekit/api-hooks"; +import type { TokenDto } from "@stakekit/api-hooks"; import { tokenString } from "../../../domain"; import type { YieldBalanceType, + YieldPendingActionType, YieldTokenDto, } from "../../../providers/yield-api-client-provider/types"; import type { BalanceTokenActionType } from "./types"; @@ -13,6 +14,6 @@ export const getBalanceTokenActionType = ({ }: { balanceType: YieldBalanceType; token: TokenDto | YieldTokenDto; - actionType: ActionTypes; + actionType: YieldPendingActionType; }): BalanceTokenActionType => `${balanceType}-${tokenString(token)}-${actionType}`; diff --git a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts index 148d6aa1..415ebd80 100644 --- a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts @@ -1,4 +1,3 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; @@ -15,6 +14,7 @@ import { useSavedRef } from "../../../hooks/use-saved-ref"; import { usePendingActionStore } from "../../../providers/pending-action-store"; import { useYieldApiFetchClient } from "../../../providers/yield-api-client-provider"; import { createManageAction } from "../../../providers/yield-api-client-provider/actions"; +import type { YieldPendingActionType } from "../../../providers/yield-api-client-provider/types"; import { formatNumber } from "../../../utils"; import { getGasFeeInUSD } from "../../../utils/formatters"; import { useRegisterFooterButton } from "../../components/footer-outlet/context"; @@ -92,7 +92,7 @@ export const usePendingActionReview = () => { Maybe.of( t( `position_details.pending_action_button.${ - pendingRequest.requestDto.action.toLowerCase() as Lowercase + pendingRequest.requestDto.action.toLowerCase() as Lowercase }` as const ) ), diff --git a/packages/widget/src/providers/pending-action-store/index.tsx b/packages/widget/src/providers/pending-action-store/index.tsx index 80136eca..13f179f0 100644 --- a/packages/widget/src/providers/pending-action-store/index.tsx +++ b/packages/widget/src/providers/pending-action-store/index.tsx @@ -1,6 +1,5 @@ import type { ActionDto, - ActionTypes, AddressesDto, TokenDto, YieldDto, @@ -10,13 +9,14 @@ import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; import type { YieldCreateManageActionDto, + YieldPendingActionType, YieldTokenDto, } from "../yield-api-client-provider/types"; type InitData = { requestDto: YieldCreateManageActionDto; addresses: AddressesDto; - pendingActionType: ActionTypes; + pendingActionType: YieldPendingActionType; integrationData: YieldDto; interactedToken: TokenDto | YieldTokenDto; gasFeeToken: TokenDto; diff --git a/packages/widget/src/providers/yield-api-client-provider/compat.ts b/packages/widget/src/providers/yield-api-client-provider/compat.ts index 9ae58d8f..c290447b 100644 --- a/packages/widget/src/providers/yield-api-client-provider/compat.ts +++ b/packages/widget/src/providers/yield-api-client-provider/compat.ts @@ -299,6 +299,7 @@ export const adaptYieldDto = ({ tokens: tokens.length ? tokens : (legacyYieldDto?.tokens ?? [token]), metadata: getMetadata({ yieldDto, legacyYieldDto }), rewardRate, + rewardRateDetails: yieldDto.rewardRate, rewardType: getRewardType({ yieldDto, legacyYieldDto }), status: { ...(legacyYieldDto?.status ?? {}), diff --git a/packages/widget/src/providers/yield-api-client-provider/types.ts b/packages/widget/src/providers/yield-api-client-provider/types.ts index 6ec565cf..750288f7 100644 --- a/packages/widget/src/providers/yield-api-client-provider/types.ts +++ b/packages/widget/src/providers/yield-api-client-provider/types.ts @@ -9,12 +9,16 @@ export type YieldBalanceType = components["schemas"]["BalanceType"]; export type YieldPendingActionDto = components["schemas"]["PendingActionDto"]; export type YieldTokenDto = components["schemas"]["TokenDto"]; +export type YieldRewardDto = components["schemas"]["RewardDto"]; export type YieldRewardRateDto = components["schemas"]["RewardRateDto"]; export type YieldActionArgumentsDto = components["schemas"]["ActionArgumentsDto"]; export type YieldCreateActionDto = components["schemas"]["CreateActionDto"]; export type YieldCreateManageActionDto = components["schemas"]["CreateManageActionDto"]; +export type YieldPendingActionType = + | YieldPendingActionDto["type"] + | NonNullable; export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; diff --git a/packages/widget/src/translation/English/translations.json b/packages/widget/src/translation/English/translations.json index 2baf1772..5af0f911 100644 --- a/packages/widget/src/translation/English/translations.json +++ b/packages/widget/src/translation/English/translations.json @@ -157,7 +157,14 @@ "validators_nominator_count": "Nominator count", "validators_subnet_name": "Subnet name", "validators_market_cap": "Market cap", - "validators_token_symbol": "Token symbol" + "validators_token_symbol": "Token symbol", + "apy_composition": { + "title": "APY composition", + "native": "Native APY", + "protocol_incentive": "Protocol incentive APY", + "campaign": "Campaign APY", + "up_to": "Up to {{value}}" + } }, "dashboard": { "details": { @@ -328,6 +335,8 @@ "stake": "staked", "unstake": "unstaked", "claim_rewards": "claimed rewards", + "auto_sweep_unstake_rewards": "unstaked rewards", + "auto_sweep_withdraw_rewards": "withdrew rewards", "restake_rewards": "restaked rewards", "withdraw": "withdrawn", "restake": "restaked", @@ -445,6 +454,7 @@ "unstake": "Unstake", "withdraw": "Withdraw", "available": "{{amount}} {{symbol}} available", + "personalized_apy": "Personalized APY", "select_validators": { "submit": "Submit" }, @@ -490,6 +500,8 @@ "stake": "Stake", "unstake": "Unstake", "claim_rewards": "Claim rewards", + "auto_sweep_unstake_rewards": "Unstake rewards", + "auto_sweep_withdraw_rewards": "Withdraw rewards", "restake_rewards": "Restake rewards", "withdraw": "Withdraw", "withdraw_all": "Withdraw all", @@ -518,6 +530,8 @@ "stake": "Stake", "unstake": "Unstake", "claim_rewards": "Claim", + "auto_sweep_unstake_rewards": "Unstake rewards", + "auto_sweep_withdraw_rewards": "Withdraw rewards", "restake_rewards": "Restake", "withdraw": "Withdraw", "withdraw_all": "Withdraw all", @@ -549,6 +563,8 @@ "stake": "Stake with {{providerName}}", "unstake": "Unstake from {{providerName}}", "claim_rewards": "Claim rewards from {{providerName}}", + "auto_sweep_unstake_rewards": "Unstake rewards from {{providerName}}", + "auto_sweep_withdraw_rewards": "Withdraw rewards from {{providerName}}", "restake_rewards": "Restake rewards with {{providerName}}", "withdraw": "Withdraw from {{providerName}}", "withdraw_all": "Withdraw all from {{providerName}}", diff --git a/packages/widget/src/translation/French/translations.json b/packages/widget/src/translation/French/translations.json index d0c42eb0..1fd96c1a 100644 --- a/packages/widget/src/translation/French/translations.json +++ b/packages/widget/src/translation/French/translations.json @@ -154,7 +154,14 @@ "validators_nominator_count": "Nombre de nominators", "validators_subnet_name": "Nom du sous-réseau", "validators_market_cap": "Capitalisation boursière", - "validators_token_symbol": "Symbole du token" + "validators_token_symbol": "Symbole du token", + "apy_composition": { + "title": "Composition de l'APY", + "native": "APY natif", + "protocol_incentive": "APY d'incitation protocolaire", + "campaign": "APY de campagne", + "up_to": "Jusqu'a {{value}}" + } }, "review": { "estimated_reward": "Récompenses estimées", @@ -278,6 +285,8 @@ "stake": "staké", "unstake": "déstaké", "claim_rewards": "récompenses réclamées", + "auto_sweep_unstake_rewards": "récompenses retirées", + "auto_sweep_withdraw_rewards": "récompenses retirées", "restake_rewards": "récompenses restakées", "withdraw": "reitré", "restake": "restaké", @@ -395,6 +404,7 @@ "unstake": "Déstaker", "withdraw": "Retirer", "available": "{{amount}} {{symbol}} disponibles", + "personalized_apy": "APY personnalise", "select_validators": { "submit": "Envoyer" }, @@ -440,6 +450,8 @@ "stake": "Staker", "unstake": "Déstaker", "claim_rewards": "Réclamer les récompenses", + "auto_sweep_unstake_rewards": "Retirer les récompenses", + "auto_sweep_withdraw_rewards": "Retirer les récompenses", "restake_rewards": "Restaker les récompenses", "withdraw": "Retirer", "withdraw_all": "Retirer tout", @@ -468,6 +480,8 @@ "stake": "Staker", "unstake": "Déstaker", "claim_rewards": "Réclamer", + "auto_sweep_unstake_rewards": "Retirer les récompenses", + "auto_sweep_withdraw_rewards": "Retirer les récompenses", "restake_rewards": "Restaker", "withdraw": "Retirer", "withdraw_all": "Retirer tout", @@ -499,6 +513,8 @@ "stake": "Staker avec {{providerName}}", "unstake": "Déstaker de {{providerName}}", "claim_rewards": "Réclamer les récompenses de {{providerName}}", + "auto_sweep_unstake_rewards": "Retirer les récompenses de {{providerName}}", + "auto_sweep_withdraw_rewards": "Retirer les récompenses de {{providerName}}", "restake_rewards": "Restaker les récompenses avec {{providerName}}", "withdraw": "Retirer de {{providerName}}", "withdraw_all": "Retirer tout de {{providerName}}", diff --git a/packages/widget/src/types/yield-api-schema.d.ts b/packages/widget/src/types/yield-api-schema.d.ts index bf972271..6ed192e2 100644 --- a/packages/widget/src/types/yield-api-schema.d.ts +++ b/packages/widget/src/types/yield-api-schema.d.ts @@ -84,6 +84,26 @@ export interface paths { patch?: never; trace?: never; }; + "/v1/yields/{yieldId}/balances/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get historical balance snapshots for a yield + * @description Returns a chronological time series of balance snapshots for a wallet address within a yield. Each entry reflects the position at a specific timestamp or block. Supports configurable sampling intervals and point-in-time queries. Only available for ERC4626 vaults with indexed transfer history. + */ + get: operations["YieldsController_getBalanceHistory"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/yields/{yieldId}/balances": { parameters: { query?: never; @@ -104,6 +124,66 @@ export interface paths { patch?: never; trace?: never; }; + "/v1/yields/{yieldId}/rewards/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get reward history + * @description Retrieve a chronological list of on-chain reward events for an indexed yield. Each record includes timestamp, token metadata, amount, reward source, and transaction reference. + */ + get: operations["YieldsController_getYieldRewards"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/{yieldId}/reward-rate/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get historical reward rate snapshots for a yield + * @description Returns a chronological time series of reward rate snapshots for the specified yield, suitable for charting and analytics. Supports configurable time ranges, sampling intervals (day/week/month), and pagination. + */ + get: operations["YieldsController_getYieldRewardRateHistory"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/yields/{yieldId}/tvl/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get historical TVL snapshots for a yield + * @description Returns a chronological time series of Total Value Locked for the specified yield, expressed in underlying token units. Supports configurable time ranges, sampling intervals (day/week/month), and pagination. + */ + get: operations["YieldsController_getYieldTvlHistory"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/yields/{yieldId}/validators": { parameters: { query?: never; @@ -532,6 +612,7 @@ export interface components { | "staking" | "restaking" | "protocol_incentive" + | "campaign_incentive" | "points" | "lending_interest" | "mev" @@ -1364,6 +1445,8 @@ export interface components { | "STAKE" | "UNSTAKE" | "CLAIM_REWARDS" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_REWARDS" | "RESTAKE_REWARDS" | "WITHDRAW" | "WITHDRAW_ALL" @@ -1387,7 +1470,7 @@ export interface components { /** @description Argument schema required to execute this action */ arguments?: components["schemas"]["ArgumentSchemaDto"] | null; /** - * @description Amount involved in the action + * @description Amount involved in the action, in human-readable token units (not the smallest denomination). * @example 0.1 */ amount?: string | null; @@ -1725,6 +1808,8 @@ export interface components { balances: components["schemas"]["BalanceDto"][]; /** @description Balance for the output token */ outputTokenBalance?: components["schemas"]["BalanceDto"] | null; + /** @description Personalized reward rate breakdown for this balance position */ + rewardRate?: components["schemas"]["RewardRateDto"] | null; }; YieldErrorDto: { /** @@ -1856,6 +1941,16 @@ export interface components { * @example P-avax1... */ pAddressBech?: string; + /** + * @description Day of month when auto-sweep window starts (used by Solana auto-sweep balance actions) + * @example 20 + */ + autoSweepDayOfMonth?: number; + /** + * @description IANA timezone used to evaluate auto-sweep window day (e.g. Europe/London) + * @example Europe/London + */ + autoSweepTimezone?: string; }; BalancesQueryDto: { /** @@ -1875,7 +1970,7 @@ export interface components { }; BalancesRequestDto: { /** - * @description Array of balance queries + * @description Array of balance queries (maximum 25 queries per request) * @example [ * { * "yieldId": "ethereum-eth-lido-staking", @@ -2110,7 +2205,8 @@ export interface components { | "LUGANODES_EXIT_REQUEST" | "INFSTONES_PROVISION" | "INFSTONES_EXIT_REQUEST" - | "INFSTONES_CLAIM_REQUEST"; + | "INFSTONES_CLAIM_REQUEST" + | "BATCH"; /** * @description Transaction hash (available after broadcast) * @example 0x1234567890abcdef... @@ -2175,15 +2271,15 @@ export interface components { }; ActionArgumentsDto: { /** - * @description Amount to stake/unstake - * @example 1000000000000000000 + * @description Amount in human-readable token units, not the smallest denomination. For example, "1.500000" for 1.5 USDC (6 decimals) or "0.01" for 0.01 ETH (18 decimals). Precision up to the token's decimal places is supported. + * @example 1.500000 */ amount?: string; /** - * @description Amounts to stake/unstake + * @description Amounts in human-readable token units, not the smallest denomination. Precision up to the token's decimal places is supported. * @example [ - * "1000000000000000000", - * "2000000000000000000" + * "1.500000", + * "2.000000" * ] */ amounts?: string[]; @@ -2531,6 +2627,8 @@ export interface components { | "STAKE" | "UNSTAKE" | "CLAIM_REWARDS" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_REWARDS" | "RESTAKE_REWARDS" | "WITHDRAW" | "WITHDRAW_ALL" @@ -2557,12 +2655,12 @@ export interface components { */ address: string; /** - * @description Amount involved in the action - * @example 1000000000000000000 + * @description Amount involved in the action, in human-readable token units (not the smallest denomination). + * @example 1.0 */ amount: string | null; /** - * @description Raw wei amount (full precision) + * @description Raw smallest-denomination amount (full precision) * @example 1000000000000000000 */ amountRaw: string | null; @@ -2832,6 +2930,143 @@ export interface components { /** Format: date-time */ updatedAt: string; }; + BalanceHistorySnapshotDto: { + /** + * @description Timestamp of this snapshot (ISO 8601) + * @example 2025-07-12T00:00:00.000Z + */ + timestamp: string; + /** + * @description Block number closest to this snapshot + * @example 20540000 + */ + blockNumber: number; + /** + * @description Unique identifier of the yield + * @example ethereum-eth-lido-staking + */ + yieldId: string; + /** @description Balance entries at this point in time */ + balances: components["schemas"]["BalanceDto"][]; + }; + RewardEventDto: { + /** + * @description Timestamp of this reward event (ISO 8601) + * @example 2025-07-12T00:00:00.000Z + */ + timestamp: string; + /** + * @description Block number when the reward was earned + * @example 20540000 + */ + blockNumber: number; + /** + * @description Unique identifier of the yield + * @example ethereum-usdc-morpho-vault + */ + yieldId: string; + /** @description Token metadata for the reward */ + token: components["schemas"]["TokenDto"]; + /** + * @description Human-readable reward amount + * @example 1.4 + */ + amount: string; + /** + * @description Raw reward amount in base units (wei) + * @example 1400000 + */ + amountRaw: string; + /** + * @description Source of the reward derived from yield type + * @example vault_yield + */ + yieldSource: string; + /** + * @description Transaction hash where the reward was earned + * @example 0xabc123... + */ + transactionHash?: string | null; + }; + RewardRateSnapshotDto: { + /** + * @description Timestamp of this snapshot (ISO 8601) + * @example 2025-07-10T00:00:00.000Z + */ + timestamp: string; + /** + * @description Reward rate as a decimal string + * @example 0.0312 + */ + rewardRate: string; + }; + RewardRateHistoryResponseDto: { + /** + * @description Unique identifier of the yield + * @example ethereum-eth-lido-staking + */ + yieldId: string; + /** + * @description Sampling interval used for this response + * @example day + * @enum {string} + */ + interval: "day" | "week" | "month"; + /** + * @description Start of the returned date range (ISO 8601) + * @example 2025-06-01T00:00:00.000Z + */ + from: string; + /** + * @description End of the returned date range (ISO 8601) + * @example 2025-07-10T00:00:00.000Z + */ + to: string; + /** @description Chronological reward rate snapshots (most recent first) */ + series: components["schemas"]["RewardRateSnapshotDto"][]; + }; + TvlSnapshotDto: { + /** + * @description Timestamp of this snapshot (ISO 8601) + * @example 2025-07-11T00:00:00.000Z + */ + timestamp: string; + /** + * @description Total value locked in token units (human-readable) + * @example 512340000.12 + */ + tvl: string; + /** + * @description Total value locked in smallest token unit (wei) + * @example 512340000120000 + */ + tvlRaw: string; + }; + TvlHistoryResponseDto: { + /** + * @description Unique identifier of the yield + * @example ethereum-usdc-aave-v3 + */ + yieldId: string; + /** + * @description Sampling interval used for this response + * @example day + * @enum {string} + */ + interval: "day" | "week" | "month"; + /** + * @description Start of the returned date range (ISO 8601) + * @example 2025-06-12T00:00:00.000Z + */ + from: string; + /** + * @description End of the returned date range (ISO 8601) + * @example 2025-07-12T00:00:00.000Z + */ + to: string; + /** @description Chronological TVL snapshots (most recent first) */ + series: components["schemas"]["TvlSnapshotDto"][]; + }; CreateActionDto: { /** * @description Yield ID to perform the action on @@ -2868,6 +3103,8 @@ export interface components { | "STAKE" | "UNSTAKE" | "CLAIM_REWARDS" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_REWARDS" | "RESTAKE_REWARDS" | "WITHDRAW" | "WITHDRAW_ALL" @@ -2932,6 +3169,8 @@ export interface components { | "STAKE" | "UNSTAKE" | "CLAIM_REWARDS" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_REWARDS" | "RESTAKE_REWARDS" | "WITHDRAW" | "WITHDRAW_ALL" @@ -3739,33 +3978,564 @@ export interface operations { }; }; }; - YieldsController_getYieldBalances: { + YieldsController_getBalanceHistory: { parameters: { - query?: never; + query: { + /** + * @description Wallet address to fetch history for + * @example 0x742d35Cc6634C0532925a3b844Bc454e4438f44e + */ + address: string; + /** + * @description Start of time range (ISO 8601) + * @example 2025-01-01T00:00:00Z + */ + from?: string; + /** + * @description End of time range (ISO 8601). Defaults to now. + * @example 2025-07-12T00:00:00Z + */ + to?: string; + /** + * @description Block number for a point-in-time snapshot. When provided, from/to/interval are ignored. + * @example 20540000 + */ + blockNumber?: number; + /** @description Sampling resolution for the time series */ + interval?: "block" | "hour" | "day" | "week"; + /** @description Sort order by timestamp. Defaults to most recent first (desc). */ + sort?: "asc" | "desc"; + /** + * @description Maximum number of items to return (default 30, max 100) + * @example 30 + */ + limit?: number; + /** + * @description Pagination offset + * @example 0 + */ + offset?: number; + }; header?: never; path: { /** - * @description The unique identifier of the yield opportunity + * @description The unique identifier of the yield * @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"]; - }; - }; + requestBody?: never; responses: { - /** @description Returns balance information including different balance types (active, entering, exiting, withdrawable, claimable, locked) with amounts and available actions */ + /** @description Returns a paginated time series of balance snapshots */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["YieldBalancesDto"]; + "application/json": components["schemas"]["PaginatedResponseDto"] & { + items?: components["schemas"]["BalanceHistorySnapshotDto"][]; + }; + }; + }; + /** @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_getYieldRewards: { + parameters: { + query: { + /** + * @description Wallet address to fetch rewards for + * @example 0x742d35Cc6634C0532925a3b844Bc454e4438f44e + */ + address: string; + /** + * @description Start of time range (ISO 8601) + * @example 2025-01-01T00:00:00Z + */ + from?: string; + /** + * @description End of time range (ISO 8601) + * @example 2025-07-12T00:00:00Z + */ + to?: string; + /** @description Sort order by timestamp (default: desc) */ + sort?: "asc" | "desc"; + /** + * @description Maximum number of items to return (default: 100, max: 100) + * @example 100 + */ + limit?: number; + /** + * @description Pagination offset (default: 0) + * @example 0 + */ + offset?: number; + }; + header?: never; + path: { + /** + * @description The unique identifier of the yield + * @example ethereum-usdc-morpho-vault + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Paginated reward events */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponseDto"]; + }; + }; + /** @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 Reward history not available for this yield (not indexed) */ + 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_getYieldRewardRateHistory: { + parameters: { + query?: { + /** + * @description Start of time range (ISO 8601). Overrides period when provided. + * @example 2025-01-01T00:00:00Z + */ + from?: string; + /** + * @description End of time range (ISO 8601). Defaults to now. + * @example 2025-07-10T00:00:00Z + */ + to?: string; + /** @description Predefined time window. Ignored when from/to are provided. Default: 30d. */ + period?: "1d" | "7d" | "30d" | "90d" | "1y" | "all"; + /** @description Sampling resolution (day/week/month). Default: day. */ + interval?: "day" | "week" | "month"; + /** @description Maximum number of data points to return (default 100, max 365) */ + limit?: number; + /** @description Pagination offset */ + offset?: number; + }; + header?: never; + path: { + /** + * @description The unique identifier of the yield + * @example ethereum-eth-lido-staking + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns a time series of reward rate snapshots */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RewardRateHistoryResponseDto"]; + }; + }; + /** @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_getYieldTvlHistory: { + parameters: { + query?: { + /** + * @description Start of time range (ISO 8601). Overrides period when provided. + * @example 2025-01-01T00:00:00Z + */ + from?: string; + /** + * @description End of time range (ISO 8601). Defaults to now. + * @example 2025-07-10T00:00:00Z + */ + to?: string; + /** @description Predefined time window. Ignored when from/to are provided. Default: 30d. */ + period?: "1d" | "7d" | "30d" | "90d" | "1y" | "all"; + /** @description Sampling resolution (day/week/month). Default: day. */ + interval?: "day" | "week" | "month"; + /** @description Maximum number of data points to return (default 100, max 365) */ + limit?: number; + /** @description Pagination offset */ + offset?: number; + }; + header?: never; + path: { + /** + * @description The unique identifier of the yield + * @example ethereum-usdc-aave-v3-lending + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns a time series of TVL snapshots in token units */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TvlHistoryResponseDto"]; }; }; /** @description Invalid request parameters */ @@ -3879,7 +4649,7 @@ export interface operations { path: { /** * @description The unique identifier of the yield - * @example cosmos-staking + * @example solana-sol-native-multivalidator-staking */ yieldId: string; }; @@ -4008,6 +4778,8 @@ export interface operations { | "STAKE" | "UNSTAKE" | "CLAIM_REWARDS" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_REWARDS" | "RESTAKE_REWARDS" | "WITHDRAW" | "WITHDRAW_ALL" diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index b7f2f888..ea543d00 100644 --- a/packages/widget/tests/fixtures/index.ts +++ b/packages/widget/tests/fixtures/index.ts @@ -10,11 +10,49 @@ import { Just } from "purify-ts"; import type { YieldActionArgumentsDto, YieldActionDto, + YieldDto as YieldApiYieldDto, + YieldBalanceDto, + YieldRewardRateDto, YieldTransactionDto, } from "../../src/providers/yield-api-client-provider/types"; const apyFaker = () => faker.number.float({ min: 0, max: 0.05 }); +export const yieldRewardRateFixture = ( + overrides?: Partial +): YieldRewardRateDto => ({ + total: apyFaker(), + rateType: "APY", + components: [], + ...overrides, +}); + +export const yieldApiYieldFixture = ( + overrides?: Partial +): YieldApiYieldDto => + ({ + ...getYieldV2ControllerGetYieldByIdResponseMock(), + rewardRate: yieldRewardRateFixture(), + ...overrides, + }) as YieldApiYieldDto; + +export const yieldBalanceFixture = ( + overrides?: Partial +): YieldBalanceDto => { + const token = overrides?.token ?? yieldApiYieldFixture().token; + + return { + address: faker.finance.ethereumAddress(), + type: "active", + amount: "1", + amountRaw: "1000000000000000000", + pendingActions: [], + token, + isEarning: true, + ...overrides, + } as YieldBalanceDto; +}; + export const yieldFixture = (overrides?: Partial) => Just(getYieldV2ControllerGetYieldByIdResponseMock()) .map( diff --git a/packages/widget/tests/use-cases/__screenshots__/sk-wallet.test.tsx/SK-Wallet-should-work-with-ton-external-provider-1.png b/packages/widget/tests/use-cases/__screenshots__/sk-wallet.test.tsx/SK-Wallet-should-work-with-ton-external-provider-1.png deleted file mode 100644 index 2506470c187b92a366cbbe259b5bd3508ca2e9aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2802 zcmeAS@N?(olHy`uVBq!ia0y~yU}^y33mi;9k$%OnW(EeX<(@8%Ar*7pTs355FyJ__ zLH5V?Gcgy`4JY2b%g%6T7CXZnVFLyh79IvhMrH?x0}KKJqukNZ7)=bLd116D7%d4$ zOTy8TaI_>GEeUB|5;pQPF#P}jVbZN8V9S*a)MRC5c+Sdax8(5yMW6_Sr>mdKI;Vst E0BSEwqyPW_ diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-1.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-1.png deleted file mode 100644 index 3366fc034499378d6df81243adf81edef6b32888..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27548 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

Q*KPv>H{_QIOKyxu)jbs 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�xQrX~PX93$Dd?Uhsq%$uld(J~ml@4zsN2zn4cpn`l=Rv0*> zQTm-1z9uIlvkye?tM+Ml)cQWCyxWt2oz}smXs8$C{JLs&$^C3GU@_$AFKj7a4hMvyIPCQv3&gr9n{7`_Xr+B98g*t`hv6_q-u-9y4S z!^3Wp)eBJR2n6ph?7t&mdJTjZRCX~;)zh43s%QC4$M}Y>!%1|#HLH{QV;-8rkeFN_ zfM8HvKl!@32O@`A?S|iuzKS%%u7Vj}f(#E_S#+>o)HBmM(8MEsVhOdFYGO9Zk91(c zm-J2_=LkXv>LW3kG4ciktaJgTAr4>WD4t^BM&NYK69RnJ8LVOB&?@4pKF01^uVo2IpdF$VvcFk$i*WYVV)tcDE)F+( z{=kqAZZt^v<*(;+mpylJ-wU_KR9-FaUUx2{6hIkzvGDo;DYHi!dkMs^Y@PtWQWRdCA| zkuS}m4Dp!QrGPc5`LYVH?tIOKm12!&^f;{cc$rT2|~Jb85Z=U!`z<)z^co|IL-jrtB% zt8D?rZ{EB?`5iIVMw-8x|KL3u`|aD)^IVDUq!ibo5?64FUTd3vsFL*(or0%Mw(Dv+ zu$)T8dRI3hLY9}8;UFbQR}s z&~ikD3tY1m_r8t~0=p^F)$0~Ib;Rq5%+hL8I(Y+>b7f;Pn$$yc)UfV+ZP>I+QhN?m2>ev>t(kTdYfj>~s9rb@Wqx@6%-v8c{_kZCX{PW%akum+x8~2}@fc{+G z|E(J7&kOSB1^JJjtp9m2{1iYSe*SdgyP1oC9h(h`w;_B*w2d{(y&u58Ag*54yOevu HI`H2B8~$S@ diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-2.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-different-than-gas-token-Txs-gas---gas-token-amount-2.png deleted file mode 100644 index a59ceb285537b89f0e2adc66399b7a72a882e556..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29975 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 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-1.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-1.png deleted file mode 100644 index 680029e043b39b6cdd92156ee7ff1278d8c466cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29451 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 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-2.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-2.png deleted file mode 100644 index 464e83397800a50b477c7d5fdd26c03260ccec77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29594 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 diff --git a/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-3.png b/packages/widget/tests/use-cases/gas-warning-flow/__screenshots__/gas-warning-flow.test.tsx/Gas-warning-flow-Stake-token-same-as-gas-token-Txs-gas---gas-token-amount-3.png deleted file mode 100644 index 5c34b0324d0e5e3c20f73276b424e02214f9805c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29755 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КBsKhe1@W6apwwALk zXe7!jX3T_f1XPOR!*Vz8WvJ8HNmoopGmEHg&qW4byHl_7pR?uR**jB9OJx*hQzvs5 z=Nit6>k8!~CRMX3(r;Q}w;^lGq?mPJmAT|Lus`$-=>h7ty0@(7LyL*iG8R<{YTR#v z3q-vI4iu5nbQf;VMI62LLP%--!+Fo!yuLdv%e7=Ow;dNxJ)k|$2$oMt{8M*`csnGX zldG4;6r&2fN=&7kNJ(QWqfxCXd@;038Bs>da-!M33lsWlW@(c2S}?a7j#`mKb+}-M zOS;uZv8W59Uw=LI)T&V721J);!Ls~vw4t1<`u!>Fx>bJjHqc~}@AMm47oc-Fm%wnh zz0K)p&;As%OLG78f|B}K?c~Y#+7bm9L!YQh^L~4MhAW19@iC($W}o`KJyzbU5~e65 z4sjUFkpsTl4QYrwHT1Fi8A?9UQ_h$jyYm%z_0PviPlJ>vH$}ypK$V)~ph2o( zikIlA$vtYh?YuIkSChdMrdnkjn;6Q}3{bZxF(1(EG(+?IoM**lK4MsqXa@ait;Q@g zxH*_e@x(n-H%ZT7enT*#z7l0M8+-lx4pIgB>})0TRhmRB z@t6w-?O~PY3Rzn7>Fs9YA=_xjR}fQJJLwoncFF1lUp0bzG$|yFqln?=JfsK*BzZ2i zhIYm#tfd8EpY}&&M}Tt77?S$G)c<)Ta}n(uw}+CMf8FXy1{Q}*bk>v61SV|j&@-1g$g8y45yFz96^ zNq{|#BWjjC^8fg#tbaA?{4Eju?;5KAe1QL4f&W~A|6GB8aRm%j94-(DT+y6o3jP}( z_4l9i>)&_<=9CBo!q1=DpM`O&2#Yzliifbq;L^6l*iJJ3hj2pm)UgZ|(<}c26feV) diff --git a/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-2.png b/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-2.png deleted file mode 100644 index d069219e54f9d180e2e6f3288686c9d6ac3020ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29246 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КBsKhe1@W6apwwALk zXe7!jX3T_f1XPOR!*Vz8WvJ8HNmoopGmEHg&qW4byHl_7pR?uR**jB9OJx*hQzvs5 z=Nit6>k8!~CRMX3(r;Q}w;^lGq?mPJmAT|Lus`$-=>h7ty0@(7LyL*iG8R<{YTR#v z3q-vI4iu5nbQf;VMI62LLP%--!+Fo!yuLdv%e7=Ow;dNxJ)k|$2$oMt{8M*`csnGX zldG4;6r&2fN=&7kNJ(QWqfxCXd@;038Bs>da-!M33lsWlW@(c2S}?a7j#`mKb+}-M zOS;uZv8W59Uw=LI)T&V721J);!Ls~vw4t1<`u!>Fx>bJjHqc~}@AMm47oc-Fm%wnh zz0K)p&;As%OLG78f|B}K?c~Y#+7bm9L!YQh^L~4MhAW19@iC($W}o`KJyzbU5~e65 z4sjUFkpsTl4QYrwHT1Fi8A?9UQ_h$jyYm%z_0PviPlJ>vH$}ypK$V)~ph2o( zikIlA$vtYh?YuIkSChdMrdnkjn;6Q}3{bZxF(1(EG(+?IoM**lK4MsqXa@ait;Q@g zxH*_e@x(n-H%ZT7enT*#z7l0M8+-lx4pIgB>})0TRhmRB z@t6w-?O~PY3Rzn7>Fs9YA=_xjR}fQJJLwoncFF1lUp0bzG$|yFqln?=JfsK*BzZ2i zhIYm#tfd8EpY}&&M}Tt77?S$G)c<)Ta}n(uw}+CMf8FXy1{Q}*bk>v61SV|j&@-1g$g8y45yFz96^ zNq{|#BWjjC^8fg#tbaA?{4Eju?;5KAe1QL4f&W~A|6GB8aRm%j94-(DT+y6o3jP}( z_4l9i>)&_<=9CBo!q1=DpM`O&2#Yzliifbq;L^6l*iJJ3hj2pm)UgZ|(<}c26feV) diff --git a/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-3.png b/packages/widget/tests/use-cases/staking-flow/__screenshots__/staking-flow.test.tsx/Staking-flow-Works-as-expected-3.png deleted file mode 100644 index d069219e54f9d180e2e6f3288686c9d6ac3020ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29246 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КBsKhe1@W6apwwALk zXe7!jX3T_f1XPOR!*Vz8WvJ8HNmoopGmEHg&qW4byHl_7pR?uR**jB9OJx*hQzvs5 z=Nit6>k8!~CRMX3(r;Q}w;^lGq?mPJmAT|Lus`$-=>h7ty0@(7LyL*iG8R<{YTR#v z3q-vI4iu5nbQf;VMI62LLP%--!+Fo!yuLdv%e7=Ow;dNxJ)k|$2$oMt{8M*`csnGX zldG4;6r&2fN=&7kNJ(QWqfxCXd@;038Bs>da-!M33lsWlW@(c2S}?a7j#`mKb+}-M zOS;uZv8W59Uw=LI)T&V721J);!Ls~vw4t1<`u!>Fx>bJjHqc~}@AMm47oc-Fm%wnh zz0K)p&;As%OLG78f|B}K?c~Y#+7bm9L!YQh^L~4MhAW19@iC($W}o`KJyzbU5~e65 z4sjUFkpsTl4QYrwHT1Fi8A?9UQ_h$jyYm%z_0PviPlJ>vH$}ypK$V)~ph2o( zikIlA$vtYh?YuIkSChdMrdnkjn;6Q}3{bZxF(1(EG(+?IoM**lK4MsqXa@ait;Q@g zxH*_e@x(n-H%ZT7enT*#z7l0M8+-lx4pIgB>})0TRhmRB z@t6w-?O~PY3Rzn7>Fs9YA=_xjR}fQJJLwoncFF1lUp0bzG$|yFqln?=JfsK*BzZ2i zhIYm#tfd8EpY}&&M}Tt77?S$G)c<)Ta}n(uw}+CMf8FXy1{Q}*bk>v61SV|j&@-1g$g8y45yFz96^ zNq{|#BWjjC^8fg#tbaA?{4Eju?;5KAe1QL4f&W~A|6GB8aRm%j94-(DT+y6o3jP}( z_4l9i>)&_<=9CBo!q1=DpM`O&2#Yzliifbq;L^6l*iJJ3hj2pm)UgZ|(<}c26feV) diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts new file mode 100644 index 00000000..cc6302cb --- /dev/null +++ b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts @@ -0,0 +1,334 @@ +import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; +import { delay, HttpResponse, http } from "msw"; +import { avalanche } from "viem/chains"; +import { vitest } from "vitest"; +import type { + YieldDto as YieldApiYieldDto, + YieldBalanceDto, + YieldRewardRateDto, +} from "../../../src/providers/yield-api-client-provider/types"; +import { waitForMs } from "../../../src/utils"; +import { + yieldApiYieldFixture, + yieldBalanceFixture, + yieldFixture, + yieldRewardRateFixture, +} from "../../fixtures"; +import { worker } from "../../mocks/worker"; +import { rkMockWallet } from "../../utils/mock-connector"; + +const setUrl = ({ + accountId, + balanceId, + yieldId, +}: { + accountId?: string; + balanceId?: string; + yieldId?: string; +}) => { + const searchParams = new URLSearchParams(); + + if (accountId) { + searchParams.set("accountId", accountId); + } + + if (balanceId) { + searchParams.set("balanceId", balanceId); + } + + if (yieldId) { + searchParams.set("yieldId", yieldId); + } + + const url = new URL(window.location.href); + url.search = searchParams.toString(); + window.history.pushState({}, "", url); +}; + +export const setup = async () => { + const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; + + const token: TokenDto = { + name: "USDA", + symbol: "USDA", + decimals: 18, + network: "avalanche-c", + coinGeckoId: "angle-usd", + logoURI: "https://assets.stakek.it/tokens/usda.svg", + }; + + const rewardToken: TokenDto = { + name: "United Stables", + symbol: "U", + decimals: 18, + network: token.network, + address: "0x58D97B57BB95320F9a05dC918Aef65434969c2B2", + logoURI: "https://assets.stakek.it/tokens/usda.svg", + }; + + const morphoToken: TokenDto = { + name: "Morpho Token", + symbol: "MORPHO", + decimals: 18, + network: token.network, + address: "0x58D97B57BB95320F9a05dC918Aef65434969c2B3", + logoURI: "https://assets.stakek.it/tokens/usda.svg", + }; + + const discoveryRewardRate: YieldRewardRateDto = yieldRewardRateFixture({ + total: 0.045507546653006034, + rateType: "APY", + components: [ + { + rate: 0.0028386677110199426, + rateType: "APR", + token: morphoToken, + yieldSource: "protocol_incentive", + description: "MORPHO rewards", + }, + { + rate: 0.002, + rateType: "APR", + token: rewardToken, + yieldSource: "campaign_incentive", + description: "U rewards", + }, + { + rate: 0.042668878941986094, + rateType: "APY", + token: rewardToken, + yieldSource: "vault", + description: "Supply APY", + }, + ], + }); + + const personalizedRewardRate: YieldRewardRateDto = yieldRewardRateFixture({ + total: 0.04530754665300604, + rateType: "APY", + components: [ + { + rate: 0.0028386677110199426, + rateType: "APR", + token: morphoToken, + yieldSource: "protocol_incentive", + description: "MORPHO rewards", + }, + { + rate: 0.0018, + rateType: "APR", + token: rewardToken, + yieldSource: "campaign_incentive", + description: "U rewards", + }, + { + rate: 0.042668878941986094, + rateType: "APY", + token: rewardToken, + yieldSource: "vault", + description: "Supply APY", + }, + ], + }); + + const yieldId = + "avalanche-c-usda-trust-0xbeefa1abfebe621df50ceaef9f54fdb73648c92c-vault"; + + const legacyYieldBase = yieldFixture(); + const rawYieldBase = yieldApiYieldFixture(); + + const legacyYield: YieldDto = { + ...legacyYieldBase, + id: yieldId, + token, + tokens: [token], + rewardRate: discoveryRewardRate.total, + rewardType: "apy", + apy: discoveryRewardRate.total, + validators: [], + feeConfigurations: [], + args: { + enter: { + args: { + amount: { + required: true, + minimum: 0, + }, + }, + }, + exit: { + args: { + amount: { + required: true, + minimum: 0, + }, + }, + }, + }, + metadata: { + ...legacyYieldBase.metadata, + name: "Trust USDA Earn", + type: "vault", + token, + rewardTokens: undefined, + gasFeeToken: token, + provider: { + id: legacyYieldBase.metadata.provider?.id ?? "benqi", + name: "Trust", + description: "", + externalLink: "https://trustwallet.com", + logoURI: "https://assets.stakek.it/providers/benqi.svg", + }, + }, + status: { + enter: true, + exit: true, + }, + }; + + const rawYield: YieldApiYieldDto = { + ...rawYieldBase, + id: yieldId, + token, + tokens: [token], + inputTokens: [token], + outputToken: { + ...token, + symbol: "steakUSDA", + name: "Steakhouse USDA", + }, + network: token.network, + chainId: `${avalanche.id}`, + providerId: rawYieldBase.providerId, + rewardRate: discoveryRewardRate, + metadata: { + ...(rawYieldBase.metadata ?? {}), + name: "Trust USDA Earn", + description: "Trust campaign vault", + logoURI: "https://assets.stakek.it/providers/benqi.svg", + }, + mechanics: { + ...(rawYieldBase.mechanics ?? {}), + type: "vault", + gasFeeToken: token, + rewardClaiming: "auto", + rewardSchedule: "day", + requiresValidatorSelection: false, + supportsLedgerWalletApi: true, + }, + status: { + enter: true, + exit: true, + }, + }; + + const activeBalance: YieldBalanceDto = yieldBalanceFixture({ + address: account, + type: "active", + amount: "1000251.8279906842", + amountRaw: "1000251827990684200000000", + amountUsd: "1000355.009527", + isEarning: true, + token, + pendingActions: [], + }); + + worker.use( + http.get("*/v1/yields/enabled/networks", async () => { + await delay(); + return HttpResponse.json([token.network]); + }), + http.get("*/v1/tokens", async () => { + await delay(); + return HttpResponse.json([ + { + token, + availableYields: [yieldId], + }, + ]); + }), + http.post("*/v1/tokens/balances/scan", async () => { + await delay(); + return HttpResponse.json([ + { + token, + amount: "1000251.8279906842", + availableYields: [yieldId], + }, + ]); + }), + http.post("*/v1/tokens/balances", async () => { + await delay(); + return HttpResponse.json([ + { + token, + amount: "1000251.8279906842", + }, + ]); + }), + http.post("*/v1/tokens/prices", async () => { + await delay(); + return HttpResponse.json({ + "avalanche-c-undefined": { + price: 1, + price_24_h: 0, + }, + }); + }), + http.get(`*/v1/yields/${yieldId}`, async ({ request }) => { + await delay(); + + const url = new URL(request.url); + + return HttpResponse.json( + url.searchParams.has("ledgerWalletAPICompatible") + ? legacyYield + : rawYield + ); + }), + http.get("*/v1/yields/:yieldId/validators", async () => { + await delay(); + return HttpResponse.json({ + items: [], + total: 0, + }); + }), + http.post("*/v1/yields/balances", async () => { + await delay(); + return HttpResponse.json({ + items: [ + { + yieldId, + balances: [activeBalance], + rewardRate: personalizedRewardRate, + }, + ], + errors: [], + }); + }) + ); + + const requestFn = vitest.fn(async ({ method }: { method: string }) => { + await waitForMs(100); + + switch (method) { + case "eth_chainId": + return avalanche.id; + case "eth_requestAccounts": + return [account]; + default: + throw new Error("unhandled method"); + } + }); + + const customConnectors = rkMockWallet({ accounts: [account], requestFn }); + + return { + account, + customConnectors, + setUrl, + legacyYield, + discoveryRewardRate, + personalizedRewardRate, + }; +}; diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx new file mode 100644 index 00000000..d955411d --- /dev/null +++ b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx @@ -0,0 +1,105 @@ +import { describe, expect, it } from "vitest"; +import { renderApp } from "../../utils/test-utils"; +import { setup } from "./setup"; + +describe("Trust incentive APY", () => { + it("shows APY composition during discovery", async () => { + const { account, customConnectors, legacyYield, setUrl } = await setup(); + + setUrl({ + accountId: account, + yieldId: legacyYield.id, + }); + + const app = await renderApp({ + wagmi: { + __customConnectors__: customConnectors, + }, + }); + + await expect + .element(app.getByTestId("estimated-reward__percent").getByText("4.55%")) + .toBeInTheDocument(); + + await expect.element(app.getByText("APY composition")).toBeInTheDocument(); + await expect + .element( + app.getByTestId("reward-rate-breakdown__native").getByText("4.27%") + ) + .toBeInTheDocument(); + await expect + .element( + app + .getByTestId("reward-rate-breakdown__protocol-incentive") + .getByText("0.28%") + ) + .toBeInTheDocument(); + await expect + .element( + app + .getByTestId("reward-rate-breakdown__campaign") + .getByText("Up to 0.20%") + ) + .toBeInTheDocument(); + + await app.getByTestId("select-opportunity").click(); + + const selectContainer = app.getByTestId("select-modal__container"); + + await expect + .element(selectContainer.getByText("Trust USDA Earn")) + .toBeInTheDocument(); + await expect + .element(selectContainer.getByText("4.55%")) + .toBeInTheDocument(); + await expect + .element(selectContainer.getByText("Up to 0.20%")) + .toBeInTheDocument(); + + app.unmount(); + }); + + it("shows personalized APY on the position details page", async () => { + const { account, customConnectors, legacyYield, setUrl } = await setup(); + + setUrl({ + accountId: account, + balanceId: "default", + yieldId: legacyYield.id, + }); + + const app = await renderApp({ + wagmi: { + __customConnectors__: customConnectors, + }, + }); + + await expect.element(app.getByText("Personalized APY")).toBeInTheDocument(); + await expect + .element(app.getByTestId("personalized-reward-rate").getByText("4.53%")) + .toBeInTheDocument(); + await expect + .element( + app + .getByTestId("personalized-reward-rate-breakdown__native") + .getByText("4.27%") + ) + .toBeInTheDocument(); + await expect + .element( + app + .getByTestId("personalized-reward-rate-breakdown__protocol-incentive") + .getByText("0.28%") + ) + .toBeInTheDocument(); + await expect + .element( + app + .getByTestId("personalized-reward-rate-breakdown__campaign") + .getByText("0.18%") + ) + .toBeInTheDocument(); + + app.unmount(); + }); +}); From 6b49e39e8f8f37eb804bd2647689d20c81ca55ce Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Fri, 20 Mar 2026 16:34:52 +0100 Subject: [PATCH 07/23] feat: useMaxAmount --- .../state/earn-page-state-context.tsx | 6 + .../pages/details/earn-page/state/types.ts | 1 + .../state/use-stake-enter-request-dto.ts | 3 + .../hooks/use-stake-exit-request-dto.ts | 10 +- .../pages/position-details/state/index.tsx | 11 +- .../src/pages/position-details/state/types.ts | 1 + packages/widget/src/worker.ts | 447 +++++++++++------- 7 files changed, 298 insertions(+), 181 deletions(-) diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx index 371a6c7c..72c34d2d 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx @@ -50,6 +50,7 @@ const getInitialState = (): State => ({ selectedStakeId: Maybe.empty(), selectedValidators: new Map(), stakeAmount: new BigNumber(0), + useMaxAmount: false, tronResource: Maybe.empty(), selectedProviderYieldId: Maybe.empty(), }); @@ -150,6 +151,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { return { ...state, stakeAmount: action.data, + useMaxAmount: false, }; } @@ -157,6 +159,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { return { ...state, stakeAmount: action.data, + useMaxAmount: true, }; } @@ -180,6 +183,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { selectedStakeId, selectedValidators, stakeAmount: _stakeAmount, + useMaxAmount, tronResource, selectedProviderYieldId, } = state; @@ -340,6 +344,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { selectedStake, selectedValidators, stakeAmount, + useMaxAmount, actions, tronResource, stakeAmountGreaterThanAvailableAmount, @@ -358,6 +363,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { selectedToken, selectedValidators, stakeAmount, + useMaxAmount, actions, tronResource, stakeAmountGreaterThanAvailableAmount, diff --git a/packages/widget/src/pages/details/earn-page/state/types.ts b/packages/widget/src/pages/details/earn-page/state/types.ts index b81bc4bd..54ebd1e0 100644 --- a/packages/widget/src/pages/details/earn-page/state/types.ts +++ b/packages/widget/src/pages/details/earn-page/state/types.ts @@ -20,6 +20,7 @@ export type State = { >; selectedValidators: Map; stakeAmount: BigNumber; + useMaxAmount: boolean; tronResource: Maybe; selectedProviderYieldId: Maybe; }; diff --git a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts index ac884270..c2c98736 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts @@ -10,6 +10,7 @@ export const useStakeEnterRequestDto = () => { const { selectedStake, stakeAmount, + useMaxAmount, selectedValidators, tronResource, selectedToken, @@ -83,6 +84,7 @@ export const useStakeEnterRequestDto = () => { ledgerWalletApiCompatible: isLedgerLive ?? undefined, tronResource: tronResource.extract(), amount: stakeAmount.toString(10), + useMaxAmount: useMaxAmount || undefined, providerId: selectedProviderYieldId.extract(), ...validatorsOrProvider, }, @@ -98,6 +100,7 @@ export const useStakeEnterRequestDto = () => { selectedToken, selectedValidators, stakeAmount, + useMaxAmount, tronResource, selectedProviderYieldId, ] 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 adf7ec3f..a3615b46 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 @@ -8,8 +8,12 @@ import { useUnstakeOrPendingActionState } from "../state"; export const useStakeExitRequestDto = () => { const { address, additionalAddresses } = useSKWallet(); - const { unstakeAmount, integrationData, stakedOrLiquidBalances } = - useUnstakeOrPendingActionState(); + const { + unstakeAmount, + unstakeUseMaxAmount, + integrationData, + stakedOrLiquidBalances, + } = useUnstakeOrPendingActionState(); return useMemo( () => @@ -92,6 +96,7 @@ export const useStakeExitRequestDto = () => { additionalAddresses, argumentsDto: { amount: unstakeAmount.toString(10), + useMaxAmount: unstakeUseMaxAmount || undefined, ...validatorsOrProvider, }, }), @@ -104,6 +109,7 @@ export const useStakeExitRequestDto = () => { stakedOrLiquidBalances, integrationData, unstakeAmount, + unstakeUseMaxAmount, ] ); }; diff --git a/packages/widget/src/pages/position-details/state/index.tsx b/packages/widget/src/pages/position-details/state/index.tsx index 92010387..bbd7d34c 100644 --- a/packages/widget/src/pages/position-details/state/index.tsx +++ b/packages/widget/src/pages/position-details/state/index.tsx @@ -233,6 +233,7 @@ export const UnstakeOrPendingActionProvider = ({ return { ...state, unstakeAmount: action.data, + unstakeUseMaxAmount: false, }; } @@ -240,6 +241,7 @@ export const UnstakeOrPendingActionProvider = ({ return { ...state, unstakeAmount: maxEnterOrExitAmount, + unstakeUseMaxAmount: true, }; } @@ -260,10 +262,15 @@ export const UnstakeOrPendingActionProvider = ({ const [state, dispatch] = useReducer(reducer, { unstakeAmount: minEnterOrExitAmount, + unstakeUseMaxAmount: false, pendingActions: new Map(), }); - const { pendingActions, unstakeAmount: _ustankeAmount } = state; + const { + pendingActions, + unstakeAmount: _ustankeAmount, + unstakeUseMaxAmount, + } = state; const unstakeAmount = useMemo( () => @@ -335,6 +342,7 @@ export const UnstakeOrPendingActionProvider = ({ unstakeAmountError, unstakeToken, unstakeAmount, + unstakeUseMaxAmount, pendingActions, positionBalancePrices, reducedStakedOrLiquidBalance, @@ -353,6 +361,7 @@ export const UnstakeOrPendingActionProvider = ({ unstakeAmountError, unstakeToken, unstakeAmount, + unstakeUseMaxAmount, pendingActions, positionBalancePrices, reducedStakedOrLiquidBalance, diff --git a/packages/widget/src/pages/position-details/state/types.ts b/packages/widget/src/pages/position-details/state/types.ts index cba4dba7..91da291d 100644 --- a/packages/widget/src/pages/position-details/state/types.ts +++ b/packages/widget/src/pages/position-details/state/types.ts @@ -38,6 +38,7 @@ export type Actions = export type State = { unstakeAmount: BigNumber; + unstakeUseMaxAmount: boolean; pendingActions: Map; }; diff --git a/packages/widget/src/worker.ts b/packages/widget/src/worker.ts index e4608e7e..fb4b7385 100644 --- a/packages/widget/src/worker.ts +++ b/packages/widget/src/worker.ts @@ -1,190 +1,281 @@ import { HttpResponse, http } from "msw"; import { setupWorker } from "msw/browser"; -// const validAddressAndNetwork = { -// address: "akash12z0hpqxj3txaf85zlla7zqffp7n9sl8wc3hlzh", -// network: "akash", -// }; +const yieldId = "ethereum-matic-native-staking"; + +const maticToken = { + address: "0x0000000000000000000000000000000000001010", + symbol: "POL", + name: "Polygon Ecosystem Token", + decimals: 18, + network: "ethereum", + coinGeckoId: "matic-network", + logoURI: "https://assets.stakek.it/tokens/matic.svg", + isPoints: false, +}; + +const morphoToken = { + address: "0x58d97b57bb95320f9a05dc918aef65434969c2b3", + symbol: "MORPHO", + name: "Morpho Token", + decimals: 18, + network: "ethereum", + isPoints: false, +}; + +const campaignToken = { + address: "0x58d97b57bb95320f9a05dc918aef65434969c2b2", + symbol: "U", + name: "United Stables", + decimals: 18, + network: "ethereum", + isPoints: false, +}; + +const discoveryRewardRate = { + total: 0.045507546653006034, + rateType: "APY", + components: [ + { + rate: 0.0028386677110199426, + rateType: "APR", + token: morphoToken, + yieldSource: "protocol_incentive", + description: "MORPHO rewards", + }, + { + rate: 0.002, + rateType: "APR", + token: campaignToken, + yieldSource: "campaign_incentive", + description: "U rewards", + }, + { + rate: 0.042668878941986094, + rateType: "APY", + token: campaignToken, + yieldSource: "staking", + description: "Native staking APY", + }, + ], +}; + +const personalizedRewardRate = { + total: 0.04530754665300604, + rateType: "APY", + components: [ + { + rate: 0.0028386677110199426, + rateType: "APR", + token: morphoToken, + yieldSource: "protocol_incentive", + description: "MORPHO rewards", + }, + { + rate: 0.0018, + rateType: "APR", + token: campaignToken, + yieldSource: "campaign_incentive", + description: "U rewards", + }, + { + rate: 0.042668878941986094, + rateType: "APY", + token: campaignToken, + yieldSource: "staking", + description: "Native staking APY", + }, + ], +}; + +const legacyYieldDto = { + id: yieldId, + token: maticToken, + tokens: [maticToken], + rewardRate: discoveryRewardRate.total, + rewardType: "apy", + apy: discoveryRewardRate.total, + feeConfigurations: [], + args: { + enter: { + addresses: { + address: { + required: true, + network: "ethereum", + }, + }, + args: { + amount: { + required: true, + minimum: 0, + }, + }, + }, + exit: { + addresses: { + address: { + required: true, + network: "ethereum", + }, + }, + args: { + amount: { + required: true, + minimum: 0, + }, + }, + }, + }, + metadata: { + name: "Trust POL Staking", + description: "Local mock for campaign APY QA", + documentation: "https://trustwallet.com", + logoURI: "https://assets.stakek.it/tokens/matic.svg", + type: "staking", + token: maticToken, + tokens: [maticToken], + rewardTokens: [campaignToken, morphoToken], + rewardClaiming: "auto", + rewardSchedule: "day", + gasFeeToken: maticToken, + fee: { + enabled: false, + depositFee: false, + managementFee: false, + performanceFee: false, + }, + provider: { + id: "benqi", + name: "Trust", + description: "", + externalLink: "https://trustwallet.com", + logoURI: "https://assets.stakek.it/providers/benqi.svg", + }, + supportsLedgerWalletApi: true, + supportsMultipleValidators: false, + }, + status: { + enter: true, + exit: true, + }, + validators: [], +}; + +const yieldApiYieldDto = { + id: yieldId, + token: maticToken, + tokens: [maticToken], + inputTokens: [maticToken], + outputToken: maticToken, + network: "ethereum", + chainId: "1", + providerId: "benqi", + rewardRate: discoveryRewardRate, + metadata: { + name: "Trust POL Staking", + description: "Local mock for campaign APY QA", + documentation: "https://trustwallet.com", + logoURI: "https://assets.stakek.it/tokens/matic.svg", + }, + mechanics: { + type: "staking", + gasFeeToken: maticToken, + rewardClaiming: "auto", + rewardSchedule: "day", + supportsLedgerWalletApi: true, + requiresValidatorSelection: false, + arguments: { + enter: { + fields: [ + { + name: "amount", + type: "string", + label: "Amount", + required: true, + minimum: "0", + }, + ], + }, + exit: { + fields: [ + { + name: "amount", + type: "string", + label: "Amount", + required: true, + minimum: "0", + }, + ], + }, + }, + }, + status: { + enter: true, + exit: true, + }, +}; export const worker = setupWorker( + http.get("*/v1/yields/ethereum-matic-native-staking", async ({ request }) => { + const url = new URL(request.url); + + return HttpResponse.json( + url.searchParams.has("ledgerWalletAPICompatible") + ? legacyYieldDto + : yieldApiYieldDto + ); + }), + http.post("*/v1/yields/balances", async () => { + return HttpResponse.json({ + items: [ + { + yieldId, + balances: [ + { + address: "0x15775b23340c0f50e0428d674478b0e9d3d0a759", + amount: "1000251.8279906842", + amountRaw: "10002518279906842", + type: "active", + token: maticToken, + pendingActions: [], + amountUsd: "1000355.009527", + isEarning: true, + }, + ], + rewardRate: personalizedRewardRate, + }, + ], + errors: [], + }); + }), + http.post("*/v1/balances", async () => { + return HttpResponse.json({ + items: [ + { + yieldId, + balances: [ + { + address: "0x15775b23340c0f50e0428d674478b0e9d3d0a759", + amount: "1000251.8279906842", + amountRaw: "10002518279906842", + type: "active", + token: maticToken, + pendingActions: [], + amountUsd: "1000355.009527", + isEarning: true, + }, + ], + rewardRate: personalizedRewardRate, + }, + ], + errors: [], + }); + }), http.post("*/v1/actions/enter/estimate-gas", async () => { return HttpResponse.json({ amount: "0.1", - token: { - network: "polygon", - coinGeckoId: "matic-network", - name: "Polygon", - decimals: 18, - symbol: "MATIC", - logoURI: "https://assets.stakek.it/tokens/matic.svg", - }, + token: maticToken, gasLimit: "", }); }) - // http.get("*/v1/yields/celo-celo-native-staking", async () => { - // await delay(); - - // return HttpResponse.json({ - // id: "celo-celo-native-staking", - // token: { - // name: "Celo", - // symbol: "CELO", - // decimals: 18, - // address: "0x471EcE3750Da237f93B8E339c536989b8978a438", - // network: "celo", - // coinGeckoId: "celo", - // logoURI: "https://assets.stakek.it/tokens/celo.svg", - // }, - // tokens: [ - // { - // name: "Celo", - // symbol: "CELO", - // decimals: 18, - // address: "0x471EcE3750Da237f93B8E339c536989b8978a438", - // network: "celo", - // coinGeckoId: "celo", - // logoURI: "https://assets.stakek.it/tokens/celo.svg", - // }, - // ], - // args: { - // enter: { - // addresses: { - // address: { - // required: true, - // network: "celo", - // }, - // }, - // args: { - // amount: { - // required: true, - // minimum: 0, - // }, - // validatorAddress: { - // required: true, - // }, - // }, - // }, - // exit: { - // addresses: { - // address: { - // required: true, - // network: "celo", - // }, - // }, - // args: { - // amount: { - // required: true, - // minimum: 0, - // }, - // validatorAddress: { - // required: true, - // }, - // signatureVerification: { - // required: true, - // }, - // }, - // }, - // }, - // status: { - // enter: true, - // exit: true, - // }, - // apy: 0.03992371968603679, - // rewardRate: 0.03992371968603679, - // rewardType: "apy", - // metadata: { - // cooldownPeriod: { - // days: 3, - // }, - // defaultValidator: "0xdadbd6cfb29b054adc9c4c2ef0f21f0bbdb44871", - // description: "Stake your CELO natively", - // fee: { - // enabled: false, - // }, - // gasFeeToken: { - // name: "Celo", - // symbol: "CELO", - // decimals: 18, - // address: "0x471EcE3750Da237f93B8E339c536989b8978a438", - // network: "celo", - // coinGeckoId: "celo", - // logoURI: "https://assets.stakek.it/tokens/celo.svg", - // }, - // id: "celo-celo-native-staking", - // logoURI: "https://assets.stakek.it/tokens/celo.svg", - // minimumStake: 0, - // name: "CELO Native Staking", - // revshare: { - // enabled: true, - // }, - // rewardClaiming: "auto", - // rewardSchedule: "day", - // supportsMultipleValidators: true, - // token: { - // name: "Celo", - // symbol: "CELO", - // decimals: 18, - // address: "0x471EcE3750Da237f93B8E339c536989b8978a438", - // network: "celo", - // coinGeckoId: "celo", - // logoURI: "https://assets.stakek.it/tokens/celo.svg", - // }, - // type: "staking", - // warmupPeriod: { - // days: 1, - // }, - // documentation: "https://docs.stakek.it/docs/celo-celo-native-staking", - // supportsLedgerWalletApi: true, - // isUnderMaintenance: false, - // ledgerClearSigning: true, - // contractAddresses: [ - // "0x7d21685C17607338b313a7174bAb6620baD0aaB7", - // "0x8D6677192144292870907E3Fa8A5527fE55A7ff6", - // "0x6cC083Aed9e3ebe302A6336dBC7c921C9f03349E", - // ], - // }, - // validators: [ - // { - // address: "0xe92b7ba8497486e94bb59c51f595b590c4a5f894", - // status: "active", - // name: "Stakely", - // image: "https://assets.stakek.it/validators/stakely.png", - // website: "https://stakely.io/", - // apr: 0.0393, - // commission: 0.1, - // stakedBalance: "2263157", - // votingPower: 0.0090962642447408, - // preferred: true, - // }, - // { - // address: "0x81cef0668e15639d0b101bdc3067699309d73bed", - // status: "active", - // name: "Chorus One", - // image: "https://assets.stakek.it/validators/chorus_one.png", - // website: "https://chorus.one/", - // apr: 0.04015260366943412, - // commission: 0.075, - // stakedBalance: "4056456", - // votingPower: 0.0163040370920639, - // preferred: true, - // }, - // ], - // isAvailable: true, - // }); - // }) ); - -// http.post("*/v1/actions/enter", async () => { -// await delay(); -// return HttpResponse.json( -// { -// message: "YieldUnderMaintenanceError", -// details: { yieldId: "optimism-op-aave-v3-lending" }, -// }, -// { status: 400 } -// ); -// }), - -// http.all("*", () => { -// return passthrough(); -// }) From 3476aa6341e732a0ec7080310ad7778ce874631b Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Fri, 20 Mar 2026 16:37:10 +0100 Subject: [PATCH 08/23] fix: format --- .../molecules/select-opportunity-list-item/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx index f7b416fe..ecde1dee 100644 --- a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx +++ b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx @@ -43,7 +43,9 @@ export const SelectOpportunityListItem = ({ }); const primaryRateFormatted = getRewardRateFormatted({ - rewardRate: campaignRate ? item.rewardRate - campaignRate.rate : item.rewardRate, + rewardRate: campaignRate + ? item.rewardRate - campaignRate.rate + : item.rewardRate, rewardType: item.rewardType, }); From eb6656f22cd96a6bf34058df0a987f3aba5b39d0 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Mon, 23 Mar 2026 12:10:45 +0100 Subject: [PATCH 09/23] fix: misc --- packages/widget/src/domain/types/yields.ts | 18 +-------- .../src/hooks/api/use-yield-balances-scan.ts | 19 --------- .../get-yield-opportunity.ts | 5 +-- .../widget/src/hooks/use-positions-data.ts | 7 +--- .../widget/src/hooks/use-under-maintenance.ts | 40 ++++++++++++++++--- .../src/providers/api/get-enabled-networks.ts | 13 +++--- .../widget/src/providers/cosmos/config.ts | 5 ++- .../widget/src/providers/ethereum/config.ts | 5 ++- .../widget/src/providers/substrate/config.ts | 5 ++- packages/widget/src/providers/wagmi/index.ts | 8 +++- .../yield-api-client-provider/actions.ts | 22 +++++----- .../yield-api-client-provider/index.tsx | 4 +- .../yield-api-client-provider/types.ts | 5 ++- 13 files changed, 85 insertions(+), 71 deletions(-) diff --git a/packages/widget/src/domain/types/yields.ts b/packages/widget/src/domain/types/yields.ts index 1cd9b66a..409ba363 100644 --- a/packages/widget/src/domain/types/yields.ts +++ b/packages/widget/src/domain/types/yields.ts @@ -2,7 +2,7 @@ import type { ValidatorDto, YieldDto, YieldType } from "@stakekit/api-hooks"; import { EvmNetworks } from "@stakekit/common"; import BigNumber from "bignumber.js"; import type { TFunction } from "i18next"; -import { List, Maybe } from "purify-ts"; +import { Maybe } from "purify-ts"; import type { SupportedSKChains } from "./chains"; export type ExtendedYieldType = YieldType | "native_staking" | "pooled_staking"; @@ -197,21 +197,5 @@ export const isNonZeroRewardRateYield = ( yieldDto: Pick ) => yieldDto.rewardRate > 0 || zeroRewardRateYieldIdWhitelist.has(yieldDto.id); -export const getComputedRewardRate = (yieldDto: YieldDto) => { - const liveFeeConfigurations = yieldDto.feeConfigurations.filter( - (fc) => fc.status === "LIVE" - ); - - const firstLiveFeeConfiguration = List.head( - liveFeeConfigurations - ).extractNullable(); - - if (liveFeeConfigurations.length === 1 && firstLiveFeeConfiguration) { - return firstLiveFeeConfiguration.computedRewardRate; - } - - return yieldDto.rewardRate; -}; - export const isERC4626 = (yieldDto: YieldDto) => yieldDto.metadata.supportedStandards?.includes("ERC4626") ?? false; 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 a9063875..99ad6117 100644 --- a/packages/widget/src/hooks/api/use-yield-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-yield-balances-scan.ts @@ -6,7 +6,6 @@ import { useSKWallet } from "../../providers/sk-wallet"; import { useActionHistoryData } from "../../providers/stake-history"; import { useYieldApiClient } from "../../providers/yield-api-client-provider"; import type { - YieldBalanceDto, YieldBalancesByYieldDto, YieldBalancesRequestDto, } from "../../providers/yield-api-client-provider/types"; @@ -104,21 +103,3 @@ export const useInvalidateYieldBalances = () => { const getYieldYieldBalancesScanQueryKey = () => ["post", "/v1/yields/balances"] as const; - -const normalizeYieldBalanceForPosition = ( - balance: YieldBalanceDto -): YieldBalanceDto => ({ - ...balance, - amount: balance.shareAmount ?? balance.amount, -}); - -export const normalizeYieldBalancesForPosition = ( - balances: YieldBalancesByYieldDto[] -): YieldBalancesByYieldDto[] => - balances.map((balanceByYield) => ({ - ...balanceByYield, - balances: balanceByYield.balances.map(normalizeYieldBalanceForPosition), - outputTokenBalance: balanceByYield.outputTokenBalance - ? normalizeYieldBalanceForPosition(balanceByYield.outputTokenBalance) - : balanceByYield.outputTokenBalance, - })); diff --git a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts index 425c7d59..3c98ddc2 100644 --- a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts +++ b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts @@ -1,17 +1,16 @@ import type { YieldDto } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; -import type { Client } from "openapi-fetch"; import { EitherAsync } from "purify-ts"; import { yieldYieldOpportunity } from "../../../common/private-api"; import { isEthenaUsdeStaking } from "../../../domain/types/yields"; import { adaptYieldDto } from "../../../providers/yield-api-client-provider/compat"; import { getResponseData } from "../../../providers/yield-api-client-provider/request-helpers"; -import type { paths } from "../../../types/yield-api-schema"; +import type { YieldApiFetchClient } from "../../../providers/yield-api-client-provider/types"; type Params = { yieldId: string; isLedgerLive: boolean; - yieldApiFetchClient: Client; + yieldApiFetchClient: YieldApiFetchClient; signal?: AbortSignal; }; diff --git a/packages/widget/src/hooks/use-positions-data.ts b/packages/widget/src/hooks/use-positions-data.ts index 657ecdde..f88c60e3 100644 --- a/packages/widget/src/hooks/use-positions-data.ts +++ b/packages/widget/src/hooks/use-positions-data.ts @@ -9,10 +9,7 @@ import type { YieldBalanceDto, YieldBalancesByYieldDto, } from "../providers/yield-api-client-provider/types"; -import { - normalizeYieldBalancesForPosition, - useYieldBalancesScan, -} from "./api/use-yield-balances-scan"; +import { useYieldBalancesScan } from "./api/use-yield-balances-scan"; export const usePositionsData = () => { const { data, ...rest } = useYieldBalancesScan({ @@ -30,7 +27,7 @@ export const usePositionsData = () => { const positionsDataSelector = createSelector( (balancesData: YieldBalancesByYieldDto[]) => balancesData, (balancesData) => - normalizeYieldBalancesForPosition(balancesData).reduce((acc, val) => { + balancesData.reduce((acc, val) => { acc.set(val.yieldId, { yieldId: val.yieldId, rewardRate: val.rewardRate, diff --git a/packages/widget/src/hooks/use-under-maintenance.ts b/packages/widget/src/hooks/use-under-maintenance.ts index 1ab719e3..d2c04b70 100644 --- a/packages/widget/src/hooks/use-under-maintenance.ts +++ b/packages/widget/src/hooks/use-under-maintenance.ts @@ -1,11 +1,41 @@ -import { type HealthStatusDto, useHealthHealthV2 } from "@stakekit/api-hooks"; -import type { AxiosError } from "axios"; +import { useQuery } from "@tanstack/react-query"; +import { useYieldApiFetchClient } from "../providers/yield-api-client-provider"; +import type { components } from "../types/yield-api-schema"; + +type HealthStatusDto = components["schemas"]["HealthStatusDto"]; export const useUnderMaintenance = () => { - const { data, error } = useHealthHealthV2({ - query: { refetchInterval: 1000 * 30 }, + const yieldApiFetchClient = useYieldApiFetchClient(); + const { data, error } = useQuery({ + queryKey: ["yield-api-health"], + queryFn: async ({ signal }) => { + const response = await yieldApiFetchClient.GET("/health", { signal }); + + if (response.data) { + return response.data; + } + + throw new YieldApiResponseError(response.response.status, response.error); + }, + refetchInterval: 1000 * 30, }); - if (error?.status === 500 || (data?.db && data.db !== "OK")) return true; + const isServiceUnavailable = + error instanceof YieldApiResponseError && error.status >= 500; + const isUnhealthy = data?.status !== undefined && data.status !== "OK"; + + if (isServiceUnavailable || isUnhealthy) { + return true; + } + return false; }; + +class YieldApiResponseError extends Error { + constructor( + readonly status: number, + override readonly cause?: unknown + ) { + super("Yield API health request failed"); + } +} diff --git a/packages/widget/src/providers/api/get-enabled-networks.ts b/packages/widget/src/providers/api/get-enabled-networks.ts index 1ed81338..7d4c1797 100644 --- a/packages/widget/src/providers/api/get-enabled-networks.ts +++ b/packages/widget/src/providers/api/get-enabled-networks.ts @@ -1,23 +1,26 @@ -import { yieldGetMyNetworks } from "@stakekit/api-hooks"; import type { Networks } from "@stakekit/common"; import type { QueryClient } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; import { config } from "../../config"; +import { getResponseData } from "../yield-api-client-provider/request-helpers"; +import type { YieldApiFetchClient } from "../yield-api-client-provider/types"; export const getEnabledNetworks = ({ queryClient, + yieldApiFetchClient, }: { queryClient: QueryClient; + yieldApiFetchClient: YieldApiFetchClient; }) => EitherAsync(() => queryClient.fetchQuery({ staleTime: Number.POSITIVE_INFINITY, queryKey: [config.appPrefix, "enabled-networks"], queryFn: async () => - ( - await EitherAsync(() => yieldGetMyNetworks()).map( - (v) => new Set(v as Networks[]) + new Set( + (await getResponseData(yieldApiFetchClient.GET("/v1/networks"))).map( + (network) => network.id as Networks ) - ).unsafeCoerce(), + ), }) ).mapLeft(() => new Error("Could not get enabled networks")); diff --git a/packages/widget/src/providers/cosmos/config.ts b/packages/widget/src/providers/cosmos/config.ts index 76d36fd8..dfe38824 100644 --- a/packages/widget/src/providers/cosmos/config.ts +++ b/packages/widget/src/providers/cosmos/config.ts @@ -6,6 +6,7 @@ import type { CosmosChainsMap } from "../../domain/types/chains/cosmos"; import { supportedCosmosChains } from "../../domain/types/chains/cosmos"; import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "../../utils"; import { getEnabledNetworks } from "../api/get-enabled-networks"; +import type { YieldApiFetchClient } from "../yield-api-client-provider/types"; import { getWagmiChain } from "./chains"; const queryKey = [config.appPrefix, "cosmos-config"]; @@ -14,11 +15,13 @@ const staleTime = Number.POSITIVE_INFINITY; const queryFn = async ({ queryClient, forceWalletConnectOnly, + yieldApiFetchClient, }: { queryClient: QueryClient; forceWalletConnectOnly: boolean; + yieldApiFetchClient: YieldApiFetchClient; }) => - getEnabledNetworks({ queryClient }) + getEnabledNetworks({ queryClient, yieldApiFetchClient }) .chain< Error, { diff --git a/packages/widget/src/providers/ethereum/config.ts b/packages/widget/src/providers/ethereum/config.ts index 24afaa8a..7b91a30d 100644 --- a/packages/widget/src/providers/ethereum/config.ts +++ b/packages/widget/src/providers/ethereum/config.ts @@ -15,6 +15,7 @@ import { type EvmChainsMap, evmChainsMap } from "../../domain/types/chains/evm"; import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "../../utils"; import { getEnabledNetworks } from "../api/get-enabled-networks"; import type { VariantProps } from "../settings/types"; +import type { YieldApiFetchClient } from "../yield-api-client-provider/types"; import { createFineryWallets } from "./finery-wallet-list"; import { passCorrectChainsToWallet } from "./utils"; @@ -22,17 +23,19 @@ const queryFn = async ({ queryClient, forceWalletConnectOnly, variant, + yieldApiFetchClient, }: { queryClient: QueryClient; forceWalletConnectOnly: boolean; variant: VariantProps["variant"]; + yieldApiFetchClient: YieldApiFetchClient; }): Promise<{ evmChainsMap: Partial; evmChains: Chain[]; connector: Maybe; fineryWallets: ReturnType | null; }> => - getEnabledNetworks({ queryClient }).caseOf({ + getEnabledNetworks({ queryClient, yieldApiFetchClient }).caseOf({ Right: (networks) => { const filteredEvmChainsMap: Partial = typeSafeObjectFromEntries( diff --git a/packages/widget/src/providers/substrate/config.ts b/packages/widget/src/providers/substrate/config.ts index 1ba6936d..27f1e4f1 100644 --- a/packages/widget/src/providers/substrate/config.ts +++ b/packages/widget/src/providers/substrate/config.ts @@ -13,6 +13,7 @@ import { typeSafeObjectFromEntries, } from "../../utils"; import { getEnabledNetworks } from "../api/get-enabled-networks"; +import type { YieldApiFetchClient } from "../yield-api-client-provider/types"; import { getSubstrateConnectors } from "./substrate-connector"; const queryKey = [config.appPrefix, "substrate-config"]; @@ -21,9 +22,11 @@ const staleTime = Number.POSITIVE_INFINITY; const queryFn = async ({ queryClient, forceWalletConnectOnly, + yieldApiFetchClient, }: { queryClient: QueryClient; forceWalletConnectOnly: boolean; + yieldApiFetchClient: YieldApiFetchClient; }): Promise<{ substrateChainsMap: Partial; substrateChains: Chain[]; @@ -32,7 +35,7 @@ const queryFn = async ({ wallets: WalletList[number]["wallets"]; }>; }> => - getEnabledNetworks({ queryClient }).caseOf({ + getEnabledNetworks({ queryClient, yieldApiFetchClient }).caseOf({ Right: (networks) => { const filteredSubstrateChainsMap: Partial = typeSafeObjectFromEntries( diff --git a/packages/widget/src/providers/wagmi/index.ts b/packages/widget/src/providers/wagmi/index.ts index 3b09cf5f..381ade10 100644 --- a/packages/widget/src/providers/wagmi/index.ts +++ b/packages/widget/src/providers/wagmi/index.ts @@ -84,7 +84,10 @@ const buildWagmiConfig = async (opts: { wagmiConfig: ReturnType; queryParamsInitChainId: number | undefined; }> => { - return getEnabledNetworks({ queryClient: opts.queryClient }) + return getEnabledNetworks({ + queryClient: opts.queryClient, + yieldApiFetchClient: opts.yieldApiFetchClient, + }) .chain((networks) => EitherAsync.fromPromise(() => Promise.all([ @@ -92,10 +95,12 @@ const buildWagmiConfig = async (opts: { forceWalletConnectOnly: opts.forceWalletConnectOnly, queryClient: opts.queryClient, variant: opts.variant, + yieldApiFetchClient: opts.yieldApiFetchClient, }), getCosmosConfig({ forceWalletConnectOnly: opts.forceWalletConnectOnly, queryClient: opts.queryClient, + yieldApiFetchClient: opts.yieldApiFetchClient, }), getMiscConfig({ enabledNetworks: networks, @@ -109,6 +114,7 @@ const buildWagmiConfig = async (opts: { getSubstrateConfig({ queryClient: opts.queryClient, forceWalletConnectOnly: opts.forceWalletConnectOnly, + yieldApiFetchClient: opts.yieldApiFetchClient, }), getInitParams({ isLedgerLive: opts.isLedgerLive, diff --git a/packages/widget/src/providers/yield-api-client-provider/actions.ts b/packages/widget/src/providers/yield-api-client-provider/actions.ts index 487a8538..41e5ecf0 100644 --- a/packages/widget/src/providers/yield-api-client-provider/actions.ts +++ b/packages/widget/src/providers/yield-api-client-provider/actions.ts @@ -4,11 +4,13 @@ import type { TokenDto, YieldDto, } from "@stakekit/api-hooks"; -import type { Client } from "openapi-fetch"; -import type { paths } from "../../types/yield-api-schema"; import { adaptActionDto } from "./compat"; import { getResponseData } from "./request-helpers"; -import type { YieldCreateActionDto, YieldCreateManageActionDto } from "./types"; +import type { + YieldApiFetchClient, + YieldCreateActionDto, + YieldCreateManageActionDto, +} from "./types"; export const createEnterAction = async ({ addresses, @@ -18,7 +20,7 @@ export const createEnterAction = async ({ yieldDto, }: { addresses: AddressesDto; - fetchClient: Client; + fetchClient: YieldApiFetchClient; inputToken: TokenDto; requestDto: YieldCreateActionDto; yieldDto: YieldDto; @@ -45,7 +47,7 @@ export const createExitAction = async ({ yieldDto, }: { addresses: AddressesDto; - fetchClient: Client; + fetchClient: YieldApiFetchClient; requestDto: YieldCreateActionDto; yieldDto: YieldDto; }): Promise => { @@ -70,7 +72,7 @@ export const createManageAction = async ({ yieldDto, }: { addresses: AddressesDto; - fetchClient: Client; + fetchClient: YieldApiFetchClient; requestDto: YieldCreateManageActionDto; yieldDto: YieldDto; }): Promise => { @@ -95,7 +97,7 @@ export const listActions = async ({ offset, }: { address: string; - fetchClient: Client; + fetchClient: YieldApiFetchClient; limit: number; offset: number; }) => @@ -115,7 +117,7 @@ export const getTransaction = async ({ fetchClient, transactionId, }: { - fetchClient: Client; + fetchClient: YieldApiFetchClient; transactionId: string; }) => getResponseData( @@ -133,7 +135,7 @@ export const submitTransaction = async ({ signedTransaction, transactionId, }: { - fetchClient: Client; + fetchClient: YieldApiFetchClient; signedTransaction: string; transactionId: string; }) => @@ -155,7 +157,7 @@ export const submitTransactionHash = async ({ hash, transactionId, }: { - fetchClient: Client; + fetchClient: YieldApiFetchClient; hash: string; transactionId: string; }) => diff --git a/packages/widget/src/providers/yield-api-client-provider/index.tsx b/packages/widget/src/providers/yield-api-client-provider/index.tsx index b3bb82df..93f729b9 100644 --- a/packages/widget/src/providers/yield-api-client-provider/index.tsx +++ b/packages/widget/src/providers/yield-api-client-provider/index.tsx @@ -1,5 +1,4 @@ import type { i18n } from "i18next"; -import type { Client } from "openapi-fetch"; import createFetchClient from "openapi-fetch"; import type { OpenapiQueryClient } from "openapi-react-query"; import createClient from "openapi-react-query"; @@ -12,11 +11,12 @@ import { handleGeoBlockResponse } from "../../hooks/use-geo-block"; import { handleRichErrorResponse } from "../../hooks/use-rich-errors"; import type { paths } from "../../types/yield-api-schema"; import { useSettings } from "../settings"; +import type { YieldApiFetchClient } from "./types"; const QueryContext = createContext | undefined>( undefined ); -const FetchContext = createContext | undefined>(undefined); +const FetchContext = createContext(undefined); export const createYieldApiFetchClient = ({ apiKey, diff --git a/packages/widget/src/providers/yield-api-client-provider/types.ts b/packages/widget/src/providers/yield-api-client-provider/types.ts index 750288f7..3919b904 100644 --- a/packages/widget/src/providers/yield-api-client-provider/types.ts +++ b/packages/widget/src/providers/yield-api-client-provider/types.ts @@ -1,4 +1,7 @@ -import type { components } from "../../types/yield-api-schema"; +import type { Client } from "openapi-fetch"; +import type { components, paths } from "../../types/yield-api-schema"; + +export type YieldApiFetchClient = Client; export type YieldDto = components["schemas"]["YieldDto"]; export type YieldBalancesRequestDto = From bdfb8dfaff76e1b5be00cdd1447e7c8923ce4977 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Mon, 23 Mar 2026 19:04:14 +0100 Subject: [PATCH 10/23] fix: actions filtering --- packages/widget/src/hooks/api/use-activity-actions.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/widget/src/hooks/api/use-activity-actions.ts b/packages/widget/src/hooks/api/use-activity-actions.ts index 7aa74c46..e58fd694 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -12,13 +12,13 @@ import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportuni const PAGE_SIZE = 20; export const useActivityActions = () => { - const { address, isLedgerLive } = useSKWallet(); + const { address, isLedgerLive, network } = useSKWallet(); const queryClient = useSKQueryClient(); const yieldApiFetchClient = useYieldApiFetchClient(); const query = useInfiniteQuery({ enabled: !!address, - queryKey: ["activity-actions", address], + queryKey: ["activity-actions", address, network], queryFn: async ({ pageParam = 0 }) => { return ( await EitherAsync(() => @@ -33,7 +33,10 @@ export const useActivityActions = () => { .map((actionList) => ({ ...actionList, data: (actionList.items ?? []).filter( - (x) => x.status !== ActionStatus.CREATED + (action) => + action.status !== ActionStatus.CREATED && + (!network || + action.transactions.some((tx) => tx.network === network)) ), })) .chain(async (actionList) => From e96863ee27c194b9981d13be5348b991731b92f4 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Tue, 24 Mar 2026 09:00:46 +0100 Subject: [PATCH 11/23] fix: token fallback --- packages/widget/src/domain/types/stake.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/widget/src/domain/types/stake.ts b/packages/widget/src/domain/types/stake.ts index 0f5a0fb7..6bc6d151 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -86,6 +86,7 @@ export const getInitialToken = (args: { */ .altLazy(() => List.find(hasYields, args.tokenBalances)) .altLazy(() => List.find(hasYields, args.defaultTokens)) + .altLazy(() => List.head(args.defaultTokens)) .map((val) => val.token); export const canBeInitialYield = (args: { From fecb25fc1a7e3ea56c56613e788cbf73d802b3a2 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Tue, 24 Mar 2026 16:37:35 +0100 Subject: [PATCH 12/23] fix: remove nonce, type fallback --- packages/widget/src/providers/sk-wallet/index.tsx | 2 +- .../widget/src/providers/yield-api-client-provider/compat.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/widget/src/providers/sk-wallet/index.tsx b/packages/widget/src/providers/sk-wallet/index.tsx index 8edc2543..aeb91a11 100644 --- a/packages/widget/src/providers/sk-wallet/index.tsx +++ b/packages/widget/src/providers/sk-wallet/index.tsx @@ -445,7 +445,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { data: val.data, to: val.to, value: val.value, - nonce: val.nonce, + // nonce: val.nonce, maxFeePerGas: val.maxFeePerGas, maxPriorityFeePerGas: val.maxPriorityFeePerGas, chainId: val.chainId, diff --git a/packages/widget/src/providers/yield-api-client-provider/compat.ts b/packages/widget/src/providers/yield-api-client-provider/compat.ts index c290447b..e1f06903 100644 --- a/packages/widget/src/providers/yield-api-client-provider/compat.ts +++ b/packages/widget/src/providers/yield-api-client-provider/compat.ts @@ -169,7 +169,6 @@ const getMetadata = ({ real_world_asset: "vault", } as const; const type = - fallbackMetadata?.type ?? yieldTypeMap[(mechanics?.type ?? "vault") as keyof typeof yieldTypeMap] ?? "vault"; From 083f1424c0d8adb051f95742a0a30162d7434329 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Tue, 24 Mar 2026 16:46:42 +0100 Subject: [PATCH 13/23] feat: init --- biome.json | 71 +- package.json | 2 +- packages/widget/public/mockServiceWorker.js | 192 +- packages/widget/src/App.tsx | 2 +- packages/widget/src/assets/images/index.ts | 2 +- .../widget/src/common/check-gas-amount.ts | 57 +- .../widget/src/common/get-gas-mode-value.ts | 16 - .../widget/src/common/get-token-balances.ts | 8 +- packages/widget/src/common/private-api.ts | 4 +- .../widget/src/components/atoms/box/index.tsx | 2 +- .../components/atoms/collapsible/index.tsx | 2 +- .../src/components/atoms/copy-text/index.tsx | 6 +- .../src/components/atoms/max-button/index.tsx | 2 +- .../components/atoms/number-input/index.tsx | 4 +- .../number-input/use-auto-resize-text.ts | 14 +- .../components/atoms/select-modal/index.tsx | 4 +- .../src/components/atoms/token-icon/index.tsx | 6 +- .../atoms/token-icon/provider-icon/index.tsx | 5 +- .../hooks/use-variant-network-urls.ts | 2 +- .../hooks/use-variant-token-urls.ts | 13 +- .../token-icon/token-icon-container/index.tsx | 8 +- .../components/atoms/typography/styles.css.ts | 4 +- .../components/atoms/virtual-list/index.tsx | 16 +- .../molecules/amount-toggle/index.tsx | 2 +- .../molecules/connect-button/index.tsx | 2 +- .../molecules/global-modals/index.tsx | 2 +- .../src/components/molecules/header/index.tsx | 4 +- .../components/molecules/header/use-header.ts | 2 +- .../components/molecules/help-modal/index.tsx | 4 +- .../molecules/reward-rate-breakdown/index.tsx | 2 + .../select-opportunity-list-item/index.tsx | 54 +- .../molecules/select-validator/index.tsx | 7 +- .../molecules/select-validator/meta-info.tsx | 10 +- .../select-validator-list.tsx | 13 +- .../molecules/select-yield/index.tsx | 6 +- .../molecules/zerion-chain-modal/index.tsx | 4 +- packages/widget/src/domain/index.ts | 29 +- packages/widget/src/domain/types/action.ts | 211 +- packages/widget/src/domain/types/addresses.ts | 11 + .../widget/src/domain/types/chains/index.ts | 2 +- .../widget/src/domain/types/connectors.ts | 2 +- packages/widget/src/domain/types/errors.ts | 5 + .../src/domain/types/external-providers.ts | 12 +- packages/widget/src/domain/types/fees.ts | 3 + .../widget/src/domain/types/init-params.ts | 4 +- .../widget/src/domain/types/pending-action.ts | 21 +- packages/widget/src/domain/types/positions.ts | 33 +- packages/widget/src/domain/types/price.ts | 7 +- .../widget/src/domain/types/reward-rate.ts | 25 +- packages/widget/src/domain/types/rewards.ts | 10 +- packages/widget/src/domain/types/settings.ts | 3 + packages/widget/src/domain/types/stake.ts | 83 +- .../widget/src/domain/types/token-balance.ts | 11 + packages/widget/src/domain/types/tokens.ts | 11 +- .../widget/src/domain/types/transaction.ts | 5 +- packages/widget/src/domain/types/tron.ts | 3 + .../widget/src/domain/types/validators.ts | 44 + .../domain/types/wallets/generic-wallet.ts | 12 +- packages/widget/src/domain/types/yield-api.ts | 4 + packages/widget/src/domain/types/yields.ts | 320 +- .../src/hooks/api/use-activity-actions.ts | 36 +- .../src/hooks/api/use-default-tokens.ts | 14 +- .../widget/src/hooks/api/use-multi-yields.ts | 51 +- packages/widget/src/hooks/api/use-prices.ts | 13 +- .../src/hooks/api/use-token-balances-scan.ts | 16 +- .../widget/src/hooks/api/use-tokens-prices.ts | 8 +- .../src/hooks/api/use-yield-balances-scan.ts | 18 +- .../get-yield-opportunity.ts | 48 +- .../src/hooks/api/use-yield-validators.ts | 22 +- .../use-dashboard-tabs-page-match.ts | 9 - .../use-navigate-with-scroll-to-top.ts | 2 +- .../src/hooks/tracking/use-track-page.ts | 2 +- .../src/hooks/use-add-ledger-account.ts | 4 +- packages/widget/src/hooks/use-base-token.ts | 4 +- .../widget/src/hooks/use-estimated-rewards.ts | 19 +- .../widget/src/hooks/use-force-max-amount.ts | 8 +- .../widget/src/hooks/use-gas-fee-token.ts | 4 +- .../widget/src/hooks/use-gas-warning-check.ts | 21 +- packages/widget/src/hooks/use-geo-block.ts | 8 +- .../widget/src/hooks/use-handle-deep-links.ts | 14 +- packages/widget/src/hooks/use-init-params.ts | 6 +- .../widget/src/hooks/use-init-query-params.ts | 10 +- .../src/hooks/use-invalidate-query-n-times.ts | 4 +- .../src/hooks/use-local-storage-value.ts | 8 +- packages/widget/src/hooks/use-logout.ts | 2 +- .../src/hooks/use-max-min-yield-amount.ts | 20 +- .../src/hooks/use-position-balance-by-type.ts | 8 +- .../widget/src/hooks/use-position-balances.ts | 2 +- .../widget/src/hooks/use-position-data.ts | 2 +- .../widget/src/hooks/use-positions-data.ts | 8 +- .../widget/src/hooks/use-provider-details.ts | 74 +- .../widget/src/hooks/use-region-code-names.ts | 12 +- .../get-reward-token-symbols.tsx | 4 +- .../hooks/use-reward-token-details/index.ts | 15 +- .../widget/src/hooks/use-rewards-summary.ts | 21 +- packages/widget/src/hooks/use-rich-errors.ts | 4 +- .../src/hooks/use-staked-or-liquid-balance.ts | 6 +- packages/widget/src/hooks/use-summary.tsx | 51 +- .../src/hooks/use-sync-element-height.ts | 2 +- .../widget/src/hooks/use-under-maintenance.ts | 2 +- .../widget/src/hooks/use-validators-config.ts | 4 +- .../widget/src/hooks/use-yield-meta-info.tsx | 52 +- packages/widget/src/hooks/use-yield-type.ts | 5 +- packages/widget/src/main.tsx | 2 +- .../containers/animation-layout.tsx | 2 +- .../activity/action-list-item/index.tsx | 16 +- .../activity/activity-details.page.tsx | 17 +- .../activity/activity.page.tsx | 6 +- .../activity/position-balances.tsx | 8 +- .../common/components/header.tsx | 2 +- .../utila-select-validator-section.tsx | 8 +- .../utila-select-validator-trigger.tsx | 2 +- .../components/positions-list-item.tsx | 21 +- .../positions/hooks/use-position-list-item.ts | 14 +- .../components/position-details-actions.tsx | 15 +- .../components/position-details-info.tsx | 7 +- .../components/provider-details.tsx | 9 +- .../components/top-header.tsx | 5 +- .../position-details/index.tsx | 2 +- .../components/positions-list-item.tsx | 21 +- .../hooks/use-activity-complete.hook.ts | 33 +- .../pages/complete/hooks/use-complete.hook.ts | 6 +- .../complete/pages/activity-complete.page.tsx | 2 +- .../src/pages/complete/pages/common.page.tsx | 36 +- .../complete/pages/pending-complete.page.tsx | 13 +- .../complete/pages/stake-complete.page.tsx | 15 +- .../complete/pages/unstake-complete.page.tsx | 13 +- .../widget/src/pages/complete/state/index.tsx | 4 +- .../pages/components/footer-outlet/index.tsx | 4 +- .../components/layout/layout-context.tsx | 6 +- .../src/pages/components/meta-info/index.tsx | 10 +- .../components/action-list-item/index.tsx | 13 +- .../components/list-item-bullet/index.tsx | 6 +- .../hooks/use-action-list-item.ts | 31 +- .../state/activity-page.context.tsx | 27 +- .../src/pages/details/activity-page/types.ts | 5 +- .../details/details-page/components/tabs.tsx | 2 +- .../details/details-page/details.page.tsx | 2 +- .../components/extra-args-selection/index.tsx | 7 +- .../components/select-provider/index.tsx | 41 +- .../components/select-token-section/index.tsx | 13 +- .../select-token-list-item.tsx | 8 +- .../select-token-section/select-token.tsx | 4 +- .../select-validator-section/index.tsx | 14 +- .../select-validator-trigger.tsx | 4 +- .../use-select-validator.ts | 2 +- .../components/select-yield-section/index.tsx | 3 +- .../select-opportunity.tsx | 12 +- .../select-yield-reward-details.tsx | 14 +- .../select-yield-section/staked-via.tsx | 17 +- .../use-animate-yield-percent.ts | 4 +- .../src/pages/details/earn-page/earn.page.tsx | 2 +- .../earn-page/state/earn-page-context.tsx | 113 +- .../state/earn-page-state-context.tsx | 43 +- .../pages/details/earn-page/state/types.ts | 24 +- .../earn-page/state/use-amount-validation.ts | 8 +- .../earn-page/state/use-get-init-yield.ts | 8 +- .../state/use-get-token-balances-map.ts | 6 +- .../details/earn-page/state/use-init-token.ts | 14 +- .../details/earn-page/state/use-init-yield.ts | 16 +- .../state/use-pending-action-deep-link.ts | 46 +- .../state/use-stake-enter-request-dto.ts | 32 +- .../earn-page/state/use-token-balance.ts | 10 +- .../earn-page/state/use-track-state-events.ts | 9 +- .../pages/details/earn-page/state/utils.ts | 12 +- .../src/pages/details/earn-page/types.ts | 7 +- .../components/positions-list-item.tsx | 17 +- .../hooks/use-position-list-item.ts | 25 +- .../positions-page/hooks/use-positions.ts | 30 +- .../components/amount-block.tsx | 19 +- .../components/position-balances.tsx | 8 +- .../components/provider-details.tsx | 9 +- .../components/static-action-block.tsx | 9 +- .../hooks/use-pending-actions.ts | 37 +- .../hooks/use-position-details.ts | 29 +- .../hooks/use-stake-exit-request-dto.ts | 36 +- .../hooks/use-unstake-machine.ts | 38 +- .../hooks/use-validator-addresses-handling.ts | 12 +- .../src/pages/position-details/hooks/utils.ts | 32 +- .../position-details.page.tsx | 25 +- .../pages/position-details/state/index.tsx | 59 +- .../src/pages/position-details/state/types.ts | 21 +- .../src/pages/position-details/state/utils.ts | 2 +- .../review/hooks/use-action-review.hook.ts | 73 +- .../widget/src/pages/review/hooks/use-fees.ts | 24 +- .../review/hooks/use-pending-review.hook.ts | 42 +- .../review/hooks/use-stake-review.hook.ts | 45 +- .../review/hooks/use-unstake-review.hook.ts | 37 +- .../pages/review/pages/action-review.page.tsx | 7 +- .../review/pages/common-page/common.page.tsx | 6 +- .../components/review-top-section.tsx | 9 +- .../review/pages/pending-review.page.tsx | 7 +- .../pages/review/pages/stake-review.page.tsx | 4 +- .../review/pages/unstake-review.page.tsx | 7 +- .../steps/hooks/use-steps-machine.hook.ts | 90 +- .../src/pages/steps/hooks/use-steps.hook.ts | 23 +- .../pages/steps/pages/activity-steps.page.tsx | 21 +- .../src/pages/steps/pages/common.page.tsx | 6 +- .../pages/steps/pages/pending-steps.page.tsx | 7 +- .../pages/steps/pages/stake-steps.page.tsx | 7 +- .../widget/src/pages/steps/pages/tx-state.tsx | 15 +- .../pages/steps/pages/unstake-steps.page.tsx | 7 +- .../src/providers/activity-provider/index.tsx | 11 +- .../src/providers/api/get-enabled-networks.ts | 6 +- .../cosmos/chains/get-chain-registry.ts | 8 +- .../src/providers/cosmos/chains/index.ts | 2 +- .../widget/src/providers/cosmos/config.ts | 24 +- .../providers/cosmos/cosmos-connector-meta.ts | 2 +- .../src/providers/cosmos/cosmos-connector.ts | 16 +- .../providers/cosmos/wallet-connect/client.ts | 2 +- .../cosmos/wallet-connect/main-wallet.ts | 2 +- .../cosmos/wallet-connect/registry.ts | 2 +- .../src/providers/cosmos/wallet-manager.ts | 10 +- .../src/providers/enter-stake-store/index.tsx | 24 +- .../widget/src/providers/ethereum/config.ts | 8 +- .../ethereum/finery-wallet-list/index.ts | 8 +- .../widget/src/providers/ethereum/utils.ts | 4 +- .../src/providers/exit-stake-store/index.tsx | 21 +- .../src/providers/external-provider/index.ts | 22 +- .../widget/src/providers/ledger/config.ts | 8 +- .../src/providers/ledger/ledger-connector.ts | 44 +- .../ledger/ledger-live-connector-meta.ts | 2 +- packages/widget/src/providers/ledger/utils.ts | 14 +- .../providers/misc/cardano-connector-meta.ts | 2 +- .../src/providers/misc/cardano-connector.ts | 4 +- packages/widget/src/providers/misc/config.ts | 26 +- .../providers/misc/solana-connector-meta.ts | 2 +- .../src/providers/misc/solana-connector.ts | 8 +- .../src/providers/misc/ton-connector-meta.ts | 2 +- .../src/providers/misc/ton-connector.ts | 12 +- .../src/providers/misc/tron-connector-meta.ts | 2 +- .../src/providers/misc/tron-connector.ts | 4 +- .../src/providers/mount-animation/index.tsx | 10 +- .../providers/pending-action-store/index.tsx | 21 +- .../src/providers/query-client/index.tsx | 2 +- packages/widget/src/providers/rainbow-kit.tsx | 6 +- .../widget/src/providers/rainbow/index.tsx | 6 +- .../src/providers/root-element/index.tsx | 4 +- packages/widget/src/providers/safe/config.ts | 2 +- .../src/providers/safe/safe-connector-meta.ts | 2 +- .../src/providers/safe/safe-connector.ts | 8 +- .../widget/src/providers/settings/index.tsx | 10 +- .../widget/src/providers/settings/types.ts | 3 +- .../widget/src/providers/sk-wallet/index.tsx | 86 +- .../sk-wallet/use-additional-addresses.ts | 12 +- .../sk-wallet/use-connector-chains.ts | 2 +- .../src/providers/sk-wallet/use-cosmos-cw.ts | 4 +- .../src/providers/sk-wallet/use-init.ts | 10 +- .../sk-wallet/use-ledger-accounts.ts | 2 +- .../use-ledger-current-account-id.ts | 4 +- .../sk-wallet/use-ledger-disabled-chains.ts | 2 +- .../sk-wallet/use-sync-external-provider.ts | 8 +- .../src/providers/stake-history/index.tsx | 4 +- .../widget/src/providers/substrate/config.ts | 14 +- .../substrate/substrate-connector-meta.ts | 2 +- .../substrate/substrate-connector.ts | 32 +- .../widget/src/providers/theme-wrapper.tsx | 8 +- .../widget/src/providers/tracking/index.tsx | 12 +- packages/widget/src/providers/wagmi/index.ts | 36 +- packages/widget/src/providers/wagmi/utils.ts | 2 +- .../yield-api-client-provider/actions.ts | 81 +- .../yield-api-client-provider/compat.ts | 519 -- .../yield-api-client-provider/index.tsx | 8 +- .../request-helpers.ts | 6 +- .../yield-api-client-provider/types.ts | 71 +- packages/widget/src/services/local-storage.ts | 14 +- packages/widget/src/styles/theme/atoms.css.ts | 4 +- .../widget/src/styles/theme/contract.css.ts | 2 +- .../widget/src/styles/theme/global.css.ts | 14 +- .../widget/src/styles/tokens/breakpoints.ts | 2 +- packages/widget/src/translation/index.ts | 8 +- packages/widget/src/types/purify-extend.d.ts | 2 +- .../widget/src/utils/create-state-context.ts | 4 +- packages/widget/src/utils/date.ts | 2 +- packages/widget/src/utils/extend-purify.ts | 2 +- packages/widget/src/utils/formatters.ts | 28 +- packages/widget/src/utils/index.ts | 18 +- packages/widget/src/utils/mappers.ts | 7 +- packages/widget/src/utils/maybe-window.ts | 2 +- packages/widget/src/utils/memoize.ts | 2 +- .../widget/src/utils/region-iso-3166-codes.ts | 7306 ++++++++++++++++- packages/widget/src/utils/text.ts | 2 +- packages/widget/src/worker.ts | 4 +- packages/widget/tests/fixtures/index.ts | 35 +- .../deep-links-flow/deep-links-flow.test.tsx | 12 +- .../deep-links-flow/param-validation.test.tsx | 22 +- .../tests/use-cases/deep-links-flow/setup.ts | 54 +- .../external-provider.test.tsx | 22 +- .../use-cases/external-provider/setup.ts | 28 +- .../gas-warning-flow.test.tsx | 11 +- .../tests/use-cases/gas-warning-flow/setup.ts | 52 +- .../widget/tests/use-cases/geo-block.test.tsx | 4 +- .../use-cases/renders-initial-page.test.tsx | 13 +- .../use-cases/select-opportunity.test.tsx | 21 +- .../widget/tests/use-cases/sk-wallet.test.tsx | 10 +- .../tests/use-cases/staking-flow/setup.ts | 8 +- .../staking-flow/staking-flow.test.tsx | 26 +- .../use-cases/trust-incentive-apy/setup.ts | 15 +- .../trust-incentive-apy.test.tsx | 12 +- .../use-cases/under-maintenance.test.tsx | 2 +- packages/widget/vite/vite.config.bundle.ts | 2 +- packages/widget/vite/vite.config.website.ts | 2 +- pnpm-lock.yaml | 74 +- 303 files changed, 10226 insertions(+), 2714 deletions(-) delete mode 100644 packages/widget/src/common/get-gas-mode-value.ts create mode 100644 packages/widget/src/domain/types/addresses.ts create mode 100644 packages/widget/src/domain/types/errors.ts create mode 100644 packages/widget/src/domain/types/fees.ts create mode 100644 packages/widget/src/domain/types/settings.ts create mode 100644 packages/widget/src/domain/types/token-balance.ts create mode 100644 packages/widget/src/domain/types/tron.ts create mode 100644 packages/widget/src/domain/types/validators.ts create mode 100644 packages/widget/src/domain/types/yield-api.ts delete mode 100644 packages/widget/src/hooks/navigation/use-dashboard-tabs-page-match.ts delete mode 100644 packages/widget/src/providers/yield-api-client-provider/compat.ts diff --git a/biome.json b/biome.json index ec464ce6..027a6627 100644 --- a/biome.json +++ b/biome.json @@ -1,70 +1,53 @@ { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, "files": { + "ignoreUnknown": false, "includes": [ "**", "!**/node_modules", "!**/dist", - "!**/.next", - "!**/next", - "!**/.turbo", - "!**/node_modules", - "!**/dist", - "!**/.next", "!**/.turbo", - "!**/region-iso-3166-codes.ts", - "!**/mockServiceWorker.js", - "!**/package.json" + "!**/.vscode", + "!**/routeTree.gen.ts", + "!**/client-factory.ts", + "!**/api-schemas.ts", + "!**/opensrc" ] }, "formatter": { "enabled": true, - "formatWithErrors": false, - "indentStyle": "space", - "indentWidth": 2, - "lineWidth": 80 + "indentStyle": "space" }, - "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { "enabled": true, "rules": { "recommended": true, - "style": { - "noNonNullAssertion": "off", - "noParameterAssign": "error", - "useAsConstAssertion": "error", - "useDefaultParameterLast": "error", - "useEnumInitializers": "error", - "useSelfClosingElements": "error", - "useSingleVarDeclarator": "error", - "noUnusedTemplateLiteral": "error", - "useNumberNamespace": "error", - "noInferrableTypes": "error", - "noUselessElse": "error" - }, - "suspicious": { - "noArrayIndexKey": "off", - "useIterableCallbackReturn": "off" - }, - "a11y": { "noSvgWithoutTitle": "off" }, - "correctness": { - "useJsxKeyInIterable": "off", - "useUniqueElementIds": "off" - }, - "complexity": { "noForEach": "off", "useIndexOf": "off" } + "complexity": { + "noBannedTypes": "off" + } + } + }, + "css": { + "parser": { + "tailwindDirectives": true } }, "javascript": { "formatter": { - "semicolons": "always", - "arrowParentheses": "always", - "trailingCommas": "es5" + "quoteStyle": "double" } }, - "html": { - "formatter": { - "enabled": true, - "indentStyle": "space" + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } } } } diff --git a/package.json b/package.json index ac2bf626..dbe23620 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "version": "changeset version" }, "devDependencies": { - "@biomejs/biome": "^2.3.8", + "@biomejs/biome": "^2.4.8", "@changesets/cli": "^2.29.8", "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", diff --git a/packages/widget/public/mockServiceWorker.js b/packages/widget/public/mockServiceWorker.js index f5cddde0..57451a30 100644 --- a/packages/widget/public/mockServiceWorker.js +++ b/packages/widget/public/mockServiceWorker.js @@ -7,114 +7,114 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' -const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') -const activeClientIds = new Set() +const PACKAGE_VERSION = "2.12.2"; +const INTEGRITY_CHECKSUM = "4db4a41e972cec1b64cc569c66952d82"; +const IS_MOCKED_RESPONSE = Symbol("isMockedResponse"); +const activeClientIds = new Set(); -addEventListener('install', function () { - self.skipWaiting() -}) +addEventListener("install", () => { + self.skipWaiting(); +}); -addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) +addEventListener("activate", (event) => { + event.waitUntil(self.clients.claim()); +}); -addEventListener('message', async function (event) { - const clientId = Reflect.get(event.source || {}, 'id') +addEventListener("message", async (event) => { + const clientId = Reflect.get(event.source || {}, "id"); if (!clientId || !self.clients) { - return + return; } - const client = await self.clients.get(clientId) + const client = await self.clients.get(clientId); if (!client) { - return + return; } const allClients = await self.clients.matchAll({ - type: 'window', - }) + type: "window", + }); switch (event.data) { - case 'KEEPALIVE_REQUEST': { + case "KEEPALIVE_REQUEST": { sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break + type: "KEEPALIVE_RESPONSE", + }); + break; } - case 'INTEGRITY_CHECK_REQUEST': { + case "INTEGRITY_CHECK_REQUEST": { sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', + type: "INTEGRITY_CHECK_RESPONSE", payload: { packageVersion: PACKAGE_VERSION, checksum: INTEGRITY_CHECKSUM, }, - }) - break + }); + break; } - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) + case "MOCK_ACTIVATE": { + activeClientIds.add(clientId); sendToClient(client, { - type: 'MOCKING_ENABLED', + type: "MOCKING_ENABLED", payload: { client: { id: client.id, frameType: client.frameType, }, }, - }) - break + }); + break; } - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) + case "CLIENT_CLOSED": { + activeClientIds.delete(clientId); const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) + return client.id !== clientId; + }); // Unregister itself when there are no more clients if (remainingClients.length === 0) { - self.registration.unregister() + self.registration.unregister(); } - break + break; } } -}) +}); -addEventListener('fetch', function (event) { - const requestInterceptedAt = Date.now() +addEventListener("fetch", (event) => { + const requestInterceptedAt = Date.now(); // Bypass navigation requests. - if (event.request.mode === 'navigate') { - return + if (event.request.mode === "navigate") { + return; } // Opening the DevTools triggers the "only-if-cached" request // that cannot be handled by the worker. Bypass such requests. if ( - event.request.cache === 'only-if-cached' && - event.request.mode !== 'same-origin' + event.request.cache === "only-if-cached" && + event.request.mode !== "same-origin" ) { - return + return; } // Bypass all requests when there are no active clients. // Prevents the self-unregistered worked from handling requests // after it's been terminated (still remains active until the next reload). if (activeClientIds.size === 0) { - return + return; } - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) -}) + const requestId = crypto.randomUUID(); + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)); +}); /** * @param {FetchEvent} event @@ -122,28 +122,28 @@ addEventListener('fetch', function (event) { * @param {number} requestInterceptedAt */ async function handleRequest(event, requestId, requestInterceptedAt) { - const client = await resolveMainClient(event) - const requestCloneForEvents = event.request.clone() + const client = await resolveMainClient(event); + const requestCloneForEvents = event.request.clone(); const response = await getResponse( event, client, requestId, requestInterceptedAt, - ) + ); // Send back the response clone for the "response:*" life-cycle events. // Ensure MSW is active and ready to handle the message, otherwise // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { - const serializedRequest = await serializeRequest(requestCloneForEvents) + const serializedRequest = await serializeRequest(requestCloneForEvents); // Clone the response so both the client and the library could consume it. - const responseClone = response.clone() + const responseClone = response.clone(); sendToClient( client, { - type: 'RESPONSE', + type: "RESPONSE", payload: { isMockedResponse: IS_MOCKED_RESPONSE in response, request: { @@ -160,10 +160,10 @@ async function handleRequest(event, requestId, requestInterceptedAt) { }, }, responseClone.body ? [serializedRequest.body, responseClone.body] : [], - ) + ); } - return response + return response; } /** @@ -175,30 +175,30 @@ async function handleRequest(event, requestId, requestInterceptedAt) { * @returns {Promise} */ async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) + const client = await self.clients.get(event.clientId); if (activeClientIds.has(event.clientId)) { - return client + return client; } - if (client?.frameType === 'top-level') { - return client + if (client?.frameType === "top-level") { + return client; } const allClients = await self.clients.matchAll({ - type: 'window', - }) + type: "window", + }); return allClients .filter((client) => { // Get only those clients that are currently visible. - return client.visibilityState === 'visible' + return client.visibilityState === "visible"; }) .find((client) => { // Find the client ID that's recorded in the // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) + return activeClientIds.has(client.id); + }); } /** @@ -211,36 +211,36 @@ async function resolveMainClient(event) { async function getResponse(event, client, requestId, requestInterceptedAt) { // Clone the request because it might've been already used // (i.e. its body has been read and sent to the client). - const requestClone = event.request.clone() + const requestClone = event.request.clone(); function passthrough() { // Cast the request headers to a new Headers instance // so the headers can be manipulated with. - const headers = new Headers(requestClone.headers) + const headers = new Headers(requestClone.headers); // Remove the "accept" header value that marked this request as passthrough. // This prevents request alteration and also keeps it compliant with the // user-defined CORS policies. - const acceptHeader = headers.get('accept') + const acceptHeader = headers.get("accept"); if (acceptHeader) { - const values = acceptHeader.split(',').map((value) => value.trim()) + const values = acceptHeader.split(",").map((value) => value.trim()); const filteredValues = values.filter( - (value) => value !== 'msw/passthrough', - ) + (value) => value !== "msw/passthrough", + ); if (filteredValues.length > 0) { - headers.set('accept', filteredValues.join(', ')) + headers.set("accept", filteredValues.join(", ")); } else { - headers.delete('accept') + headers.delete("accept"); } } - return fetch(requestClone, { headers }) + return fetch(requestClone, { headers }); } // Bypass mocking when the client is not active. if (!client) { - return passthrough() + return passthrough(); } // Bypass initial page load requests (i.e. static assets). @@ -248,15 +248,15 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet // and is not ready to handle requests. if (!activeClientIds.has(client.id)) { - return passthrough() + return passthrough(); } // Notify the client that a request has been intercepted. - const serializedRequest = await serializeRequest(event.request) + const serializedRequest = await serializeRequest(event.request); const clientMessage = await sendToClient( client, { - type: 'REQUEST', + type: "REQUEST", payload: { id: requestId, interceptedAt: requestInterceptedAt, @@ -264,19 +264,19 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { }, }, [serializedRequest.body], - ) + ); switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) + case "MOCK_RESPONSE": { + return respondWithMock(clientMessage.data); } - case 'PASSTHROUGH': { - return passthrough() + case "PASSTHROUGH": { + return passthrough(); } } - return passthrough() + return passthrough(); } /** @@ -287,21 +287,21 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { */ function sendToClient(client, message, transferrables = []) { return new Promise((resolve, reject) => { - const channel = new MessageChannel() + const channel = new MessageChannel(); channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error) + if (event.data?.error) { + return reject(event.data.error); } - resolve(event.data) - } + resolve(event.data); + }; client.postMessage(message, [ channel.port2, ...transferrables.filter(Boolean), - ]) - }) + ]); + }); } /** @@ -314,17 +314,17 @@ function respondWithMock(response) { // instance will have status code set to 0. Since it's not possible to create // a Response instance with status code 0, handle that use-case separately. if (response.status === 0) { - return Response.error() + return Response.error(); } - const mockedResponse = new Response(response.body, response) + const mockedResponse = new Response(response.body, response); Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { value: true, enumerable: true, - }) + }); - return mockedResponse + return mockedResponse; } /** @@ -345,5 +345,5 @@ async function serializeRequest(request) { referrerPolicy: request.referrerPolicy, body: await request.arrayBuffer(), keepalive: request.keepalive, - } + }; } diff --git a/packages/widget/src/App.tsx b/packages/widget/src/App.tsx index 655fa278..2923bd25 100644 --- a/packages/widget/src/App.tsx +++ b/packages/widget/src/App.tsx @@ -43,7 +43,7 @@ export const SKApp = (props: SKAppProps) => { : { variant: props.variant ?? "default" }; const [router] = useState(() => - createMemoryRouter([{ path: "*", Component: Root }]) + createMemoryRouter([{ path: "*", Component: Root }]), ); return ( diff --git a/packages/widget/src/assets/images/index.ts b/packages/widget/src/assets/images/index.ts index 2334a5ac..2e4c9fb0 100644 --- a/packages/widget/src/assets/images/index.ts +++ b/packages/widget/src/assets/images/index.ts @@ -17,5 +17,5 @@ export const preloadImages = () => Object.values(images).forEach((src) => { const img = new Image(); img.src = src; - }) + }), ); diff --git a/packages/widget/src/common/check-gas-amount.ts b/packages/widget/src/common/check-gas-amount.ts index 7cd1041f..e82a07fa 100644 --- a/packages/widget/src/common/check-gas-amount.ts +++ b/packages/widget/src/common/check-gas-amount.ts @@ -1,12 +1,10 @@ -import { - type AddressWithTokenDto, - type TokenDto, - tokenGetTokenBalances, -} from "@stakekit/api-hooks"; +import { tokenGetTokenBalances } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; -import { EitherAsync, Left, List, Right } from "purify-ts"; +import { EitherAsync, Left, List, Maybe, Right } from "purify-ts"; import { equalTokens } from "../domain"; import type { ActionDtoWithGasEstimate } from "../domain/types/action"; +import type { AddressWithTokenDto } from "../domain/types/addresses"; +import type { TokenDto } from "../domain/types/tokens"; type CheckGasAmountIfStake = | { isStake: true; stakeToken: TokenDto; stakeAmount: BigNumber } @@ -20,27 +18,36 @@ export const checkGasAmount = ({ addressWithTokenDto: AddressWithTokenDto; gasEstimate: ActionDtoWithGasEstimate["gasEstimate"]; } & CheckGasAmountIfStake) => - EitherAsync(() => tokenGetTokenBalances({ addresses: [addressWithTokenDto] })) - .mapLeft(() => new GetGasTokenError()) - .chain((res) => - EitherAsync.liftEither( - List.head(res) - .map((val) => ({ ...val, amount: new BigNumber(val.amount ?? 0) })) - .toEither(new GasTokenMissingError()) - ).chain(async (gasTokenBalance) => { - const amount = - rest.isStake && equalTokens(gasTokenBalance.token, rest.stakeToken) - ? gasTokenBalance.amount.minus(rest.stakeAmount) - : gasTokenBalance.amount; + EitherAsync.liftEither( + Maybe.fromNullable(gasEstimate).toEither(new GasTokenMissingError()), + ).chain((gasEstimate) => + EitherAsync(() => + tokenGetTokenBalances({ addresses: [addressWithTokenDto] }), + ) + .mapLeft(() => new GetGasTokenError()) + .chain((res) => + EitherAsync.liftEither( + List.head(res) + .map((val) => ({ + ...val, + amount: new BigNumber(val.amount ?? 0), + })) + .toEither(new GasTokenMissingError()), + ).chain(async (gasTokenBalance) => { + const amount = + rest.isStake && equalTokens(gasTokenBalance.token, rest.stakeToken) + ? gasTokenBalance.amount.minus(rest.stakeAmount) + : gasTokenBalance.amount; - if (gasEstimate.amount.isGreaterThan(amount)) { - return Left(new NotEnoughGasTokenError()); - } + if (gasEstimate.amount.isGreaterThan(amount)) { + return Left(new NotEnoughGasTokenError()); + } - return Right(null); - }) - ) - .chainLeft(async (e) => Right(e)); + return Right(null); + }), + ) + .chainLeft(async (e) => Right(e)), + ); export class NotEnoughGasTokenError extends Error { constructor() { diff --git a/packages/widget/src/common/get-gas-mode-value.ts b/packages/widget/src/common/get-gas-mode-value.ts deleted file mode 100644 index 92651257..00000000 --- a/packages/widget/src/common/get-gas-mode-value.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { - type Networks, - transactionGetGasForNetwork, -} from "@stakekit/api-hooks"; -import { EitherAsync, List } from "purify-ts"; - -export const getAverageGasMode = ({ network }: { network: Networks }) => - EitherAsync(() => transactionGetGasForNetwork(network)) - .mapLeft(() => new Error("Get gas for network error")) - .chain((gas) => - EitherAsync.liftEither( - List.find((v) => v.name === "average", gas.modes.values).toEither( - new Error("Average gas mode not found") - ) - ) - ); diff --git a/packages/widget/src/common/get-token-balances.ts b/packages/widget/src/common/get-token-balances.ts index 2a739960..4082dae2 100644 --- a/packages/widget/src/common/get-token-balances.ts +++ b/packages/widget/src/common/get-token-balances.ts @@ -26,7 +26,7 @@ export const getTokenBalances = ({ enabledYieldsOnly: tokensForEnabledYieldsOnly, }), EitherAsync.liftEither( - Right({ additionalAddresses, address, network }) + Right({ additionalAddresses, address, network }), ).chain(async (params) => { if (!params.address || !params.network) { return Right([]); @@ -48,7 +48,7 @@ export const getTokenBalances = ({ tokenBalances.map((b) => ({ defaultTokens: d, tokenBalancesScan: b, - })) - ) - ) + })), + ), + ), ).mapLeft(() => new Error("could not get tokens")); diff --git a/packages/widget/src/common/private-api.ts b/packages/widget/src/common/private-api.ts index 7eea9757..335cfa54 100644 --- a/packages/widget/src/common/private-api.ts +++ b/packages/widget/src/common/private-api.ts @@ -11,7 +11,7 @@ import { */ export const tokenTokenBalancesScan = ( tokenBalanceScanDto: TokenBalanceScanDto, - signal?: AbortSignal + signal?: AbortSignal, ) => { return customFetch({ url: "/v1/tokens/balances/scan", @@ -29,7 +29,7 @@ export const tokenTokenBalancesScan = ( export const yieldYieldOpportunity = ( integrationId: string, params?: { ledgerWalletAPICompatible?: boolean }, - signal?: AbortSignal + signal?: AbortSignal, ) => { return customFetch({ url: `/v1/yields/${integrationId}`, diff --git a/packages/widget/src/components/atoms/box/index.tsx b/packages/widget/src/components/atoms/box/index.tsx index 1ade0b55..50aea9d3 100644 --- a/packages/widget/src/components/atoms/box/index.tsx +++ b/packages/widget/src/components/atoms/box/index.tsx @@ -44,5 +44,5 @@ export const Box = forwardRef( ref, ...nativeProps, }); - } + }, ); diff --git a/packages/widget/src/components/atoms/collapsible/index.tsx b/packages/widget/src/components/atoms/collapsible/index.tsx index 7bbd5926..686f2861 100644 --- a/packages/widget/src/components/atoms/collapsible/index.tsx +++ b/packages/widget/src/components/atoms/collapsible/index.tsx @@ -42,7 +42,7 @@ export const CollapsibleRoot = ({ children, ...controlledProps }: Props) => { collapsed: internalState[0], onClick: () => internalState[1]((prev) => !prev), }, - [controlledProps.onClick, controlledProps.collapsed, internalState] + [controlledProps.onClick, controlledProps.collapsed, internalState], ); return ( diff --git a/packages/widget/src/components/atoms/copy-text/index.tsx b/packages/widget/src/components/atoms/copy-text/index.tsx index 0704a0b4..e4eb06d8 100644 --- a/packages/widget/src/components/atoms/copy-text/index.tsx +++ b/packages/widget/src/components/atoms/copy-text/index.tsx @@ -20,7 +20,7 @@ type CopyTextContextType = { }; const CopyTextContext = createContext( - undefined + undefined, ); const useCopyText = () => { @@ -48,7 +48,7 @@ export const Provider = ({ MaybeWindow.ifJust((w) => w.navigator.clipboard.writeText(text)); setShowCopySuccess(true); }, - [text] + [text], ); useEffect(() => { @@ -63,7 +63,7 @@ export const Provider = ({ const value = useMemo( () => ({ showCopySuccess, onClick }), - [onClick, showCopySuccess] + [onClick, showCopySuccess], ); return ( diff --git a/packages/widget/src/components/atoms/max-button/index.tsx b/packages/widget/src/components/atoms/max-button/index.tsx index a65cf552..d50c7a51 100644 --- a/packages/widget/src/components/atoms/max-button/index.tsx +++ b/packages/widget/src/components/atoms/max-button/index.tsx @@ -30,7 +30,7 @@ export const MaxButton = ({ className={clsx( pressAnimation, combineRecipeWithVariant({ rec: container, variant }), - className + className, )} {...rest} > diff --git a/packages/widget/src/components/atoms/number-input/index.tsx b/packages/widget/src/components/atoms/number-input/index.tsx index 0acc9106..5a30e5fa 100644 --- a/packages/widget/src/components/atoms/number-input/index.tsx +++ b/packages/widget/src/components/atoms/number-input/index.tsx @@ -121,11 +121,11 @@ export const NumberInput = memo( {localState} , - rootElement + rootElement, )} ); - } + }, ); const stringToBigNumber = (str: string) => diff --git a/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts b/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts index e0cc8f0f..4088c130 100644 --- a/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts +++ b/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts @@ -54,7 +54,7 @@ const scale = ({ const descendingFontSizes = getDescendingFontSizes(inputEl); let currentFontSize = Number.parseFloat( - w.getComputedStyle(spanEl).fontSize + w.getComputedStyle(spanEl).fontSize, ); for (const fs of descendingFontSizes) { @@ -72,8 +72,8 @@ const convertRemToPixels = (rem: number) => rem * Number.parseFloat( MaybeDocument.map( - (doc) => getComputedStyle(doc.documentElement).fontSize - ).orDefault("0") + (doc) => getComputedStyle(doc.documentElement).fontSize, + ).orDefault("0"), ); const getDescendingFontSizes = (el: HTMLElement) => @@ -89,8 +89,8 @@ const getDescendingFontSizes = (el: HTMLElement) => Number.parseFloat( w .getComputedStyle(el) - .getPropertyValue(fs.replace(/(var\()|(\))/g, "")) - ) - ) - ) + .getPropertyValue(fs.replace(/(var\()|(\))/g, "")), + ), + ), + ), ).orDefault([]); diff --git a/packages/widget/src/components/atoms/select-modal/index.tsx b/packages/widget/src/components/atoms/select-modal/index.tsx index ff681e9f..83ce8970 100644 --- a/packages/widget/src/components/atoms/select-modal/index.tsx +++ b/packages/widget/src/components/atoms/select-modal/index.tsx @@ -55,7 +55,7 @@ export type SelectModalProps = SelectModalWithoutStateProps & { }; const SelectModalContext = createContext( - undefined + undefined, ); const useSelectModalContext = () => { @@ -209,7 +209,7 @@ export const SelectModal = ({ state, ...props }: SelectModalProps) => { isOpen, setOpen: (val) => setOpen(val), }, - [isOpen, state] + [isOpen, state], ); return ( diff --git a/packages/widget/src/components/atoms/token-icon/index.tsx b/packages/widget/src/components/atoms/token-icon/index.tsx index e6a90eeb..222e246e 100644 --- a/packages/widget/src/components/atoms/token-icon/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/index.tsx @@ -1,6 +1,6 @@ -import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; +import type { YieldMetadata } from "../../../domain/types/yields"; 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"; @@ -14,7 +14,7 @@ export const TokenIcon = ({ hideNetwork, }: { token: TokenDto | YieldTokenDto; - metadata?: YieldMetadataDto; + metadata?: YieldMetadata; tokenLogoHw?: Atoms["hw"]; tokenNetworkLogoHw?: Atoms["hw"]; hideNetwork?: boolean; diff --git a/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx b/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx index d7968906..64834f59 100644 --- a/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx @@ -1,4 +1,5 @@ -import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; +import type { TokenDto } from "../../../../domain/types/tokens"; +import type { YieldMetadata } from "../../../../domain/types/yields"; import { useSettings } from "../../../../providers/settings"; import type { Atoms } from "../../../../styles/theme/atoms.css"; import { NetworkLogoImage } from "../network-icon-image"; @@ -13,7 +14,7 @@ export const ProviderIcon = ({ hideNetwork, }: { token: TokenDto; - metadata?: YieldMetadataDto; + metadata?: YieldMetadata; tokenLogoHw?: Atoms["hw"]; tokenNetworkLogoHw?: Atoms["hw"]; hideNetwork?: boolean; diff --git a/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts b/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts index 3746987b..3845a437 100644 --- a/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts +++ b/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts @@ -35,6 +35,6 @@ export const useVariantNetworkUrls = (network: Networks) => { return useMemo( () => getVariantNetworkUrl({ chainIconMapping, network }), - [chainIconMapping, network] + [chainIconMapping, network], ); }; 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 cdfa3c0c..ade5c6d3 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 @@ -1,13 +1,16 @@ -import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { config } from "../../../../../config"; +import type { + TokenDto, + YieldTokenDto, +} from "../../../../../domain/types/tokens"; +import type { YieldMetadata } from "../../../../../domain/types/yields"; import { useSettings } from "../../../../../providers/settings"; -import type { YieldTokenDto } from "../../../../../providers/yield-api-client-provider/types"; export const useVariantTokenUrls = ( token: TokenDto | YieldTokenDto, - metadata?: YieldMetadataDto + metadata?: YieldMetadata, ): { mainUrl: string | undefined; fallbackUrl: string | undefined; @@ -20,7 +23,7 @@ export const useVariantTokenUrls = ( if (metadata) { const mainUrl = Maybe.fromFalsy(variant === "zerion") .filter(() => - skETHIconUrlsSuffix.some((v) => metadata.logoURI.endsWith(v)) + skETHIconUrlsSuffix.some((v) => metadata.logoURI.endsWith(v)), ) .map(() => zerionETHIcon) .orDefault(metadata.logoURI); @@ -36,7 +39,7 @@ export const useVariantTokenUrls = ( const tokenMappingResult = Maybe.fromNullable(tokenIconMapping) .chainNullable((mapping) => { if (typeof mapping === "function") { - return mapping(token as TokenDto); + return mapping(token as Parameters[0]); } 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 9b587b46..916af561 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,14 +1,14 @@ -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 type { TokenDto, YieldTokenDto } from "../../../../domain/types/tokens"; +import type { YieldMetadata } from "../../../../domain/types/yields"; import { Box } from "../../box"; import { useVariantNetworkUrls } from "./hooks/use-variant-network-urls"; import { useVariantTokenUrls } from "./hooks/use-variant-token-urls"; type TokenIconContainerProps = { token: TokenDto | YieldTokenDto; - metadata?: YieldMetadataDto; + metadata?: YieldMetadata; hideNetwork?: boolean; children: (props: TokenIconContainerReturnType) => ReactElement; }; @@ -25,7 +25,7 @@ export const TokenIconContainer = ({ }: TokenIconContainerProps) => { const { mainUrl, fallbackUrl, name, providerIcon } = useVariantTokenUrls( token, - metadata + metadata, ); const networkLogoUri = useVariantNetworkUrls(token.network as Networks); diff --git a/packages/widget/src/components/atoms/typography/styles.css.ts b/packages/widget/src/components/atoms/typography/styles.css.ts index 47048ac7..24d4c7fe 100644 --- a/packages/widget/src/components/atoms/typography/styles.css.ts +++ b/packages/widget/src/components/atoms/typography/styles.css.ts @@ -34,7 +34,7 @@ export const heading = recipe({ { fontSize: typeof vars.heading.h1.mobile.fontSize } >; } - > + >, ), }, weight: { @@ -96,7 +96,7 @@ export const textStyles = recipe({ { fontSize: typeof vars.text.small.mobile.fontSize } >; } - > + >, ), }, }, diff --git a/packages/widget/src/components/atoms/virtual-list/index.tsx b/packages/widget/src/components/atoms/virtual-list/index.tsx index eb88c416..21b76f42 100644 --- a/packages/widget/src/components/atoms/virtual-list/index.tsx +++ b/packages/widget/src/components/atoms/virtual-list/index.tsx @@ -74,7 +74,7 @@ export const VirtualList = ({ List.head([...virtualItems].reverse()) .filter((item) => item.index >= data.length - 1) .isJust(), - [virtualItems, data.length] + [virtualItems, data.length], ); const fetchNextPageRef = useSavedRef(fetchNextPage); @@ -143,7 +143,7 @@ export const GroupedVirtualList = ({ const rowVirtualizer = useVirtualizer({ count: groupCounts.reduce( (acc, numChildren) => acc + numChildren, - groupCounts.length + groupCounts.length, ), getScrollElement: () => innerRef.current, estimateSize, @@ -182,17 +182,17 @@ export const GroupedVirtualList = ({ type: "child", index: acc.childIndex + i, parentIndex: parentIndex, - }) satisfies ChildResult - ) + }) satisfies ChildResult, + ), ); acc.childIndex += numChildren; return acc; }, - { resultArray: [] as ResultsArray[], childIndex: 0 } + { resultArray: [] as ResultsArray[], childIndex: 0 }, ), - [groupCounts] + [groupCounts], ); const isEndReached = useMemo( @@ -200,7 +200,7 @@ export const GroupedVirtualList = ({ List.head([...virtualItems].reverse()) .filter((item) => item.index >= resultArray.length - 1) .isJust(), - [virtualItems, resultArray.length] + [virtualItems, resultArray.length], ); useEffect(() => { @@ -257,7 +257,7 @@ export const GroupedVirtualList = ({ const useIsTabletOrBigger = () => { const [windowWidth] = useState(() => - MaybeWindow.map((w) => w.innerWidth).orDefault(0) + MaybeWindow.map((w) => w.innerWidth).orDefault(0), ); return windowWidth >= breakpoints.tablet; diff --git a/packages/widget/src/components/molecules/amount-toggle/index.tsx b/packages/widget/src/components/molecules/amount-toggle/index.tsx index e7a0bcb0..b2a47475 100644 --- a/packages/widget/src/components/molecules/amount-toggle/index.tsx +++ b/packages/widget/src/components/molecules/amount-toggle/index.tsx @@ -59,4 +59,4 @@ const Amount = ({ fullAmount, shortAmount, children }: AmountProps) => { ); }; -export { Root, Amount }; +export { Amount, Root }; diff --git a/packages/widget/src/components/molecules/connect-button/index.tsx b/packages/widget/src/components/molecules/connect-button/index.tsx index 1ad0af2c..435c7b4b 100644 --- a/packages/widget/src/components/molecules/connect-button/index.tsx +++ b/packages/widget/src/components/molecules/connect-button/index.tsx @@ -32,7 +32,7 @@ export const ConnectButton = (props: ComponentProps) => { {t( isLedgerLiveAccountPlaceholder ? "init.ledger_add_account" - : "init.connect_wallet" + : "init.connect_wallet", )} ); diff --git a/packages/widget/src/components/molecules/global-modals/index.tsx b/packages/widget/src/components/molecules/global-modals/index.tsx index b737ecc7..ae5ece0a 100644 --- a/packages/widget/src/components/molecules/global-modals/index.tsx +++ b/packages/widget/src/components/molecules/global-modals/index.tsx @@ -9,7 +9,7 @@ import { TosModal } from "../tos-modal"; export const GlobalModals = () => { const geoBlock = useGeoBlock(); const regionCodeName = useRegionCodeName( - geoBlock ? geoBlock.regionCode : undefined + geoBlock ? geoBlock.regionCode : undefined, ); const [hideGeoBlock, setHideGeoBlock] = useState(false); diff --git a/packages/widget/src/components/molecules/header/index.tsx b/packages/widget/src/components/molecules/header/index.tsx index 1ad9a1ce..08c14742 100644 --- a/packages/widget/src/components/molecules/header/index.tsx +++ b/packages/widget/src/components/molecules/header/index.tsx @@ -63,7 +63,7 @@ export const Header = () => { {Maybe.fromFalsy( - !wagmiConfig.isLoading && wagmiConfig.data && variant !== "zerion" + !wagmiConfig.isLoading && wagmiConfig.data && variant !== "zerion", ) .map(() => ( @@ -74,7 +74,7 @@ export const Header = () => { aria-hidden={!mounted} > {Maybe.fromFalsy( - (isConnected || isConnecting) && chain && account + (isConnected || isConnecting) && chain && account, ) .map(() => ( { const showDisconnect = useMemo( () => Maybe.fromNullable(connector).map(shouldShowDisconnect).orDefault(false), - [connector] + [connector], ); const wagmiConfig = useWagmiConfig(); diff --git a/packages/widget/src/components/molecules/help-modal/index.tsx b/packages/widget/src/components/molecules/help-modal/index.tsx index 678db7c1..3b491ec0 100644 --- a/packages/widget/src/components/molecules/help-modal/index.tsx +++ b/packages/widget/src/components/molecules/help-modal/index.tsx @@ -36,7 +36,7 @@ export const HelpModal = ({ modal, customTrigger }: HelpModalProps) => { const { t, i18n } = useTranslation(); const getContent = ( - modal: ModalType + modal: ModalType, ): { title: string; description: string | ReactNode; @@ -167,7 +167,7 @@ export const HelpModal = ({ modal, customTrigger }: HelpModalProps) => { title: t("help_modals.get_in_touch.button"), onClick: () => MaybeWindow.ifJust((w) => - w.open("https://twitter.com/yield_xyz", "_blank") + w.open("https://twitter.com/yield_xyz", "_blank"), ), }, description: "", diff --git a/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx b/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx index f629003b..8ef6a96c 100644 --- a/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx +++ b/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx @@ -36,6 +36,8 @@ export const RewardRateBreakdown = ({ showUpToCampaign, }); + console.log({ items }); + if (!items.length) { return null; } diff --git a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx index ecde1dee..d49e079b 100644 --- a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx +++ b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx @@ -1,4 +1,3 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import type { ComponentProps, ReactNode } from "react"; @@ -7,6 +6,13 @@ import { getRewardRateBreakdown, getYieldRewardRateDetails, } from "../../../domain/types/reward-rate"; +import { + getYieldMetadata, + getYieldRewardRate, + getYieldRewardTokens, + getYieldRewardType, + type Yield, +} from "../../../domain/types/yields"; import { APToPercentage, formatNumber, fromWei } from "../../../utils"; import { getRewardRateFormatted } from "../../../utils/formatters"; import { Box } from "../../atoms/box"; @@ -20,8 +26,8 @@ export const SelectOpportunityListItem = ({ onYieldSelect, testId, }: { - item: YieldDto; - onYieldSelect: (item: YieldDto) => void; + item: Yield; + onYieldSelect: (item: Yield) => void; testId?: string; }) => { const onItemClick: ComponentProps["onItemClick"] = ({ @@ -34,24 +40,30 @@ export const SelectOpportunityListItem = ({ const { t } = useTranslation(); const campaignRate = getRewardRateBreakdown( - getYieldRewardRateDetails(item) + getYieldRewardRateDetails(item), ).find((rewardRate) => rewardRate.key === "campaign"); const totalRateFormatted = getRewardRateFormatted({ - rewardRate: item.rewardRate, - rewardType: item.rewardType, + rewardRate: getYieldRewardRate(item), + rewardType: getYieldRewardType(item), }); const primaryRateFormatted = getRewardRateFormatted({ rewardRate: campaignRate - ? item.rewardRate - campaignRate.rate - : item.rewardRate, - rewardType: item.rewardType, + ? getYieldRewardRate(item) - campaignRate.rate + : getYieldRewardRate(item), + rewardType: getYieldRewardType(item), }); + const metadata = getYieldMetadata(item); + const rewardTokens = getYieldRewardTokens(item); + return ( - + [0]["metadata"]} + token={item.token as Parameters[0]["token"]} + /> - {item.metadata.name} + {metadata.name} @@ -88,25 +100,25 @@ export const SelectOpportunityListItem = ({ - {Maybe.fromNullable(item.metadata.rewardTokens) + {Maybe.fromNullable(rewardTokens.length ? rewardTokens : null) .map((rt) => rt.map((t) => t.symbol).join(", ")) .altLazy(() => - Maybe.fromNullable(item.metadata.tvl) + Maybe.fromNullable(metadata.tvl) .map((tvl) => tvl.reduce( (acc, curr) => acc.plus(curr.value), - BigNumber(0) - ) + BigNumber(0), + ), ) .map( (tvl) => - `TVL: ${formatNumber(fromWei(tvl, item.token.decimals), 0)} ${item.token.symbol}` - ) + `TVL: ${formatNumber(fromWei(tvl, item.token.decimals), 0)} ${item.token.symbol}`, + ), ) .orDefault(item.token.symbol)} - {Maybe.fromNullable(item.metadata.rewardTokens) + {Maybe.fromNullable(rewardTokens.length ? rewardTokens : null) .map((): ReactNode | string => ( {item.token.symbol} @@ -115,11 +127,11 @@ export const SelectOpportunityListItem = ({ .extractNullable()} - {Maybe.fromNullable(item.metadata.commission) + {Maybe.fromNullable(metadata.commission) .map((commission) => APToPercentage( - commission.reduce((acc, curr) => acc + curr.value, 0) - ) + commission.reduce((acc, curr) => acc + curr.value, 0), + ), ) .map((commission) => ( void; onViewMoreClick?: () => void; validators: ValidatorDto[]; - selectedStake: YieldDto; + selectedStake: Yield; multiSelect: boolean; } & ( | { onSearch: (value: string) => void; searchValue: string } @@ -89,7 +90,7 @@ export const SelectValidator = ({ items: [] as ValidatorDto[], label: t("details.validators_other"), }, - } + }, ); // If we do not have preferred validators, show all other diff --git a/packages/widget/src/components/molecules/select-validator/meta-info.tsx b/packages/widget/src/components/molecules/select-validator/meta-info.tsx index 9120f468..c34253b4 100644 --- a/packages/widget/src/components/molecules/select-validator/meta-info.tsx +++ b/packages/widget/src/components/molecules/select-validator/meta-info.tsx @@ -1,10 +1,12 @@ -import type { RewardTypes, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Just } from "purify-ts"; import type { ReactNode } from "react"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import * as CopyText from "../../../components/atoms/copy-text"; +import type { RewardTypes } from "../../../domain/types/reward-rate"; +import type { TokenDto } from "../../../domain/types/tokens"; +import type { ValidatorDto } from "../../../domain/types/validators"; import { APToPercentage, formatAddress, formatNumber } from "../../../utils"; import { getRewardRateFormatted, @@ -42,7 +44,7 @@ export const useMetaInfo = ({ | "tokenSymbol" >]: ValidatorDto[Key] | undefined; } & { - stakedBalanceToken: YieldDto["token"] | undefined; + stakedBalanceToken: TokenDto | undefined; rewardRate: number | undefined; rewardType: RewardTypes | undefined; }) => { @@ -80,7 +82,7 @@ export const useMetaInfo = ({ val: Just(new BigNumber(stakedBalance)) .filter((v) => !v.isNaN()) .map( - (v) => `${formatNumber(v, 0)} ${stakedBalanceToken.symbol}` + (v) => `${formatNumber(v, 0)} ${stakedBalanceToken.symbol}`, ) .orDefault("-"), } @@ -187,6 +189,6 @@ export const useMetaInfo = ({ subnetName, marketCap, tokenSymbol, - ] + ], ); }; diff --git a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx index 5a03b4c9..5a961dc0 100644 --- a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx +++ b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx @@ -1,7 +1,8 @@ -import type { ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import type { ComponentProps } from "react"; import { memo } from "react"; import { useTranslation } from "react-i18next"; +import type { ValidatorDto } from "../../../domain/types/validators"; +import { getYieldRewardType, type Yield } from "../../../domain/types/yields"; import { vars } from "../../../styles/theme/contract.css"; import { getRewardRateFormatted, @@ -44,7 +45,7 @@ export const SelectValidatorList = ({ selectedValidators: Set; onItemClick: (item: ValidatorDto) => void; onViewMoreClick: () => void; - selectedStake: YieldDto; + selectedStake: Yield; tableData: ValidatorDto[]; groupedItems: GroupedItem[]; groupCounts: number[]; @@ -80,7 +81,7 @@ export const SelectValidatorList = ({ - {getRewardTypeFormatted(selectedStake.rewardType)} + {getRewardTypeFormatted(getYieldRewardType(selectedStake))} @@ -190,7 +191,7 @@ export const SelectValidatorList = ({ {t( status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive" + : "details.validators_inactive", )} @@ -201,7 +202,7 @@ export const SelectValidatorList = ({ {getRewardRateFormatted({ rewardRate: item.apr, - rewardType: selectedStake.rewardType, + rewardType: getYieldRewardType(selectedStake), })} @@ -240,7 +241,7 @@ const ValidatorMeta = memo((props: Parameters[0]) => { {Object.entries(metaInfo) .filter( (val): val is [keyof typeof metaInfo, NonNullable<(typeof val)[1]>] => - !!val[1] + !!val[1], ) .map(([key, val]) => { return ( diff --git a/packages/widget/src/components/molecules/select-yield/index.tsx b/packages/widget/src/components/molecules/select-yield/index.tsx index c4cdc9eb..e1442b2b 100644 --- a/packages/widget/src/components/molecules/select-yield/index.tsx +++ b/packages/widget/src/components/molecules/select-yield/index.tsx @@ -1,7 +1,7 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { PropsWithChildren } from "react"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; +import type { Yield } from "../../../domain/types/yields"; import { useMultiYields } from "../../../hooks/api/use-multi-yields"; import { Box } from "../../atoms/box"; import type { SelectModalProps } from "../../atoms/select-modal"; @@ -14,8 +14,8 @@ import { SelectOpportunityListItem } from "../select-opportunity-list-item"; type SelectYieldProps = PropsWithChildren< Pick & { - onItemClick: (yieldDto: YieldDto) => void; - providerYieldIds: YieldDto["id"][]; + onItemClick: (yieldDto: Yield) => void; + providerYieldIds: Yield["id"][]; } >; diff --git a/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx b/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx index 8c337c0a..dc06eade 100644 --- a/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx +++ b/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx @@ -12,7 +12,7 @@ export const ZerionChainModal = () => { const chainIds = useMemo( () => connectorChains.map((c) => c.id), - [connectorChains] + [connectorChains], ); const switchChain = connector?.switchChain; @@ -30,7 +30,7 @@ export const ZerionChainModal = () => { chainIds, selectedChainId: chain.id, onSwitchChain, - }) + }), ) .map((elem) => ( diff --git a/packages/widget/src/domain/index.ts b/packages/widget/src/domain/index.ts index 095ea757..fd536543 100644 --- a/packages/widget/src/domain/index.ts +++ b/packages/widget/src/domain/index.ts @@ -1,19 +1,18 @@ +import BigNumber from "bignumber.js"; +import { Left, type Maybe, Right } from "purify-ts"; +import type { Override } from "../types/utils"; import type { ActionDto, - TokenDto, TransactionDto, TransactionStatus, - YieldDto, -} from "@stakekit/api-hooks"; -import BigNumber from "bignumber.js"; -import { Left, type Maybe, Right } from "purify-ts"; -import type { Override } from "../types/utils"; +} from "./types/action"; import type { AnyPendingActionDto } from "./types/pending-action"; import { isPendingActionValidatorAddressesRequired, isPendingActionValidatorAddressRequired, } from "./types/pending-action"; -import type { TokenString } from "./types/tokens"; +import type { TokenDto, TokenString } from "./types/tokens"; +import { getYieldGasFeeToken, type Yield } from "./types/yields"; export { getTokenPriceInUSD } from "./types/price"; @@ -34,7 +33,7 @@ export const stakeTokenSameAsGasToken = ({ yieldDto, }: { stakeToken: TokenDto; - yieldDto: YieldDto; + yieldDto: Yield; }) => equalTokens(stakeToken, getGasFeeToken(yieldDto)); export const getMaxAmount = ({ @@ -49,15 +48,15 @@ export const getMaxAmount = ({ return BigNumber.max( BigNumber.min( integrationMaxLimit.orDefault(BigNumber(Number.POSITIVE_INFINITY)), - availableAmount.minus(gasEstimateTotal) + availableAmount.minus(gasEstimateTotal), ), - new BigNumber(0) + new BigNumber(0), ); }; -export const getBaseToken = (yieldDto: YieldDto) => yieldDto.metadata.token; -export const getGasFeeToken = (yieldDto: YieldDto) => - yieldDto.metadata.gasFeeToken; +export const getBaseToken = (yieldDto: Yield) => yieldDto.token; +export const getGasFeeToken = (yieldDto: Yield) => + getYieldGasFeeToken(yieldDto); /** * @@ -69,7 +68,7 @@ export const getValidStakeSessionTx = (stakeDto: ActionDto) => { ...stakeDto, transactions: stakeDto.transactions.filter( ( - tx + tx, ): tx is Override< TransactionDto, { @@ -78,7 +77,7 @@ export const getValidStakeSessionTx = (stakeDto: ActionDto) => { Exclude >; } - > => tx.status !== "SKIPPED" + > => tx.status !== "SKIPPED", ), }; diff --git a/packages/widget/src/domain/types/action.ts b/packages/widget/src/domain/types/action.ts index 33f09887..9dcde6f1 100644 --- a/packages/widget/src/domain/types/action.ts +++ b/packages/widget/src/domain/types/action.ts @@ -1,6 +1,211 @@ -import type { ActionDto, GasEstimateDto } from "@stakekit/api-hooks"; import type BigNumber from "bignumber.js"; +import type { components } from "../../types/yield-api-schema"; +import type { TokenDto } from "./tokens"; +import type { Yield } from "./yields"; +import { getYieldGasFeeToken } from "./yields"; +export type ActionDto = components["schemas"]["ActionDto"]; +export type TransactionDto = components["schemas"]["TransactionDto"]; +export type TransactionType = TransactionDto["type"]; +export type ActionType = ActionDto["type"]; +export type ActionStatus = ActionDto["status"]; +export type TransactionStatus = TransactionDto["status"]; +export type ActionInputToken = TokenDto; -export type ActionDtoWithGasEstimate = ActionDto & { - gasEstimate: Omit & { amount: BigNumber }; +export type YieldActionArgumentsDto = + components["schemas"]["ActionArgumentsDto"]; +export type YieldCreateActionDto = components["schemas"]["CreateActionDto"]; +export type YieldCreateManageActionDto = + components["schemas"]["CreateManageActionDto"]; +export type YieldActionDto = ActionDto; +export type YieldTransactionDto = TransactionDto; +export type TransactionGasEstimate = { + amount: string; + gasLimit?: string; + token: TokenDto; +} | null; + +export const ActionTypes = { + STAKE: "STAKE", + UNSTAKE: "UNSTAKE", + CLAIM_REWARDS: "CLAIM_REWARDS", + AUTO_SWEEP_UNSTAKE_REWARDS: "AUTO_SWEEP_UNSTAKE_REWARDS", + AUTO_SWEEP_WITHDRAW_REWARDS: "AUTO_SWEEP_WITHDRAW_REWARDS", + RESTAKE_REWARDS: "RESTAKE_REWARDS", + WITHDRAW: "WITHDRAW", + WITHDRAW_ALL: "WITHDRAW_ALL", + RESTAKE: "RESTAKE", + CLAIM_UNSTAKED: "CLAIM_UNSTAKED", + UNLOCK_LOCKED: "UNLOCK_LOCKED", + STAKE_LOCKED: "STAKE_LOCKED", + VOTE: "VOTE", + REVOKE: "REVOKE", + VOTE_LOCKED: "VOTE_LOCKED", + REVOTE: "REVOTE", + REBOND: "REBOND", + MIGRATE: "MIGRATE", + VERIFY_WITHDRAW_CREDENTIALS: "VERIFY_WITHDRAW_CREDENTIALS", + DELEGATE: "DELEGATE", +} as const satisfies Record; + +export const ActionStatus = { + CANCELED: "CANCELED", + CREATED: "CREATED", + WAITING_FOR_NEXT: "WAITING_FOR_NEXT", + PROCESSING: "PROCESSING", + FAILED: "FAILED", + SUCCESS: "SUCCESS", + STALE: "STALE", +} as const satisfies Record; + +export const TransactionStatus = { + NOT_FOUND: "NOT_FOUND", + CREATED: "CREATED", + BLOCKED: "BLOCKED", + WAITING_FOR_SIGNATURE: "WAITING_FOR_SIGNATURE", + SIGNED: "SIGNED", + BROADCASTED: "BROADCASTED", + PENDING: "PENDING", + CONFIRMED: "CONFIRMED", + FAILED: "FAILED", + SKIPPED: "SKIPPED", +} as const satisfies Record; + +const NATIVE_TOKEN_PLACEHOLDER = "0x"; + +const toLower = (value: string) => value.toLowerCase(); + +type EncodedGasEstimate = { + amount?: string | null; + gasLimit?: string | null; + token?: TokenDto | null; +}; + +export const getActionInputToken = ({ + actionDto, + inputToken, + yieldDto, +}: { + actionDto: ActionDto; + inputToken?: TokenDto; + yieldDto?: Yield | null; +}): TokenDto | undefined => { + if (inputToken) { + return inputToken; + } + + if (!yieldDto) { + return undefined; + } + + const inputTokenValue = actionDto.rawArguments?.inputToken; + + if (!inputTokenValue) { + return yieldDto.token ?? yieldDto.tokens?.[0]; + } + + const needle = toLower(inputTokenValue); + + return ( + [ + yieldDto.token, + ...(yieldDto.tokens ?? []), + ...(yieldDto.__fallback__.metadata.tokens ?? []), + ].find((token) => { + const address = token.address ? toLower(token.address) : null; + + return ( + address === needle || + token.symbol.toLowerCase() === needle || + (needle === NATIVE_TOKEN_PLACEHOLDER && !token.address) + ); + }) ?? + yieldDto.token ?? + yieldDto.tokens?.[0] + ); +}; + +export const getActionValidatorAddresses = ( + actionDto: ActionDto, +): string[] | null => + actionDto.rawArguments?.validatorAddresses ?? + (actionDto.rawArguments?.validatorAddress + ? [actionDto.rawArguments.validatorAddress] + : null); + +export const getActionCurrentStepIndex = (actionDto: ActionDto) => { + const idx = actionDto.transactions.findIndex( + (transaction) => + transaction.status !== TransactionStatus.CONFIRMED && + transaction.status !== TransactionStatus.SKIPPED, + ); + + if (idx >= 0) { + return idx; + } + + return Math.max(actionDto.transactions.length - 1, 0); +}; + +export const getTransactionGasEstimate = ({ + transactionDto, + gasFeeToken, +}: { + transactionDto: TransactionDto; + gasFeeToken?: TokenDto; +}): TransactionGasEstimate => { + const gasEstimate = transactionDto.gasEstimate; + + if (!gasEstimate) { + return null; + } + + try { + const parsed = JSON.parse(gasEstimate) as EncodedGasEstimate | null; + + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + if (!gasFeeToken) { + return null; + } + + return { + amount: gasEstimate, + token: gasFeeToken, + }; + } + + const amount = parsed.amount ?? null; + const token = parsed.token ?? gasFeeToken; + + if (!amount || !token) { + return null; + } + + return { + amount, + token, + ...(parsed.gasLimit ? { gasLimit: parsed.gasLimit } : {}), + }; + } catch { + if (!gasFeeToken) { + return null; + } + + return { + amount: gasEstimate, + token: gasFeeToken, + }; + } +}; + +export const getActionGasFeeToken = ( + yieldDto: Yield, + gasFeeToken?: TokenDto, +): TokenDto => gasFeeToken ?? getYieldGasFeeToken(yieldDto); + +export type ActionDtoWithGasEstimate = { + gasEstimate: { + amount: BigNumber; + token: TokenDto; + gasLimit?: string; + } | null; }; diff --git a/packages/widget/src/domain/types/addresses.ts b/packages/widget/src/domain/types/addresses.ts new file mode 100644 index 00000000..3e425bd0 --- /dev/null +++ b/packages/widget/src/domain/types/addresses.ts @@ -0,0 +1,11 @@ +import type { + AddressesDto, + AddressWithTokenDto, + AddressWithTokenDtoAdditionalAddresses, +} from "@stakekit/api-hooks"; + +export type { + AddressesDto, + AddressWithTokenDto, + AddressWithTokenDtoAdditionalAddresses, +}; diff --git a/packages/widget/src/domain/types/chains/index.ts b/packages/widget/src/domain/types/chains/index.ts index 164ca685..4e13681d 100644 --- a/packages/widget/src/domain/types/chains/index.ts +++ b/packages/widget/src/domain/types/chains/index.ts @@ -35,7 +35,7 @@ export const isTronChain = (chain: string): chain is SupportedMiscChains => { }; export const isBittensorChain = ( - chain: string + chain: string, ): chain is SupportedSubstrateChains => { return chain === SubstrateNetworks.Bittensor; }; diff --git a/packages/widget/src/domain/types/connectors.ts b/packages/widget/src/domain/types/connectors.ts index a404db8a..41c597bb 100644 --- a/packages/widget/src/domain/types/connectors.ts +++ b/packages/widget/src/domain/types/connectors.ts @@ -10,7 +10,7 @@ export type ConnectorWithFilteredChains = { }; export const isConnectorWithFilteredChains = ( - connector: Connector + connector: Connector, ): connector is Connector & ConnectorWithFilteredChains => { return !!(connector as unknown as ConnectorWithFilteredChains) .$filteredChains; diff --git a/packages/widget/src/domain/types/errors.ts b/packages/widget/src/domain/types/errors.ts new file mode 100644 index 00000000..1d87499a --- /dev/null +++ b/packages/widget/src/domain/types/errors.ts @@ -0,0 +1,5 @@ +import type { GeolocationError, StakeKitErrorDto } from "@stakekit/api-hooks"; +import { GeolocationErrorType } from "@stakekit/api-hooks"; + +export type { GeolocationError, StakeKitErrorDto }; +export { GeolocationErrorType }; diff --git a/packages/widget/src/domain/types/external-providers.ts b/packages/widget/src/domain/types/external-providers.ts index 6b9629a0..a3194230 100644 --- a/packages/widget/src/domain/types/external-providers.ts +++ b/packages/widget/src/domain/types/external-providers.ts @@ -9,13 +9,13 @@ export class ExternalProvider { sendTransaction(tx: SKTx, txMeta: SKTxMeta) { return EitherAsync.liftEither( Maybe.fromNullable( - this.variantProvider.current.provider.sendTransaction - ).toEither(new Error("Invalid provider type")) + this.variantProvider.current.provider.sendTransaction, + ).toEither(new Error("Invalid provider type")), ) .chain((_sendTransaction) => EitherAsync(() => _sendTransaction(tx, txMeta)).mapLeft( - () => new Error("Failed to send transaction, unknown error") - ) + () => new Error("Failed to send transaction, unknown error"), + ), ) .chain((res) => { if (typeof res === "string") { @@ -32,7 +32,7 @@ export class ExternalProvider { switchChain({ chainId }: { chainId: number }) { return EitherAsync(() => - this.variantProvider.current.provider.switchChain(chainId) + this.variantProvider.current.provider.switchChain(chainId), ).mapLeft((e) => { console.error(e); return new Error("Failed to switch chain"); @@ -41,7 +41,7 @@ export class ExternalProvider { signMessage(messageHash: string) { return EitherAsync(() => - this.variantProvider.current.provider.signMessage(messageHash) + this.variantProvider.current.provider.signMessage(messageHash), ).mapLeft((e) => { console.error(e); return new Error("Failed to sign message"); diff --git a/packages/widget/src/domain/types/fees.ts b/packages/widget/src/domain/types/fees.ts new file mode 100644 index 00000000..01a4ab9d --- /dev/null +++ b/packages/widget/src/domain/types/fees.ts @@ -0,0 +1,3 @@ +import type { FeeConfigurationDto } from "@stakekit/api-hooks"; + +export type { FeeConfigurationDto }; diff --git a/packages/widget/src/domain/types/init-params.ts b/packages/widget/src/domain/types/init-params.ts index f6985b0d..266b1d70 100644 --- a/packages/widget/src/domain/types/init-params.ts +++ b/packages/widget/src/domain/types/init-params.ts @@ -1,6 +1,6 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { SupportedSKChains } from "./chains"; import type { TokenString } from "./tokens"; +import type { Yield } from "./yields"; export type InitParams = { network: SupportedSKChains | null; @@ -8,7 +8,7 @@ export type InitParams = { yieldId: string | null; validator: string | null; pendingaction: string | null; - yieldData: YieldDto | null; + yieldData: Yield | null; accountId: string | null; tab: "earn" | "positions" | null; }; diff --git a/packages/widget/src/domain/types/pending-action.ts b/packages/widget/src/domain/types/pending-action.ts index e8a3fbbd..80a92cd6 100644 --- a/packages/widget/src/domain/types/pending-action.ts +++ b/packages/widget/src/domain/types/pending-action.ts @@ -1,5 +1,10 @@ import type { PendingActionDto as LegacyPendingActionDto } from "@stakekit/api-hooks"; -import type { YieldPendingActionDto } from "../../providers/yield-api-client-provider/types"; +import type { components } from "../../types/yield-api-schema"; + +export type YieldPendingActionDto = components["schemas"]["PendingActionDto"]; +export type YieldPendingActionType = + | YieldPendingActionDto["type"] + | NonNullable; type PendingActionArgName = | "amount" @@ -18,19 +23,19 @@ export type PendingActionAmountConfig = { }; export const isPendingActionAmountRequired = ( - pendingAction: AnyPendingActionDto + pendingAction: AnyPendingActionDto, ) => !!getPendingActionAmountConfig(pendingAction)?.required; export const isPendingActionValidatorAddressRequired = ( - pendingAction: AnyPendingActionDto + pendingAction: AnyPendingActionDto, ) => !!getPendingActionArgument(pendingAction, "validatorAddress")?.required; export const isPendingActionValidatorAddressesRequired = ( - pendingAction: AnyPendingActionDto + pendingAction: AnyPendingActionDto, ) => !!getPendingActionArgument(pendingAction, "validatorAddresses")?.required; export const getPendingActionAmountConfig = ( - pendingAction: AnyPendingActionDto + pendingAction: AnyPendingActionDto, ): PendingActionAmountConfig | null => { const amountArg = getPendingActionArgument(pendingAction, "amount"); @@ -51,14 +56,14 @@ export const getPendingActionAmountConfig = ( const getPendingActionArgument = ( pendingAction: AnyPendingActionDto, - name: PendingActionArgName + name: PendingActionArgName, ) => { const v2Field = ( pendingAction as YieldPendingActionDto ).arguments?.fields?.find( ( - field: NonNullable["fields"][number] - ) => field.name === name + field: NonNullable["fields"][number], + ) => field.name === name, ); if (v2Field) { diff --git a/packages/widget/src/domain/types/positions.ts b/packages/widget/src/domain/types/positions.ts index c9c9a5a0..a855f35d 100644 --- a/packages/widget/src/domain/types/positions.ts +++ b/packages/widget/src/domain/types/positions.ts @@ -1,13 +1,22 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; -import type { - YieldBalanceDto, - YieldBalancesByYieldDto, - YieldBalanceType, - YieldRewardRateDto, -} from "../../providers/yield-api-client-provider/types"; import type { components } from "../../types/yield-api-schema"; import { equalTokens } from ".."; +import type { YieldRewardRateDto } from "./reward-rate"; +import type { TokenDto } from "./tokens"; + +export type YieldBalanceType = components["schemas"]["BalanceType"]; +export type YieldBalanceDto = components["schemas"]["BalanceDto"]; +export type YieldBalancesByYieldDto = components["schemas"]["YieldBalancesDto"]; +export type YieldBalancesRequestDto = + components["schemas"]["BalancesRequestDto"]; +export type YieldSingleBalancesRequestDto = + components["schemas"]["YieldBalancesRequestDto"]; +export type YieldSingleBalancesResponseDto = + components["schemas"]["YieldBalancesDto"]; +export type YieldBalancesResponseDto = + components["schemas"]["BalancesResponseDto"]; +export type YieldPaginatedResponseDto = + components["schemas"]["PaginatedResponseDto"]; export type PositionBalancesByType = Map< YieldBalanceType, @@ -40,7 +49,7 @@ export type PositionsData = Map< >; export const getPositionBalanceDataKey = ( - balance: YieldBalanceDto + balance: YieldBalanceDto, ): BalanceDataKey => { if (Array.isArray(balance.validators) && balance.validators.length > 1) { return "validators"; @@ -55,10 +64,10 @@ export const getPositionBalanceDataKey = ( export const getPositionTotalAmount = ( balances: YieldBalanceDto[], - baseToken: TokenDto + baseToken: TokenDto, ) => { const baseTokenBalance = balances.find((b) => - equalTokens(b.token, baseToken) + equalTokens(b.token, baseToken), ); const baseTokenPriceInUsd = (() => { @@ -86,7 +95,7 @@ export const getPositionTotalAmount = ( if (baseTokenPriceInUsd && !baseTokenPriceInUsd.isZero()) { return { amount: acc.amount.plus( - balanceAmountUsd.dividedBy(baseTokenPriceInUsd) + balanceAmountUsd.dividedBy(baseTokenPriceInUsd), ), amountUsd: acc.amountUsd.plus(balanceAmountUsd), }; @@ -97,6 +106,6 @@ export const getPositionTotalAmount = ( amountUsd: acc.amountUsd.plus(balanceAmountUsd), }; }, - { amount: new BigNumber(0), amountUsd: new BigNumber(0) } + { 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 76494684..f43e8c02 100644 --- a/packages/widget/src/domain/types/price.ts +++ b/packages/widget/src/domain/types/price.ts @@ -1,8 +1,11 @@ +import type { PriceRequestDto, PriceResponseDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { tokenString } from ".."; import type { TokenString } from "./tokens"; +export type { PriceRequestDto, PriceResponseDto }; + type PriceToken = { symbol: string; network: string; @@ -42,7 +45,7 @@ export const getTokenPriceInUSD = ({ prices .getByToken(baseToken) .chainNullable((v) => v.price) - .orDefault(0) + .orDefault(0), ); const pricePerShareBN = BigNumber(pricePerShare); @@ -53,7 +56,7 @@ export const getTokenPriceInUSD = ({ prices .getByToken(token) .chainNullable((v) => v.price) - .orDefault(0) + .orDefault(0), ); return amountBN.times(tokenPrice); diff --git a/packages/widget/src/domain/types/reward-rate.ts b/packages/widget/src/domain/types/reward-rate.ts index 29784e65..c4c53dc3 100644 --- a/packages/widget/src/domain/types/reward-rate.ts +++ b/packages/widget/src/domain/types/reward-rate.ts @@ -1,12 +1,9 @@ -import type { RewardTypes, YieldDto } from "@stakekit/api-hooks"; -import type { - YieldRewardDto, - YieldRewardRateDto, -} from "../../providers/yield-api-client-provider/types"; +import type { components } from "../../types/yield-api-schema"; +import type { Yield } from "./yields"; -type YieldDtoWithRewardRateDetails = YieldDto & { - rewardRateDetails?: YieldRewardRateDto; -}; +export type RewardTypes = "apr" | "apy" | "variable"; +export type YieldRewardDto = components["schemas"]["RewardDto"]; +export type YieldRewardRateDto = components["schemas"]["RewardRateDto"]; export type RewardRateBreakdownKey = | "native" @@ -27,7 +24,7 @@ const breakdownOrder: RewardRateBreakdownKey[] = [ ]; export const getRewardTypeFromRateType = ( - rateType: string | null | undefined + rateType: string | null | undefined, ): RewardTypes => { const normalized = rateType?.toLowerCase(); @@ -39,7 +36,7 @@ export const getRewardTypeFromRateType = ( }; const getBreakdownKey = ( - yieldSource: YieldRewardDto["yieldSource"] + yieldSource: YieldRewardDto["yieldSource"], ): RewardRateBreakdownKey => yieldSource === "campaign_incentive" ? "campaign" @@ -48,16 +45,14 @@ const getBreakdownKey = ( : "native"; export const getYieldRewardRateDetails = ( - yieldDto: YieldDto | null | undefined -): YieldRewardRateDto | undefined => - (yieldDto as YieldDtoWithRewardRateDetails | null | undefined) - ?.rewardRateDetails; + yieldDto: Yield | null | undefined, +): YieldRewardRateDto | undefined => yieldDto?.rewardRate; export const getRewardRateBreakdown = ( rewardRate: YieldRewardRateDto | null | undefined, opts?: { showUpToCampaign?: boolean; - } + }, ): RewardRateBreakdownItem[] => { if (!rewardRate?.components?.length) { return []; diff --git a/packages/widget/src/domain/types/rewards.ts b/packages/widget/src/domain/types/rewards.ts index 0dac8575..c643f54c 100644 --- a/packages/widget/src/domain/types/rewards.ts +++ b/packages/widget/src/domain/types/rewards.ts @@ -1,4 +1,3 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import { CosmosNetworks, EvmNetworks, @@ -6,6 +5,7 @@ import { SubstrateNetworks, } from "@stakekit/common"; import type { Resources } from "i18next"; +import type { Yield } from "./yields"; const enabledRewardsSummaryYieldIds = { [SubstrateNetworks.Polkadot]: [ @@ -52,21 +52,21 @@ const enabledRewardsSummaryYieldIds = { | MiscNetworks.BinanceBeacon | MiscNetworks.Tron, { - id: YieldDto["id"]; + id: Yield["id"]; name: `dashboard.enabled_rewards_summary_yield_names.${keyof Resources["translation"]["dashboard"]["enabled_rewards_summary_yield_names"]}`; }[] >; const enabledRewardsSummaryYieldIdsSet = new Set( Object.values(enabledRewardsSummaryYieldIds).flatMap((v) => - v.map((v) => v.id) - ) + v.map((v) => v.id), + ), ); export type EnabledRewardsSummaryYieldId = (typeof enabledRewardsSummaryYieldIds)[keyof typeof enabledRewardsSummaryYieldIds][number]["id"]; export const isValidYieldIdForRewardsSummary = ( - yieldId: string + yieldId: string, ): yieldId is EnabledRewardsSummaryYieldId => enabledRewardsSummaryYieldIdsSet.has(yieldId as EnabledRewardsSummaryYieldId); diff --git a/packages/widget/src/domain/types/settings.ts b/packages/widget/src/domain/types/settings.ts new file mode 100644 index 00000000..2716695f --- /dev/null +++ b/packages/widget/src/domain/types/settings.ts @@ -0,0 +1,3 @@ +import type { TransactionFormat } from "@stakekit/api-hooks"; + +export type { TransactionFormat }; diff --git a/packages/widget/src/domain/types/stake.ts b/packages/widget/src/domain/types/stake.ts index 6bc6d151..46501b32 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -1,9 +1,4 @@ -import type { - AmountArgumentOptionsDto, - TokenBalanceScanResponseDto, - ValidatorDto, - YieldDto, -} from "@stakekit/api-hooks"; +import type { TokenBalanceScanResponseDto } from "@stakekit/api-hooks"; import { Networks } from "@stakekit/common"; import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; @@ -12,7 +7,13 @@ import type { SupportedSKChains } from "./chains"; import type { InitParams } from "./init-params"; import type { PositionsData } from "./positions"; import type { TokenString } from "./tokens"; -import { isBittensorStaking } from "./yields"; +import type { ValidatorDto } from "./validators"; +import { + getYieldActionArg, + getYieldGasFeeToken, + isBittensorStaking, + type Yield, +} from "./yields"; const amountGreaterThanZero = (val: TokenBalanceScanResponseDto) => new BigNumber(val.amount).isGreaterThan(0); @@ -24,7 +25,7 @@ const hasYieldsAndAmount = (val: TokenBalanceScanResponseDto) => hasYields(val) && amountGreaterThanZero(val); export type PreferredTokenYieldsPerNetwork = { - [Key in SupportedSKChains]?: Record; + [Key in SupportedSKChains]?: Record; }; export const getInitialToken = (args: { @@ -54,8 +55,8 @@ export const getInitialToken = (args: { (tokenSymbolCompare && tokenNetworkCompare) || tokenStringCompare ); }, - [...args.tokenBalances, ...args.defaultTokens] - ) + [...args.tokenBalances, ...args.defaultTokens], + ), ) /** * TB based on preferred token @@ -63,19 +64,19 @@ export const getInitialToken = (args: { .altLazy(() => Maybe.fromNullable(args.network) .chain((n) => - Maybe.fromNullable(args.preferredTokenYieldsPerNetwork?.[n]) + Maybe.fromNullable(args.preferredTokenYieldsPerNetwork?.[n]), ) .altLazy(() => Maybe.fromNullable(args.preferredTokenYieldsPerNetwork).chainNullable( - (v) => Object.values(v)[0] - ) + (v) => Object.values(v)[0], + ), ) .chain((preferredTokens) => List.find( (val) => !!preferredTokens[tokenString(val.token)], - [...args.tokenBalances, ...args.defaultTokens] - ) - ) + [...args.tokenBalances, ...args.defaultTokens], + ), + ), ) /** * TB based on first token with available yields and amount > 0 @@ -91,7 +92,7 @@ export const getInitialToken = (args: { export const canBeInitialYield = (args: { initQueryParams: Maybe; - yieldDto: YieldDto; + yieldDto: Yield; tokenBalanceAmount: BigNumber; positionsData: PositionsData; }) => @@ -99,8 +100,8 @@ export const canBeInitialYield = (args: { .chain((queryParams) => Maybe.fromFalsy( !!queryParams.yieldId && - queryParams.yieldId.toLowerCase() === args.yieldDto.id.toLowerCase() - ) + queryParams.yieldId.toLowerCase() === args.yieldDto.id.toLowerCase(), + ), ) .altLazy(() => Maybe.fromFalsy( @@ -108,8 +109,8 @@ export const canBeInitialYield = (args: { tokenBalanceAmount: args.tokenBalanceAmount, yieldDto: args.yieldDto, positionsData: args.positionsData, - }) - ) + }), + ), ) .isJust(); @@ -119,11 +120,11 @@ const balanceValidForYield = ({ positionsData, }: { tokenBalanceAmount: BigNumber; - yieldDto: YieldDto; + yieldDto: Yield; positionsData: PositionsData; }) => tokenBalanceAmount.isGreaterThanOrEqualTo( - getMinStakeAmount(yieldDto, positionsData) + getMinStakeAmount(yieldDto, positionsData), ); export const getInitSelectedValidators = (args: { @@ -137,46 +138,46 @@ export const getInitSelectedValidators = (args: { (val) => val.name?.toLowerCase() === initV.toLowerCase() || val.address === initV, - args.validators - ) + args.validators, + ), ) .altLazy(() => List.head(args.validators)) .map((v) => new Map([[v.address, v]])) .orDefault(new Map()); -export const isForceMaxAmount = (args: AmountArgumentOptionsDto) => - args.minimum === -1 && args.maximum === -1; +export const isForceMaxAmount = ( + args: { minimum?: number | null; maximum?: number | null } | null | undefined, +) => args?.minimum === -1 && args?.maximum === -1; -const yieldsWithEnterMinBasedOnPosition = new Map< - Networks, - Set ->([[Networks.Polkadot, new Set(["polkadot-dot-validator-staking"])]]); +const yieldsWithEnterMinBasedOnPosition = new Map>([ + [Networks.Polkadot, new Set(["polkadot-dot-validator-staking"])], +]); export const isNetworkWithEnterMinBasedOnPosition = (network: Networks) => yieldsWithEnterMinBasedOnPosition.has(network); -const isYieldWithEnterMinBasedOnPosition = (yieldDto: YieldDto) => +const isYieldWithEnterMinBasedOnPosition = (yieldDto: Yield) => Maybe.fromNullable( yieldsWithEnterMinBasedOnPosition.get( - yieldDto.metadata.gasFeeToken.network as Networks - ) + getYieldGasFeeToken(yieldDto).network as Networks, + ), ) .filter((set) => set.has(yieldDto.id)) .isJust(); export const getMinStakeAmount = ( - yieldDto: YieldDto, - positionsData: PositionsData + yieldDto: Yield, + positionsData: PositionsData, ) => { const integrationMin = new BigNumber( - yieldDto.args.enter.args?.amount?.minimum ?? 0 + getYieldActionArg(yieldDto, "enter", "amount")?.minimum ?? 0, ); if (isYieldWithEnterMinBasedOnPosition(yieldDto)) { const hasStaked = Maybe.fromNullable(positionsData.get(yieldDto.id)) .map((val) => [...val.balanceData.values()]) .map((val) => - val.some((v) => v.balances.some((b) => b.type === "active")) + val.some((v) => v.balances.some((b) => b.type === "active")), ) .orDefault(false); @@ -191,11 +192,11 @@ export const getMinStakeAmount = ( }; export const getMinUnstakeAmount = ( - yieldDto: YieldDto, - pricePerShare: string | null + yieldDto: Yield, + pricePerShare: string | null, ) => { const integrationMin = new BigNumber( - yieldDto.args.exit?.args?.amount?.minimum ?? 0 + getYieldActionArg(yieldDto, "exit", "amount")?.minimum ?? 0, ); const pricePerShareBN = new BigNumber(pricePerShare ?? 0); diff --git a/packages/widget/src/domain/types/token-balance.ts b/packages/widget/src/domain/types/token-balance.ts new file mode 100644 index 00000000..c495f168 --- /dev/null +++ b/packages/widget/src/domain/types/token-balance.ts @@ -0,0 +1,11 @@ +import type { + TokenBalanceScanDto, + TokenBalanceScanResponseDto, + YieldBalanceLabelDto, +} from "@stakekit/api-hooks"; + +export type { + TokenBalanceScanDto, + TokenBalanceScanResponseDto, + YieldBalanceLabelDto, +}; diff --git a/packages/widget/src/domain/types/tokens.ts b/packages/widget/src/domain/types/tokens.ts index 92af25e3..f1dee211 100644 --- a/packages/widget/src/domain/types/tokens.ts +++ b/packages/widget/src/domain/types/tokens.ts @@ -1,4 +1,13 @@ -import { EvmNetworks, type TokenDto } from "@stakekit/api-hooks"; +import { + EvmNetworks, + type TokenDto as LegacyTokenDto, + type TokenGetTokensParams, +} from "@stakekit/api-hooks"; +import type { components } from "../../types/yield-api-schema"; + +export type YieldTokenDto = components["schemas"]["TokenDto"]; +export type TokenDto = LegacyTokenDto | YieldTokenDto; +export type { TokenGetTokensParams }; export type TokenString = `${TokenDto["network"]}-${TokenDto["address"]}`; diff --git a/packages/widget/src/domain/types/transaction.ts b/packages/widget/src/domain/types/transaction.ts index a33c46f5..13d268e6 100644 --- a/packages/widget/src/domain/types/transaction.ts +++ b/packages/widget/src/domain/types/transaction.ts @@ -1,3 +1,4 @@ +import type { TransactionVerificationMessageDto } from "@stakekit/api-hooks"; import type { GetType } from "purify-ts"; import { array, @@ -15,6 +16,8 @@ import { import { type Address, type Hex, numberToHex } from "viem"; import type { GetEitherRight } from "../../types/utils"; +export type { TransactionVerificationMessageDto }; + const bigintCodec = Codec.custom({ decode: (input) => { if (typeof input !== "string" && typeof input !== "number") { @@ -124,7 +127,7 @@ export const unsignedTonTransactionCodec = oneOf([ address: string, amount: string, payload: string, - }) + }), ), ]); diff --git a/packages/widget/src/domain/types/tron.ts b/packages/widget/src/domain/types/tron.ts new file mode 100644 index 00000000..e1a52ab0 --- /dev/null +++ b/packages/widget/src/domain/types/tron.ts @@ -0,0 +1,3 @@ +import type { TronResourceType } from "@stakekit/api-hooks"; + +export type { TronResourceType }; diff --git a/packages/widget/src/domain/types/validators.ts b/packages/widget/src/domain/types/validators.ts new file mode 100644 index 00000000..9f396371 --- /dev/null +++ b/packages/widget/src/domain/types/validators.ts @@ -0,0 +1,44 @@ +import type { ValidatorDto as LegacyValidatorDto } from "@stakekit/api-hooks"; +import type { components } from "../../types/yield-api-schema"; + +export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; +export type ValidatorDto = LegacyValidatorDto; + +export const toValidatorDto = ( + validatorDto: YieldValidatorDto | ValidatorDto, +): ValidatorDto => { + const legacyValidator = validatorDto as ValidatorDto; + const rewardRate = + "rewardRate" in validatorDto ? validatorDto.rewardRate : undefined; + const providerId = + "provider" in validatorDto + ? (validatorDto.provider?.id ?? validatorDto.providerId) + : validatorDto.providerId; + const image = + "logoURI" in validatorDto ? validatorDto.logoURI : legacyValidator.image; + const stakedBalance = + "tvl" in validatorDto ? validatorDto.tvl : legacyValidator.stakedBalance; + + return { + address: validatorDto.address, + apr: "apr" in validatorDto ? validatorDto.apr : rewardRate?.total, + commission: validatorDto.commission, + image, + minimumStake: validatorDto.minimumStake, + name: validatorDto.name, + nominatorCount: validatorDto.nominatorCount, + preferred: validatorDto.preferred, + pricePerShare: validatorDto.pricePerShare, + providerId, + remainingPossibleStake: validatorDto.remainingPossibleStake, + remainingSlots: validatorDto.remainingSlots, + stakedBalance, + status: validatorDto.status as ValidatorDto["status"], + subnetId: validatorDto.subnetId, + subnetName: + "subnetName" in validatorDto ? validatorDto.subnetName : undefined, + tokenSymbol: validatorDto.tokenSymbol, + votingPower: validatorDto.votingPower, + website: validatorDto.website, + }; +}; diff --git a/packages/widget/src/domain/types/wallets/generic-wallet.ts b/packages/widget/src/domain/types/wallets/generic-wallet.ts index 0f3dd1b2..18094865 100644 --- a/packages/widget/src/domain/types/wallets/generic-wallet.ts +++ b/packages/widget/src/domain/types/wallets/generic-wallet.ts @@ -1,8 +1,3 @@ -import type { - ActionDto, - RewardTypes, - TransactionDto, -} from "@stakekit/api-hooks"; import type { DecodedEVMTransaction, DecodedSolanaTransaction, @@ -10,6 +5,9 @@ import type { DecodedTonTransaction, DecodedTronTransaction, } from "../../types/transaction"; +import type { ActionDto, TransactionDto } from "../action"; +import type { RewardTypes } from "../reward-rate"; +import type { TokenDto, YieldTokenDto } from "../tokens"; type EVMTx = { type: "evm"; @@ -42,7 +40,7 @@ export type ActionMeta = { actionId: ActionDto["id"]; actionType: ActionDto["type"]; amount: ActionDto["amount"]; - inputToken: ActionDto["inputToken"]; + inputToken: TokenDto | YieldTokenDto | undefined; providersDetails: { name: string; address: string | undefined; @@ -64,7 +62,7 @@ export type SKWallet = { getTransactionReceipt?(txHash: string): Promise<{ transactionHash?: string }>; sendTransaction( tx: SKTx, - txMeta: SKTxMeta + txMeta: SKTxMeta, ): Promise< | string | { type: "success"; txHash: string } diff --git a/packages/widget/src/domain/types/yield-api.ts b/packages/widget/src/domain/types/yield-api.ts new file mode 100644 index 00000000..0f7318ac --- /dev/null +++ b/packages/widget/src/domain/types/yield-api.ts @@ -0,0 +1,4 @@ +import type { Client } from "openapi-fetch"; +import type { paths } from "../../types/yield-api-schema"; + +export type YieldApiFetchClient = Client; diff --git a/packages/widget/src/domain/types/yields.ts b/packages/widget/src/domain/types/yields.ts index 409ba363..56dd1bab 100644 --- a/packages/widget/src/domain/types/yields.ts +++ b/packages/widget/src/domain/types/yields.ts @@ -1,11 +1,38 @@ -import type { ValidatorDto, YieldDto, YieldType } from "@stakekit/api-hooks"; +import type { + YieldType as LegacyYieldType, + YieldDto as OldYieldDto, +} from "@stakekit/api-hooks"; import { EvmNetworks } from "@stakekit/common"; import BigNumber from "bignumber.js"; import type { TFunction } from "i18next"; import { Maybe } from "purify-ts"; +import type { components } from "../../types/yield-api-schema"; import type { SupportedSKChains } from "./chains"; +import type { RewardTypes } from "./reward-rate"; +import type { YieldTokenDto } from "./tokens"; +import type { ValidatorDto } from "./validators"; -export type ExtendedYieldType = YieldType | "native_staking" | "pooled_staking"; +export type Yield = components["schemas"]["YieldDto"] & { + __fallback__: OldYieldDto; +}; + +export type YieldApiYieldDto = components["schemas"]["YieldDto"]; +export type YieldMetadata = OldYieldDto["metadata"]; +export type ExtendedYieldType = + | LegacyYieldType + | "liquid-staking" + | "native_staking" + | "pooled_staking"; +export type YieldActionType = "enter" | "exit"; +export type YieldArgumentName = + components["schemas"]["ArgumentFieldDto"]["name"]; + +type YieldArgumentConfig = { + required?: boolean; + minimum?: number | null; + maximum?: number | null; + options?: string[]; +} & Record; type YieldTypeLabelsMap = { [Key in ExtendedYieldType]: { @@ -35,11 +62,11 @@ export const filterValidators = ({ }: { validatorsConfig: ValidatorsConfig; validators: ValidatorDto[]; - network: YieldDto["token"]["network"]; - yieldId?: YieldDto["id"]; + network: Yield["token"]["network"]; + yieldId?: Yield["id"]; }) => { const valConfig = Maybe.fromNullable( - validatorsConfig.get(network as SupportedSKChains) + validatorsConfig.get(network as SupportedSKChains), ) .altLazy(() => Maybe.fromNullable(validatorsConfig.get("*"))) .extractNullable(); @@ -78,16 +105,247 @@ export const filterValidators = ({ return filtered; }; -export const getExtendedYieldType = (yieldDto: YieldDto) => +const toNumber = (value: number | string | null | undefined) => { + if (value === null || value === undefined) { + return undefined; + } + + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : undefined; +}; + +const secondsToDays = (seconds: number | undefined) => { + if (seconds === undefined) return undefined; + + return { days: Math.round(seconds / 86400) }; +}; + +const isSameToken = ( + left: Pick, + right: Pick, +) => + left.network === right.network && + left.symbol === right.symbol && + (left.address?.toLowerCase() ?? "") === (right.address?.toLowerCase() ?? ""); + +const mapMechanicsType = ( + type: Yield["mechanics"]["type"], +): Exclude< + ExtendedYieldType, + "liquid-staking" | "native_staking" | "pooled_staking" +> => { + switch (type) { + case "staking": + case "restaking": + case "lending": + case "vault": + return type; + case "liquidity_pool": + case "concentrated_liquidity_pool": + case "fixed_yield": + case "real_world_asset": + return "vault"; + } +}; + +const getBaseYieldType = ( + yieldDto: Yield, +): LegacyYieldType | "liquid-staking" => { + if ( + yieldDto.mechanics.type === "staking" && + (yieldDto.__fallback__.metadata.type === "liquid-staking" || + (!!yieldDto.outputToken && + !isSameToken(yieldDto.outputToken, yieldDto.token))) + ) { + return "liquid-staking"; + } + + return mapMechanicsType(yieldDto.mechanics.type); +}; + +const getFallbackActionArg = ( + yieldDto: Yield, + type: YieldActionType, + name: YieldArgumentName, +) => { + const legacyArgs = yieldDto.__fallback__.args?.[type]?.args as + | Record + | undefined; + const legacyField = legacyArgs?.[name] as YieldArgumentConfig | undefined; + + if ( + !legacyField || + typeof legacyField !== "object" || + Array.isArray(legacyField) + ) { + return undefined; + } + + return legacyField; +}; + +export const getYieldActionArg = ( + yieldDto: Yield, + type: YieldActionType, + name: YieldArgumentName, +): YieldArgumentConfig | null => { + const field = yieldDto.mechanics.arguments?.[type]?.fields?.find( + (item) => item.name === name, + ); + const legacyField = getFallbackActionArg(yieldDto, type, name); + + if (!field && !legacyField) { + return null; + } + + return { + ...(legacyField ?? {}), + ...(field + ? { + required: !!field.required, + minimum: toNumber(field.minimum) ?? legacyField?.minimum ?? null, + maximum: toNumber(field.maximum) ?? legacyField?.maximum ?? null, + ...(field.options ? { options: field.options } : {}), + } + : {}), + }; +}; + +export const isYieldActionArgRequired = ( + yieldDto: Yield, + type: YieldActionType, + name: YieldArgumentName, +) => !!getYieldActionArg(yieldDto, type, name)?.required; + +export const getYieldRewardRate = (yieldDto: Yield) => + yieldDto.rewardRate?.total ?? yieldDto.__fallback__.rewardRate ?? 0; + +export const getYieldRewardType = (yieldDto: Yield): RewardTypes => { + const rateType = yieldDto.rewardRate?.rateType?.toLowerCase(); + + if (rateType === "apr" || rateType === "apy") { + return rateType; + } + + return yieldDto.__fallback__.rewardType ?? "variable"; +}; + +const uniqTokens = (tokens: (YieldTokenDto | null | undefined)[]) => { + const seen = new Set(); + + return tokens.flatMap((token) => { + if (!token) return []; + + const key = `${token.network}:${token.address?.toLowerCase() ?? ""}:${token.symbol}`; + + if (seen.has(key)) { + return []; + } + + seen.add(key); + return [token]; + }); +}; + +export const getYieldRewardTokens = (yieldDto: Yield) => { + const derived = uniqTokens( + yieldDto.rewardRate?.components?.map((component) => component.token) ?? [], + ); + + if (derived.length) { + return derived; + } + + return yieldDto.__fallback__.metadata.rewardTokens ?? []; +}; + +export const getYieldGasFeeToken = (yieldDto: Yield) => + yieldDto.mechanics.gasFeeToken ?? + yieldDto.__fallback__.metadata.gasFeeToken ?? + yieldDto.token; + +export const getYieldProviderDetails = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.provider ?? + (yieldDto.providerId + ? { + id: yieldDto.providerId, + name: yieldDto.metadata.name, + logoURI: yieldDto.metadata.logoURI, + externalLink: undefined, + } + : null); + +export const hasYieldFeeConfigurationEnabled = (yieldDto: Yield) => + (yieldDto.__fallback__.feeConfigurations?.length ?? 0) > 0; + +export const getYieldCooldownPeriod = (yieldDto: Yield) => + secondsToDays(yieldDto.mechanics.cooldownPeriod?.seconds) ?? + yieldDto.__fallback__.metadata.cooldownPeriod; + +export const getYieldWarmupPeriod = (yieldDto: Yield) => + secondsToDays(yieldDto.mechanics.warmupPeriod?.seconds) ?? + yieldDto.__fallback__.metadata.warmupPeriod; + +export const getYieldWithdrawPeriod = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.withdrawPeriod; + +export const getYieldRewardSchedule = (yieldDto: Yield) => + yieldDto.mechanics.rewardSchedule ?? + yieldDto.__fallback__.metadata.rewardSchedule; + +export const getYieldRewardClaiming = (yieldDto: Yield) => + yieldDto.mechanics.rewardClaiming ?? + yieldDto.__fallback__.metadata.rewardClaiming; + +export const getYieldMetadata = (yieldDto: Yield): OldYieldDto["metadata"] => { + const fallbackMetadata = yieldDto.__fallback__.metadata; + const tokens = uniqTokens([ + ...(yieldDto.tokens ?? []), + ...(yieldDto.inputTokens ?? []), + ...(fallbackMetadata.tokens ?? []), + ]); + + return { + ...(fallbackMetadata ?? {}), + name: yieldDto.metadata.name ?? fallbackMetadata.name ?? "", + description: + yieldDto.metadata.description ?? fallbackMetadata.description ?? "", + documentation: + yieldDto.metadata.documentation ?? fallbackMetadata.documentation ?? "", + logoURI: yieldDto.metadata.logoURI ?? fallbackMetadata.logoURI ?? "", + type: getBaseYieldType(yieldDto), + token: yieldDto.token ?? fallbackMetadata.token, + tokens: tokens.length ? tokens : fallbackMetadata.tokens, + rewardTokens: getYieldRewardTokens(yieldDto), + rewardSchedule: getYieldRewardSchedule(yieldDto), + rewardClaiming: getYieldRewardClaiming(yieldDto), + cooldownPeriod: getYieldCooldownPeriod(yieldDto), + warmupPeriod: getYieldWarmupPeriod(yieldDto), + withdrawPeriod: getYieldWithdrawPeriod(yieldDto), + gasFeeToken: getYieldGasFeeToken(yieldDto), + provider: getYieldProviderDetails(yieldDto) ?? undefined, + supportsLedgerWalletApi: + yieldDto.mechanics.supportsLedgerWalletApi ?? + fallbackMetadata.supportsLedgerWalletApi, + supportsMultipleValidators: + yieldDto.mechanics.requiresValidatorSelection ?? + fallbackMetadata.supportsMultipleValidators, + supportedStandards: + yieldDto.metadata.supportedStandards ?? + fallbackMetadata.supportedStandards, + } as OldYieldDto["metadata"]; +}; + +export const getExtendedYieldType = (yieldDto: Yield) => isNativeStaking(yieldDto) ? "native_staking" : isPooledStaking(yieldDto) ? "pooled_staking" - : yieldDto.metadata.type; + : getBaseYieldType(yieldDto); export const getYieldTypeLabels = ( - yieldDto: YieldDto, - t: TFunction + yieldDto: Yield, + t: TFunction, ): YieldTypeLabelsMap[keyof YieldTypeLabelsMap] => { const map = { staking: { @@ -142,7 +400,7 @@ export const getYieldTypeLabels = ( return map.pooled_staking; } - return map[yieldDto.metadata.type]; + return map[getExtendedYieldType(yieldDto)]; }; const yieldTypesSortRank: { [Key in ExtendedYieldType]: number } = { @@ -155,33 +413,43 @@ const yieldTypesSortRank: { [Key in ExtendedYieldType]: number } = { restaking: 7, }; -export const getYieldTypesSortRank = (yieldDto: YieldDto) => +export const getYieldTypesSortRank = (yieldDto: Yield) => yieldTypesSortRank[getExtendedYieldType(yieldDto)]; -const isEthereumStaking = (yieldDto: YieldDto) => - yieldDto.metadata.type === "staking" && +const isEthereumStaking = (yieldDto: Yield) => + yieldDto.mechanics.type === "staking" && yieldDto.token.network === EvmNetworks.Ethereum && yieldDto.token.symbol === "ETH"; -const isNativeStaking = (yieldDto: YieldDto) => +const isNativeStaking = (yieldDto: Yield) => Maybe.fromFalsy(isEthereumStaking(yieldDto)) .chain(() => - Maybe.fromFalsy(yieldDto.args.enter.args?.amount?.required).chain(() => - Maybe.fromNullable(yieldDto.args.enter.args?.amount?.minimum) - ) + Maybe.fromFalsy( + isYieldActionArgRequired(yieldDto, "enter", "amount"), + ).chain(() => + Maybe.fromNullable( + getYieldActionArg(yieldDto, "enter", "amount")?.minimum, + ), + ), ) .map(BigNumber) .filter((v) => v.isEqualTo(32)) .isJust(); -const isPooledStaking = (yieldDto: YieldDto) => +const isPooledStaking = (yieldDto: Yield) => isEthereumStaking(yieldDto) && !isNativeStaking(yieldDto); -export const isYieldWithProviderOptions = (yieldDto: YieldDto) => - !!yieldDto.args.enter.args?.providerId?.required; +export const isYieldWithProviderOptions = (yieldDto: Yield) => + !!getYieldActionArg(yieldDto, "enter", "providerId")?.required; + +export const getYieldProviderYieldIds = (yieldDto: Yield) => + getYieldActionArg(yieldDto, "enter", "providerId")?.options ?? []; + +export const hasYieldNftsArg = (yieldDto: Yield) => + !!yieldDto.__fallback__.args.enter.args?.nfts; -export const getYieldProviderYieldIds = (yieldDto: YieldDto) => - yieldDto.args.enter.args?.providerId?.options ?? []; +export const isYieldIntegrationAggregator = (yieldDto: Yield) => + !!yieldDto.__fallback__.metadata.isIntegrationAggregator; export const isEthenaUsdeStaking = (yieldId: string) => yieldId === "ethena-usde-staking"; @@ -194,8 +462,10 @@ const zeroRewardRateYieldIdWhitelist = new Set([ ]); export const isNonZeroRewardRateYield = ( - yieldDto: Pick -) => yieldDto.rewardRate > 0 || zeroRewardRateYieldIdWhitelist.has(yieldDto.id); + yieldDto: Pick, +) => + (yieldDto.rewardRate?.total ?? 0) > 0 || + zeroRewardRateYieldIdWhitelist.has(yieldDto.id); -export const isERC4626 = (yieldDto: YieldDto) => +export const isERC4626 = (yieldDto: Yield) => yieldDto.metadata.supportedStandards?.includes("ERC4626") ?? false; diff --git a/packages/widget/src/hooks/api/use-activity-actions.ts b/packages/widget/src/hooks/api/use-activity-actions.ts index e58fd694..2c0e4f5a 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -1,12 +1,15 @@ -import { type ActionDto, ActionStatus } from "@stakekit/api-hooks"; import { useInfiniteQuery } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; import { useMemo } from "react"; +import { + type ActionDto, + ActionStatus, + getActionInputToken, +} from "../../domain/types/action"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; import { listActions } from "../../providers/yield-api-client-provider/actions"; -import { adaptActionDto } from "../../providers/yield-api-client-provider/compat"; import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportunity"; const PAGE_SIZE = 20; @@ -27,7 +30,7 @@ export const useActivityActions = () => { fetchClient: yieldApiFetchClient, limit: PAGE_SIZE, offset: pageParam, - }) + }), ) .mapLeft(() => new Error("Could not get action list")) .map((actionList) => ({ @@ -36,7 +39,7 @@ export const useActivityActions = () => { (action) => action.status !== ActionStatus.CREATED && (!network || - action.transactions.some((tx) => tx.network === network)) + action.transactions.some((tx) => tx.network === network)), ), })) .chain(async (actionList) => @@ -49,20 +52,23 @@ export const useActivityActions = () => { yieldApiFetchClient, }) .map((yieldData) => ({ - actionData: adaptActionDto({ - actionDto: action, - addresses: { address: action.address }, - gasFeeToken: yieldData.metadata.gasFeeToken, - yieldDto: yieldData, - }) as ActionDto, + actionData: action as ActionDto, yieldData, })) - .chainLeft(() => EitherAsync(() => Promise.resolve(null))) - ) + .chainLeft(() => EitherAsync(() => Promise.resolve(null))), + ), ) .map((res) => res.filter((x) => x !== null)) - .map((res) => res.filter((x) => !!x.actionData.inputToken)) - .map((data) => ({ ...actionList, data })) + .map((res) => + res.filter( + (x) => + !!getActionInputToken({ + actionDto: x.actionData, + yieldDto: x.yieldData, + }), + ), + ) + .map((data) => ({ ...actionList, data })), ) ).unsafeCoerce(); }, @@ -75,7 +81,7 @@ export const useActivityActions = () => { const allItems = useMemo( () => query.data?.pages.flatMap((page) => page.data), - [query.data] + [query.data], ); return { diff --git a/packages/widget/src/hooks/api/use-default-tokens.ts b/packages/widget/src/hooks/api/use-default-tokens.ts index 408ae0ee..6c97fb34 100644 --- a/packages/widget/src/hooks/api/use-default-tokens.ts +++ b/packages/widget/src/hooks/api/use-default-tokens.ts @@ -1,11 +1,9 @@ -import type { - TokenBalanceScanResponseDto, - TokenGetTokensParams, -} from "@stakekit/api-hooks"; import { getTokenGetTokensQueryKey, tokenGetTokens } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; +import type { TokenBalanceScanResponseDto } from "../../domain/types/token-balance"; +import type { TokenGetTokensParams } from "../../domain/types/tokens"; import { useSettings } from "../../providers/settings"; import { useSKWallet } from "../../providers/sk-wallet"; @@ -27,7 +25,7 @@ export const useDefaultTokens = () => { }; export const getDefaultTokens = ( - params: Parameters[0] & { queryClient: QueryClient } + params: Parameters[0] & { queryClient: QueryClient }, ) => EitherAsync(() => params.queryClient.fetchQuery({ @@ -35,7 +33,7 @@ export const getDefaultTokens = ( network: params.network ?? undefined, }), queryFn: async () => (await queryFn(params)).unsafeCoerce(), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("could not get multi yields"); @@ -49,7 +47,7 @@ const queryFn = ({ tokenGetTokens({ network, enabledYieldsOnly: enabledYieldsOnly || undefined, - }) + }), ).map((val) => - val.map((v) => ({ ...v, amount: "0" })) + val.map((v) => ({ ...v, amount: "0" })), ); diff --git a/packages/widget/src/hooks/api/use-multi-yields.ts b/packages/widget/src/hooks/api/use-multi-yields.ts index dd99de4b..9d693fbc 100644 --- a/packages/widget/src/hooks/api/use-multi-yields.ts +++ b/packages/widget/src/hooks/api/use-multi-yields.ts @@ -1,4 +1,3 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import { hashKey, type QueryClient, useQuery } from "@tanstack/react-query"; import { useSelector } from "@xstate/react"; import { createStore } from "@xstate/store"; @@ -32,8 +31,10 @@ import { } from "../../domain/types/stake"; import type { SKWallet } from "../../domain/types/wallet"; import { + hasYieldNftsArg, isNonZeroRewardRateYield, type ValidatorsConfig, + type Yield, } from "../../domain/types/yields"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; @@ -43,11 +44,11 @@ import { useValidatorsConfig } from "../use-validators-config"; import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportunity"; const multiYieldsStore = createStore({ - context: { data: new Map>() }, + context: { data: new Map>() }, on: { "yield-opportunity": ( context, - event: { data: { key: string; yieldDto: YieldDto } } + event: { data: { key: string; yieldDto: Yield } }, ) => { const newMap = new Map(context.data); const prev = newMap.get(event.data.key) ?? new Map(); @@ -101,12 +102,12 @@ export const useStreamMultiYields = (yieldIds: string[]) => { }); }; -export const useMultiYields = ( +export const useMultiYields = ( yieldIds: string[], opts?: { - select?: (val: YieldDto[]) => T; + select?: (val: Yield[]) => T; enabled?: boolean; - } + }, ) => { const { network, isConnected, isLedgerLive } = useSKWallet(); const yieldApiFetchClient = useYieldApiFetchClient(); @@ -130,20 +131,20 @@ export const useMultiYields = ( ...argsRef.current, yieldIds, validatorsConfig, - }).pipe(toArray()) + }).pipe(toArray()), ), select: opts?.select, }); }; export const getFirstEligibleYield = ( - params: Parameters[0] + params: Parameters[0], ) => EitherAsync(() => params.queryClient.fetchQuery({ queryKey: getFirstEligibleYieldQueryKey(params.yieldIds), queryFn: () => firstValueFrom(firstEligibleYield$(params)), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("could not get first eligible yield"); @@ -166,13 +167,13 @@ const multipleYields$ = (args: { yieldId: v, queryClient: args.queryClient, yieldApiFetchClient: args.yieldApiFetchClient, - }) - ) - ) + }), + ), + ), ).pipe( map((v) => (v.isRight() ? v.extract() : null)), filter( - (v): v is YieldDto => + (v): v is Yield => !!( v && defaultFiltered({ @@ -181,8 +182,8 @@ const multipleYields$ = (args: { network: args.network, isLedgerLive: args.isLedgerLive, }).length > 0 - ) - ) + ), + ), ); const firstEligibleYield$ = (args: { @@ -198,7 +199,7 @@ const firstEligibleYield$ = (args: { validatorsConfig: ValidatorsConfig; preferredTokenYieldsPerNetwork: PreferredTokenYieldsPerNetwork | null; }) => { - let defaultYield: YieldDto | null = null; + let defaultYield: Yield | null = null; const successStream = multipleYields$(args).pipe( tap((v) => { @@ -210,12 +211,12 @@ const firstEligibleYield$ = (args: { const preferredYieldId = Maybe.fromNullable( args.preferredTokenYieldsPerNetwork?.[ y.token.network as SupportedSKChains - ]?.[tokenString(y.token)] + ]?.[tokenString(y.token)], ) .altLazy(() => Maybe.fromNullable(args.preferredTokenYieldsPerNetwork).chainNullable( - (v) => Object.values(v)[0][tokenString(y.token)] - ) + (v) => Object.values(v)[0][tokenString(y.token)], + ), ) .extractNullable(); @@ -231,10 +232,10 @@ const firstEligibleYield$ = (args: { }); }), take(1), - defaultIfEmpty(null) + defaultIfEmpty(null), ); - return new Observable((subscriber) => { + return new Observable((subscriber) => { successStream.subscribe({ complete: () => subscriber.complete(), next: (v) => subscriber.next(v ?? defaultYield), @@ -244,7 +245,7 @@ const firstEligibleYield$ = (args: { }; type SelectorInputData = { - data: YieldDto[]; + data: Yield[]; isConnected: boolean; network: SKWallet["network"]; isLedgerLive: boolean; @@ -261,7 +262,7 @@ const defaultFiltered = createSelector( (data, isConnected, network) => data.filter((o) => { const defaultFilter = - !o.args.enter.args?.nfts && + !hasYieldNftsArg(o) && o.id !== "binance-bnb-native-staking" && o.id !== "binance-testnet-bnb-native-staking" && o.id !== "avax-native-staking" && @@ -271,7 +272,7 @@ const defaultFiltered = createSelector( if (!isConnected) return defaultFilter; return network === o.token.network && defaultFilter; - }) + }), ); const getFirstEligibleYieldQueryKey = (yieldIds: string[]) => [ @@ -287,5 +288,5 @@ export const getCachedFirstEligibleYield = ({ yieldIds: string[]; }) => Maybe.fromNullable( - queryClient.getQueryData(getFirstEligibleYieldQueryKey(yieldIds)) + queryClient.getQueryData(getFirstEligibleYieldQueryKey(yieldIds)), ); diff --git a/packages/widget/src/hooks/api/use-prices.ts b/packages/widget/src/hooks/api/use-prices.ts index f9942941..6ce4ccd9 100644 --- a/packages/widget/src/hooks/api/use-prices.ts +++ b/packages/widget/src/hooks/api/use-prices.ts @@ -1,8 +1,11 @@ -import type { PriceRequestDto, PriceResponseDto } from "@stakekit/api-hooks"; import { useTokenGetTokenPrices } from "@stakekit/api-hooks"; import { useCallback } from "react"; import { createSelector } from "reselect"; -import type { Prices } from "../../domain/types/price"; +import type { + PriceRequestDto, + PriceResponseDto, + Prices, +} from "../../domain/types/price"; import type { YieldTokenDto } from "../../providers/yield-api-client-provider/types"; import { priceResponseDtoToPrices } from "../../utils/mappers"; @@ -15,7 +18,7 @@ const defaultParam: PriceRequestDto = { const pricesSelector = createSelector( (val: PriceResponseDto) => val, - (val) => priceResponseDtoToPrices(val) + (val) => priceResponseDtoToPrices(val), ); type PriceRequestInput = Omit & { @@ -27,7 +30,7 @@ export const usePrices = ( opts?: { enabled?: boolean; select?: (val: Prices) => T; - } + }, ) => { const requestDto = priceRequestDto ? ({ @@ -53,7 +56,7 @@ export const usePrices = ( return mapped as T; }, - [opts?.select] + [opts?.select], ), }, }); diff --git a/packages/widget/src/hooks/api/use-token-balances-scan.ts b/packages/widget/src/hooks/api/use-token-balances-scan.ts index 86e14f6d..9b0b05f2 100644 --- a/packages/widget/src/hooks/api/use-token-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-token-balances-scan.ts @@ -1,9 +1,9 @@ -import type { TokenBalanceScanDto } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; import { EitherAsync, Just, Maybe } from "purify-ts"; import { useCallback, useMemo } from "react"; import { tokenTokenBalancesScan } from "../../common/private-api"; +import type { TokenBalanceScanDto } from "../../domain/types/token-balance"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; @@ -38,9 +38,9 @@ export const useTokenBalancesScan = () => { addresses: { address: "", additionalAddresses: undefined }, network: "ethereum", }, - } + }, ), - [additionalAddresses, address, isLedgerLiveAccountPlaceholder, network] + [additionalAddresses, address, isLedgerLiveAccountPlaceholder, network], ); return useQuery({ @@ -57,13 +57,13 @@ export const useTokenBalancesScan = () => { }; export const getTokenBalancesScan = ( - params: Parameters[0] & { queryClient: QueryClient } + params: Parameters[0] & { queryClient: QueryClient }, ) => EitherAsync(() => params.queryClient.fetchQuery({ queryKey: getTokenTokenBalancesScanQueryKey(params.tokenBalanceScanDto), queryFn: async () => (await queryFn(params)).unsafeCoerce(), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("could not get multi yields"); @@ -78,7 +78,7 @@ const queryFn = ({ (e) => { console.log(e); return new Error("could not get token balances"); - } + }, ); export const useInvalidateTokenBalances = () => { @@ -91,12 +91,12 @@ export const useInvalidateTokenBalances = () => { getTokenTokenBalancesScanQueryKey({} as TokenBalanceScanDto)[0], ], }), - [queryClient] + [queryClient], ); }; const getTokenTokenBalancesScanQueryKey = ( - tokenBalanceScanDto: TokenBalanceScanDto + tokenBalanceScanDto: TokenBalanceScanDto, ) => { return ["/v1/tokens/balances/scan", tokenBalanceScanDto] as const; }; diff --git a/packages/widget/src/hooks/api/use-tokens-prices.ts b/packages/widget/src/hooks/api/use-tokens-prices.ts index ff0fb803..23b143eb 100644 --- a/packages/widget/src/hooks/api/use-tokens-prices.ts +++ b/packages/widget/src/hooks/api/use-tokens-prices.ts @@ -1,8 +1,8 @@ -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 type { TokenDto, YieldTokenDto } from "../../domain/types/tokens"; +import type { Yield } from "../../domain/types/yields"; import { useBaseToken } from "../use-base-token"; import { useGasFeeToken } from "../use-gas-fee-token"; import { usePrices } from "./use-prices"; @@ -15,7 +15,7 @@ export const useTokensPrices = ({ yieldDto, }: { token: Maybe; - yieldDto: Maybe; + yieldDto: Maybe; }) => { const baseToken = useBaseToken(yieldDto); const gasFeeToken = useGasFeeToken(yieldDto); @@ -28,7 +28,7 @@ export const useTokensPrices = ({ tokenList: [val.token, val.baseToken, val.gasFeeToken], })) .extractNullable(), - [baseToken, gasFeeToken, token] + [baseToken, gasFeeToken, token], ); return usePrices(priceRequestDto); 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 99ad6117..a1f79c41 100644 --- a/packages/widget/src/hooks/api/use-yield-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-yield-balances-scan.ts @@ -1,14 +1,14 @@ import type { UseQueryResult } from "@tanstack/react-query"; import { Maybe } from "purify-ts"; import { useCallback, useMemo } from "react"; +import type { + YieldBalancesByYieldDto, + YieldBalancesRequestDto, +} from "../../domain/types/positions"; 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 = (opts?: { @@ -22,7 +22,7 @@ export const useYieldBalancesScan = (opts?: { const lastActionTimestamp = useMemo( () => actionHistoryData.map((v) => v.timestamp).extractNullable(), - [actionHistoryData] + [actionHistoryData], ); const param = useMemo( @@ -48,9 +48,9 @@ export const useYieldBalancesScan = (opts?: { dto: { queries: [{ address: "", network: "ethereum" }], }, - } + }, ), - [address, network] + [address, network], ); const res = yieldApi.useQuery( @@ -71,7 +71,7 @@ export const useYieldBalancesScan = (opts?: { return items as T; }, - } + }, ); /** @@ -97,7 +97,7 @@ export const useInvalidateYieldBalances = () => { queryClient.invalidateQueries({ queryKey: getYieldYieldBalancesScanQueryKey(), }), - [queryClient] + [queryClient], ); }; diff --git a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts index 3c98ddc2..14f3d9a1 100644 --- a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts +++ b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts @@ -1,11 +1,9 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; import { yieldYieldOpportunity } from "../../../common/private-api"; -import { isEthenaUsdeStaking } from "../../../domain/types/yields"; -import { adaptYieldDto } from "../../../providers/yield-api-client-provider/compat"; +import type { YieldApiFetchClient } from "../../../domain/types/yield-api"; +import { isEthenaUsdeStaking, type Yield } from "../../../domain/types/yields"; import { getResponseData } from "../../../providers/yield-api-client-provider/request-helpers"; -import type { YieldApiFetchClient } from "../../../providers/yield-api-client-provider/types"; type Params = { yieldId: string; @@ -24,14 +22,14 @@ const getKey = (params: Params) => [ export const getYieldOpportunity = ( params: Params & { queryClient: QueryClient; - } + }, ) => EitherAsync(() => params.queryClient.fetchQuery({ queryKey: getKey(params), staleTime, queryFn: () => queryFn(params), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get yield opportunity"); @@ -40,7 +38,7 @@ export const getYieldOpportunity = ( export const queryFn = async ( params: Params & { signal?: AbortSignal; - } + }, ) => (await fn(params)).unsafeCoerce(); const fn = ({ @@ -51,13 +49,8 @@ const fn = ({ }: Params & { signal?: AbortSignal; }) => { - const stripValidators = (yieldDto: YieldDto): YieldDto => ({ - ...yieldDto, - validators: [], - }); - return EitherAsync(async () => { - const [newYieldResult, legacyYieldResult] = await Promise.allSettled([ + const [newYieldResult, legacyYieldResult] = await Promise.all([ getResponseData( yieldApiFetchClient.GET("/v1/yields/{yieldId}", { params: { @@ -66,32 +59,19 @@ const fn = ({ }, }, signal, - }) + }), ), yieldYieldOpportunity( yieldId, { ledgerWalletAPICompatible: isLedgerLive }, - signal + signal, ), ]); - if (newYieldResult.status === "rejected") { - if (legacyYieldResult.status === "fulfilled") { - return stripValidators(legacyYieldResult.value); - } - - throw newYieldResult.reason; - } - - const merged = adaptYieldDto({ - yieldDto: newYieldResult.value, - legacyYieldDto: - legacyYieldResult.status === "fulfilled" - ? legacyYieldResult.value - : null, - }); - - return stripValidators(merged); + return { + ...newYieldResult, + __fallback__: legacyYieldResult, + } satisfies Yield; }) .map((y) => isEthenaUsdeStaking(y.id) @@ -101,8 +81,8 @@ const fn = ({ ...y.metadata, name: y.metadata.name.replace(/staking/i, ""), }, - } satisfies YieldDto) - : y + } satisfies Yield) + : y, ) .mapLeft((e) => { console.log(e); diff --git a/packages/widget/src/hooks/api/use-yield-validators.ts b/packages/widget/src/hooks/api/use-yield-validators.ts index 7a19a8af..7d97b176 100644 --- a/packages/widget/src/hooks/api/use-yield-validators.ts +++ b/packages/widget/src/hooks/api/use-yield-validators.ts @@ -1,9 +1,11 @@ -import type { ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import { useQuery } from "@tanstack/react-query"; +import { + toValidatorDto, + type ValidatorDto, +} from "../../domain/types/validators"; import type { ValidatorsConfig } from "../../domain/types/yields"; -import { filterValidators } from "../../domain/types/yields"; +import { filterValidators, type Yield } from "../../domain/types/yields"; import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; -import { adaptValidatorDto } from "../../providers/yield-api-client-provider/compat"; import { getResponseData } from "../../providers/yield-api-client-provider/request-helpers"; import { useValidatorsConfig } from "../use-validators-config"; @@ -12,7 +14,7 @@ const staleTime = 1000 * 60 * 2; type Params = { yieldId: string; - network?: YieldDto["token"]["network"]; + network?: Yield["token"]["network"]; validatorsConfig: ValidatorsConfig; yieldApiFetchClient: ReturnType; signal?: AbortSignal; @@ -43,25 +45,25 @@ export const getYieldValidatorsQueryFn = async ({ }, }, signal, - }) + }), ); const firstPage = await fetchPage(0); const remainingOffsets = Array.from( { length: Math.ceil(firstPage.total / PAGE_SIZE) - 1 }, - (_, index) => (index + 1) * PAGE_SIZE + (_, index) => (index + 1) * PAGE_SIZE, ); const remainingPages = await Promise.all( remainingOffsets.map((offset) => - fetchPage(offset).catch(() => ({ items: [] })) - ) + fetchPage(offset).catch(() => ({ items: [] })), + ), ); const validators = [firstPage, ...remainingPages] .flatMap((page) => page.items ?? []) - .map(adaptValidatorDto); + .map(toValidatorDto); return network ? filterValidators({ @@ -87,7 +89,7 @@ export const useYieldValidators = ({ }: { enabled?: boolean; yieldId?: string; - network?: YieldDto["token"]["network"]; + network?: Yield["token"]["network"]; }) => { const yieldApiFetchClient = useYieldApiFetchClient(); const validatorsConfig = useValidatorsConfig(); diff --git a/packages/widget/src/hooks/navigation/use-dashboard-tabs-page-match.ts b/packages/widget/src/hooks/navigation/use-dashboard-tabs-page-match.ts deleted file mode 100644 index 4233a279..00000000 --- a/packages/widget/src/hooks/navigation/use-dashboard-tabs-page-match.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useMatch } from "react-router"; - -export const useDashboardTabsPageMatch = () => { - const rootMatch = useMatch("/"); - const rewardsMatch = useMatch("/rewards"); - const activityMatch = useMatch("/activity"); - - return !!(rootMatch || rewardsMatch || activityMatch); -}; diff --git a/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts b/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts index f38b0b5d..1ecdfb03 100644 --- a/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts +++ b/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts @@ -14,6 +14,6 @@ export const useNavigateWithScrollToTop = () => { } return navigate(to, options); }, - [navigate, disableAutoScrollToTop] + [navigate, disableAutoScrollToTop], ); }; diff --git a/packages/widget/src/hooks/tracking/use-track-page.ts b/packages/widget/src/hooks/tracking/use-track-page.ts index 8cf73921..316ca690 100644 --- a/packages/widget/src/hooks/tracking/use-track-page.ts +++ b/packages/widget/src/hooks/tracking/use-track-page.ts @@ -5,7 +5,7 @@ import { useSavedRef } from "../use-saved-ref"; export const useTrackPage = ( pageName: TrackPageKey, - properties?: Properties + properties?: Properties, ) => { const { trackPageView } = useTracking(); diff --git a/packages/widget/src/hooks/use-add-ledger-account.ts b/packages/widget/src/hooks/use-add-ledger-account.ts index e239c600..29e5e40a 100644 --- a/packages/widget/src/hooks/use-add-ledger-account.ts +++ b/packages/widget/src/hooks/use-add-ledger-account.ts @@ -16,10 +16,10 @@ export const useAddLedgerAccount = () => { await EitherAsync.liftEither( connector && isLedgerLiveConnector(connector) ? Right(connector) - : Left(new Error("Only Ledger Live is supported")) + : Left(new Error("Only Ledger Live is supported")), ) .chain((ledgerLiveConnector) => - ledgerLiveConnector.requestAndSwitchAccount(chain) + ledgerLiveConnector.requestAndSwitchAccount(chain), ) .ifRight(closeChainModal) ).unsafeCoerce(); diff --git a/packages/widget/src/hooks/use-base-token.ts b/packages/widget/src/hooks/use-base-token.ts index be048069..91a606a1 100644 --- a/packages/widget/src/hooks/use-base-token.ts +++ b/packages/widget/src/hooks/use-base-token.ts @@ -1,7 +1,7 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import { useMemo } from "react"; import { getBaseToken } from "../domain"; +import type { Yield } from "../domain/types/yields"; -export const useBaseToken = (yieldDto: Maybe) => +export const useBaseToken = (yieldDto: Maybe) => useMemo(() => yieldDto.map((val) => getBaseToken(val)), [yieldDto]); diff --git a/packages/widget/src/hooks/use-estimated-rewards.ts b/packages/widget/src/hooks/use-estimated-rewards.ts index bc6e7bdb..26efd015 100644 --- a/packages/widget/src/hooks/use-estimated-rewards.ts +++ b/packages/widget/src/hooks/use-estimated-rewards.ts @@ -1,8 +1,11 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import { useMemo } from "react"; -import { isBittensorStaking } from "../domain/types/yields"; +import { + getYieldRewardType, + isBittensorStaking, + type Yield, +} from "../domain/types/yields"; import type { State } from "../pages/details/earn-page/state/types"; import { formatNumber } from "../utils"; import { getRewardRateFormatted } from "../utils/formatters"; @@ -14,7 +17,7 @@ export const useEstimatedRewards = ({ selectedValidators, selectedProviderYieldId, }: { - selectedStake: Maybe; + selectedStake: Maybe; stakeAmount: State["stakeAmount"]; selectedValidators: State["selectedValidators"]; selectedProviderYieldId: State["selectedProviderYieldId"]; @@ -46,18 +49,18 @@ export const useEstimatedRewards = ({ rewardRateAverage: val.providersDetails .reduce( (acc, val) => acc.plus(new BigNumber(val.rewardRate ?? 0)), - new BigNumber(0) + new BigNumber(0), ) .dividedBy(val.providersDetails.length), })) .map((val) => ({ percentage: getRewardRateFormatted({ rewardRate: val.rewardRateAverage.toNumber(), - rewardType: val.selectedStake.rewardType, + rewardType: getYieldRewardType(val.selectedStake), }), yearly: val.rewardRateAverage.isGreaterThan(0) ? formatNumber( - correctAmount.times(val.rewardRateAverage).decimalPlaces(5) + correctAmount.times(val.rewardRateAverage).decimalPlaces(5), ) : "-", monthly: val.rewardRateAverage.isGreaterThan(0) @@ -65,10 +68,10 @@ export const useEstimatedRewards = ({ correctAmount .times(val.rewardRateAverage) .dividedBy(12) - .decimalPlaces(5) + .decimalPlaces(5), ) : "-", })), - [providersDetails, selectedStake, correctAmount] + [providersDetails, selectedStake, correctAmount], ); }; diff --git a/packages/widget/src/hooks/use-force-max-amount.ts b/packages/widget/src/hooks/use-force-max-amount.ts index b9e396d2..4904751a 100644 --- a/packages/widget/src/hooks/use-force-max-amount.ts +++ b/packages/widget/src/hooks/use-force-max-amount.ts @@ -1,6 +1,6 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import { isForceMaxAmount } from "../domain/types/stake"; +import { getYieldActionArg, type Yield } from "../domain/types/yields"; /** * Check if we need to use max amount for staking/unstaking @@ -11,11 +11,9 @@ export const useForceMaxAmount = ({ integration, }: { type: "enter" | "exit"; - integration: Maybe; + integration: Maybe; }) => integration - .chainNullable((v) => - type === "enter" ? v.args.enter.args?.amount : v.args.exit?.args?.amount - ) + .chainNullable((v) => getYieldActionArg(v, type, "amount")) .map(isForceMaxAmount) .orDefault(false); diff --git a/packages/widget/src/hooks/use-gas-fee-token.ts b/packages/widget/src/hooks/use-gas-fee-token.ts index cd85ba37..76bc914c 100644 --- a/packages/widget/src/hooks/use-gas-fee-token.ts +++ b/packages/widget/src/hooks/use-gas-fee-token.ts @@ -1,7 +1,7 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import { useMemo } from "react"; import { getGasFeeToken } from "../domain"; +import type { Yield } from "../domain/types/yields"; -export const useGasFeeToken = (yieldDto: Maybe) => +export const useGasFeeToken = (yieldDto: Maybe) => useMemo(() => yieldDto.map((val) => getGasFeeToken(val)), [yieldDto]); diff --git a/packages/widget/src/hooks/use-gas-warning-check.ts b/packages/widget/src/hooks/use-gas-warning-check.ts index 62b60e53..99120b7f 100644 --- a/packages/widget/src/hooks/use-gas-warning-check.ts +++ b/packages/widget/src/hooks/use-gas-warning-check.ts @@ -1,4 +1,3 @@ -import type { AddressesDto, TokenDto } from "@stakekit/api-hooks"; import { useQuery } from "@tanstack/react-query"; import type BigNumber from "bignumber.js"; import { EitherAsync, type Maybe } from "purify-ts"; @@ -8,6 +7,8 @@ import { GasTokenMissingError, NotEnoughGasTokenError, } from "../common/check-gas-amount"; +import type { AddressesDto } from "../domain/types/addresses"; +import type { TokenDto } from "../domain/types/tokens"; export const useGasWarningCheck = ( props: { @@ -19,7 +20,7 @@ export const useGasWarningCheck = ( } & ( | { isStake: true; stakeAmount: BigNumber; stakeToken: TokenDto } | { isStake: false } - ) + ), ) => { const requestData = useMemo( () => @@ -34,7 +35,7 @@ export const useGasWarningCheck = ( } : { isStake: props.isStake }, })), - [props] + [props], ); return useQuery({ @@ -44,27 +45,31 @@ export const useGasWarningCheck = ( queryFn: async () => { return ( await EitherAsync.liftEither( - requestData.toEither(new Error("Request data is missing")) + requestData.toEither(new Error("Request data is missing")), ) .chain((val) => checkGasAmount({ gasEstimate: { amount: val.gasAmount, - token: val.gasFeeToken, + token: val.gasFeeToken as NonNullable< + Parameters[0]["gasEstimate"] + >["token"], }, addressWithTokenDto: { address: val.address, additionalAddresses: val.additionalAddresses, - network: val.gasFeeToken.network, + network: val.gasFeeToken.network as Parameters< + typeof checkGasAmount + >[0]["addressWithTokenDto"]["network"], tokenAddress: val.gasFeeToken.address, }, ...val.stakeData, - }) + }), ) .map( (val) => val instanceof NotEnoughGasTokenError || - val instanceof GasTokenMissingError + val instanceof GasTokenMissingError, ) ).unsafeCoerce(); }, diff --git a/packages/widget/src/hooks/use-geo-block.ts b/packages/widget/src/hooks/use-geo-block.ts index f18ceb6a..9a89b427 100644 --- a/packages/widget/src/hooks/use-geo-block.ts +++ b/packages/widget/src/hooks/use-geo-block.ts @@ -1,7 +1,9 @@ -import type { GeolocationError } from "@stakekit/api-hooks"; -import { GeolocationErrorType } from "@stakekit/api-hooks"; import type { AxiosInstance } from "axios"; import { useCallback, useSyncExternalStore } from "react"; +import { + type GeolocationError, + GeolocationErrorType, +} from "../domain/types/errors"; let _isGeoBlocked: | false @@ -66,5 +68,5 @@ export const useGeoBlock = () => }; }, []), useCallback(() => _isGeoBlocked, []), - useCallback(() => _isGeoBlocked, []) + useCallback(() => _isGeoBlocked, []), ); diff --git a/packages/widget/src/hooks/use-handle-deep-links.ts b/packages/widget/src/hooks/use-handle-deep-links.ts index 82b1ce27..103ed9c9 100644 --- a/packages/widget/src/hooks/use-handle-deep-links.ts +++ b/packages/widget/src/hooks/use-handle-deep-links.ts @@ -23,10 +23,10 @@ export const useHandleDeepLinks = () => { useEffect(() => { initQueryParams .filter((val) => - Boolean(val.yieldId && val.balanceId && !val.pendingaction && appReady) + Boolean(val.yieldId && val.balanceId && !val.pendingaction && appReady), ) .ifJust((val) => - navigateRef.current(`positions/${val.yieldId}/${val.balanceId}`) + navigateRef.current(`positions/${val.yieldId}/${val.balanceId}`), ); }, [initQueryParams, navigateRef, appReady]); @@ -35,12 +35,12 @@ export const useHandleDeepLinks = () => { Maybe.fromNullable(pendingActionDeepLinkCheck.data) .filter( (val): val is Extract => - appReady && val.type === "positionDetails" + appReady && val.type === "positionDetails", ) .ifJust((val) => navigateRef.current( - `positions/${val.yieldOp.id}/${val.balanceId}/select-validator/${val.pendingAction.type}` - ) + `positions/${val.yieldOp.id}/${val.balanceId}/select-validator/${val.pendingAction.type}`, + ), ); }, [navigateRef, pendingActionDeepLinkCheck.data, appReady]); @@ -49,7 +49,7 @@ export const useHandleDeepLinks = () => { Maybe.fromNullable(pendingActionDeepLinkCheck.data) .filter( (val): val is Extract => - appReady && val.type === "review" + appReady && val.type === "review", ) .ifJust((val) => { pendignActionStore.send({ @@ -67,7 +67,7 @@ export const useHandleDeepLinks = () => { }, }); navigateRef.current( - `positions/${val.yieldOp.id}/${val.balanceId}/pending-action/review` + `positions/${val.yieldOp.id}/${val.balanceId}/pending-action/review`, ); }); }, [ diff --git a/packages/widget/src/hooks/use-init-params.ts b/packages/widget/src/hooks/use-init-params.ts index db7f1147..06392cc2 100644 --- a/packages/widget/src/hooks/use-init-params.ts +++ b/packages/widget/src/hooks/use-init-params.ts @@ -39,7 +39,7 @@ export const useInitParams = (opts?: { }; export const getInitParams = ( - params: Parameters[0] & { queryClient: QueryClient } + params: Parameters[0] & { queryClient: QueryClient }, ) => EitherAsync(() => params.queryClient.fetchQuery({ @@ -47,7 +47,7 @@ export const getInitParams = ( staleTime, gcTime: cacheTime, queryFn: () => queryFn(params), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("could not get init query params"); @@ -70,7 +70,7 @@ const fn = ({ EitherAsync.liftEither( getAndValidateInitParams({ externalProviderInitToken: externalProviders?.initToken, - }).toEither(new Error("missing query params")) + }).toEither(new Error("missing query params")), ).chain((val) => { const yId = val.yieldId; diff --git a/packages/widget/src/hooks/use-init-query-params.ts b/packages/widget/src/hooks/use-init-query-params.ts index 8174f161..edc78302 100644 --- a/packages/widget/src/hooks/use-init-query-params.ts +++ b/packages/widget/src/hooks/use-init-query-params.ts @@ -17,7 +17,7 @@ export const useInitQueryParams = () => { getAndValidateInitParams({ externalProviderInitToken: externalProviders?.initToken, }), - [externalProviders?.initToken] + [externalProviders?.initToken], ); }; @@ -28,7 +28,7 @@ const pendingActionCodec = Codec.custom({ .chain((v) => /^[A-Z_]+$/.test(v) ? Right(v as YieldPendingActionType) - : Left("invalid pending action") + : Left("invalid pending action"), ), encode: (val) => val, }); @@ -48,7 +48,7 @@ const safeParamCodec = Codec.custom({ string .decode(val) .chain((v) => - safeString.test(v) ? Right(v) : Left("invalid string value") + safeString.test(v) ? Right(v) : Left("invalid string value"), ), encode: (val) => val, }); @@ -90,8 +90,8 @@ export const getAndValidateInitParams = ({ safeParamCodec.decode(url.searchParams.get("token")).chain((val) => val.includes("-") ? Right(val.split("-").slice(0, -1).join("-")) // network is first part of TokenString - : Left("invalid TokenString") - ) + : Left("invalid TokenString"), + ), ) .chain(skSupportedChainsCodec.decode) .toMaybe() diff --git a/packages/widget/src/hooks/use-invalidate-query-n-times.ts b/packages/widget/src/hooks/use-invalidate-query-n-times.ts index 0b3eb9ef..811c8ca2 100644 --- a/packages/widget/src/hooks/use-invalidate-query-n-times.ts +++ b/packages/widget/src/hooks/use-invalidate-query-n-times.ts @@ -35,8 +35,8 @@ export const useInvalidateQueryNTimes = ({ EitherAsync(async () => { await waitForMs(waitMs); await queryClient.invalidateQueries({ queryKey }); - }).chainLeft(async () => Right(null)) - ) + }).chainLeft(async () => Right(null)), + ), ); return null; diff --git a/packages/widget/src/hooks/use-local-storage-value.ts b/packages/widget/src/hooks/use-local-storage-value.ts index 61da7b7a..082898e0 100644 --- a/packages/widget/src/hooks/use-local-storage-value.ts +++ b/packages/widget/src/hooks/use-local-storage-value.ts @@ -6,12 +6,12 @@ import { } from "../services/local-storage"; export const useLocalStorageValue = ( - key: K + key: K, ) => { const [init] = useState(() => getStorageItem(key) .mapLeft(() => null) - .extract() + .extract(), ); const value = useRef(init); @@ -26,9 +26,9 @@ export const useLocalStorageValue = ( return () => removeListener(); }, - [key] + [key], ), useCallback(() => value.current, []), - useCallback(() => null, []) + useCallback(() => null, []), ); }; diff --git a/packages/widget/src/hooks/use-logout.ts b/packages/widget/src/hooks/use-logout.ts index 49052555..cae0d6f0 100644 --- a/packages/widget/src/hooks/use-logout.ts +++ b/packages/widget/src/hooks/use-logout.ts @@ -10,7 +10,7 @@ export const useLogout = () => { await EitherAsync(disconnect) .chain(() => EitherAsync(() => indexedDB.databases())) .ifRight((dbs) => - dbs.forEach((db) => db.name && indexedDB.deleteDatabase(db.name)) + dbs.forEach((db) => db.name && indexedDB.deleteDatabase(db.name)), ); return null; diff --git a/packages/widget/src/hooks/use-max-min-yield-amount.ts b/packages/widget/src/hooks/use-max-min-yield-amount.ts index 7b2463d9..5b36bffd 100644 --- a/packages/widget/src/hooks/use-max-min-yield-amount.ts +++ b/packages/widget/src/hooks/use-max-min-yield-amount.ts @@ -1,14 +1,14 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import type { Maybe } from "purify-ts"; import { useMemo } from "react"; import { getMaxAmount } from "../domain"; import type { PositionsData } from "../domain/types/positions"; import { getMinStakeAmount, getMinUnstakeAmount } from "../domain/types/stake"; +import { getYieldActionArg, type Yield } from "../domain/types/yields"; import { useForceMaxAmount } from "./use-force-max-amount"; type Args = { - yieldOpportunity: Maybe; + yieldOpportunity: Maybe; availableAmount: Maybe; } & ( | { type: "enter"; positionsData: PositionsData; pricePerShare?: never } @@ -35,7 +35,7 @@ export const useMaxMinYieldAmount = ({ .chainNullable((y) => type === "enter" ? getMinStakeAmount(y, positionsData) - : getMinUnstakeAmount(y, pricePerShare) + : getMinUnstakeAmount(y, pricePerShare), ) .map((a) => new BigNumber(a)), [ @@ -45,18 +45,14 @@ export const useMaxMinYieldAmount = ({ yieldOpportunity, positionsData, pricePerShare, - ] + ], ); const maxIntegrationAmount = useMemo(() => { return isForceMax ? availableAmount : yieldOpportunity - .chainNullable( - (y) => - (type === "enter" ? y.args.enter : y.args.exit)?.args?.amount - ?.maximum - ) + .chainNullable((y) => getYieldActionArg(y, type, "amount")?.maximum) .map((a) => new BigNumber(a)) .filter((v) => v.isGreaterThan(0)); }, [availableAmount, isForceMax, type, yieldOpportunity]); @@ -68,12 +64,12 @@ export const useMaxMinYieldAmount = ({ gasEstimateTotal: new BigNumber(0), integrationMaxLimit: maxIntegrationAmount, }), - [maxIntegrationAmount, availableAmount] + [maxIntegrationAmount, availableAmount], ); const minEnterOrExitAmount = useMemo( () => minIntegrationAmount.orDefault(new BigNumber(0)), - [minIntegrationAmount] + [minIntegrationAmount], ); return useMemo( @@ -92,6 +88,6 @@ export const useMaxMinYieldAmount = ({ minEnterOrExitAmount, maxIntegrationAmount, isForceMax, - ] + ], ); }; 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 cb7e7af4..4aa2e87c 100644 --- a/packages/widget/src/hooks/use-position-balance-by-type.ts +++ b/packages/widget/src/hooks/use-position-balance-by-type.ts @@ -18,9 +18,9 @@ export const usePositionBalanceByType = ({ positionBalancesData.map((val) => getPositionBalanceByTypeWithUsd({ pvd: val.balances, - }) + }), ), - [positionBalancesData] + [positionBalancesData], ); }; @@ -38,7 +38,7 @@ export const getPositionBalanceByTypeWithUsd = createSelector( if (amount.isZero() || amount.isNaN()) return acc; const tokenPriceInUsd = new BigNumber( - String(cur.amountUsd ?? 0).replace(/,/g, "") + String(cur.amountUsd ?? 0).replace(/,/g, ""), ); const prev = acc.get(cur.type); @@ -46,5 +46,5 @@ export const getPositionBalanceByTypeWithUsd = createSelector( acc.set(cur.type, [...(prev ?? []), { ...cur, tokenPriceInUsd }]); return acc; - }, new Map() as PositionBalancesByType) + }, new Map() as PositionBalancesByType), ); diff --git a/packages/widget/src/hooks/use-position-balances.ts b/packages/widget/src/hooks/use-position-balances.ts index 613e8641..f2619e1b 100644 --- a/packages/widget/src/hooks/use-position-balances.ts +++ b/packages/widget/src/hooks/use-position-balances.ts @@ -31,7 +31,7 @@ export const usePositionBalances = ({ rewardRate: val.positionData.rewardRate, }; }), - [balanceId, data] + [balanceId, data], ); return { data: value, ...rest }; diff --git a/packages/widget/src/hooks/use-position-data.ts b/packages/widget/src/hooks/use-position-data.ts index ae5c85fc..6d9e53b1 100644 --- a/packages/widget/src/hooks/use-position-data.ts +++ b/packages/widget/src/hooks/use-position-data.ts @@ -15,7 +15,7 @@ export const usePositionData = ({ id: Maybe.fromNullable(integrationId), data: Maybe.fromNullable(data), }).chainNullable((val) => val.data.get(val.id)), - [integrationId, data] + [integrationId, data], ); return { data: val, ...rest }; diff --git a/packages/widget/src/hooks/use-positions-data.ts b/packages/widget/src/hooks/use-positions-data.ts index f88c60e3..6989a80e 100644 --- a/packages/widget/src/hooks/use-positions-data.ts +++ b/packages/widget/src/hooks/use-positions-data.ts @@ -18,7 +18,7 @@ export const usePositionsData = () => { const val = useMemo>( () => data ?? new Map(), - [data] + [data], ); return { data: val, ...rest }; @@ -34,8 +34,8 @@ const positionsDataSelector = createSelector( balanceData: [...val.balances] .sort((a, b) => getPositionBalanceDataKey(a).localeCompare( - getPositionBalanceDataKey(b) - ) + getPositionBalanceDataKey(b), + ), ) .reduce((acc, b) => { const key = getPositionBalanceDataKey(b); @@ -70,7 +70,7 @@ const positionsDataSelector = createSelector( }); return acc; - }, new Map() as PositionsData) + }, new Map() as PositionsData), ); const getBalanceValidatorAddresses = (balance: YieldBalanceDto) => diff --git a/packages/widget/src/hooks/use-provider-details.ts b/packages/widget/src/hooks/use-provider-details.ts index e0150a09..0aaea3a6 100644 --- a/packages/widget/src/hooks/use-provider-details.ts +++ b/packages/widget/src/hooks/use-provider-details.ts @@ -1,9 +1,15 @@ -import type { RewardTypes, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import { List, Maybe } from "purify-ts"; import { useMemo } from "react"; +import type { RewardTypes } from "../domain/types/reward-rate"; +import type { ValidatorDto } from "../domain/types/validators"; import { + getYieldMetadata, + getYieldProviderDetails, getYieldProviderYieldIds, + getYieldRewardRate, + getYieldRewardType, isYieldWithProviderOptions, + type Yield, } from "../domain/types/yields"; import type { GetMaybeJust } from "../types/utils"; import { getRewardRateFormatted } from "../utils/formatters"; @@ -32,39 +38,42 @@ export const getProviderDetails = ({ selectedProviderYieldId, validatorsData, }: { - integrationData: Maybe; + integrationData: Maybe; validatorAddress: Maybe; - yields: Maybe; + yields: Maybe; selectedProviderYieldId: Maybe; validatorsData?: ValidatorDto[]; }): Res => { const def = integrationData.chain((val) => { - const rewardRate = val.rewardRate; + const rewardRate = getYieldRewardRate(val); + const rewardType = getYieldRewardType(val); + const metadata = getYieldMetadata(val); + const provider = getYieldProviderDetails(val); const rewardRateFormatted = getRewardRateFormatted({ rewardRate, - rewardType: val.rewardType, + rewardType, }); - return Maybe.fromNullable(val.metadata.provider) + return Maybe.fromNullable(provider) .map>((v) => ({ logo: v.logoURI, name: v.name, rewardRateFormatted, rewardRate, - rewardType: val.rewardType, + rewardType, website: v.externalLink, address: validatorAddress.extract(), })) .altLazy(() => Maybe.of({ - logo: val.metadata.logoURI, - name: val.metadata.name, + logo: metadata.logoURI, + name: metadata.name, rewardRateFormatted, rewardRate, - rewardType: val.rewardType, + rewardType, address: validatorAddress.extract(), - }) + }), ); }); @@ -73,22 +82,25 @@ export const getProviderDetails = ({ .chain>((addr) => List.find( (v) => v.address === addr || v.providerId === addr, - validatorsData ?? [] + validatorsData ?? [], ).map((validator) => { const { rewardRate, rewardType } = Maybe.fromRecord({ _: Maybe.fromFalsy(isYieldWithProviderOptions(yieldDto)), selectedProviderYieldId, }) .chain(({ selectedProviderYieldId }) => - yields.chain(List.find((v) => v.id === selectedProviderYieldId)) + yields.chain(List.find((v) => v.id === selectedProviderYieldId)), ) - .map((v) => v.rewardRate + v.rewardRate) + .map((v) => getYieldRewardRate(v) + getYieldRewardRate(v)) .map<{ rewardRate: number | undefined; rewardType: RewardTypes }>( - (res) => ({ rewardRate: res, rewardType: yieldDto.rewardType }) + (res) => ({ + rewardRate: res, + rewardType: getYieldRewardType(yieldDto), + }), ) .orDefault({ rewardRate: validator.apr, - rewardType: yieldDto.rewardType, + rewardType: getYieldRewardType(yieldDto), }); return { @@ -99,7 +111,7 @@ export const getProviderDetails = ({ rewardType, }), rewardRate, - rewardType: yieldDto.rewardType, + rewardType: getYieldRewardType(yieldDto), address: validator.address, stakedBalance: validator.stakedBalance, votingPower: validator.votingPower, @@ -108,9 +120,9 @@ export const getProviderDetails = ({ website: validator.website, preferred: validator.preferred, }; - }) + }), ) - .altLazy(() => def) + .altLazy(() => def), ); }; @@ -120,20 +132,20 @@ export const useProvidersDetails = ({ selectedProviderYieldId, validatorsData, }: { - integrationData: Maybe; + integrationData: Maybe; validatorsAddresses: Maybe>; selectedProviderYieldId: Maybe; validatorsData?: Maybe; }) => { const yields = useMultiYields( - integrationData.map(getYieldProviderYieldIds).orDefault([]) + integrationData.map(getYieldProviderYieldIds).orDefault([]), ); const shouldFetchValidators = validatorsAddresses .filter((val): val is string[] => !(val instanceof Map)) .map((val) => val.length > 0) .chain((val) => - validatorsData?.isJust() ? Maybe.of(false) : Maybe.of(val) + validatorsData?.isJust() ? Maybe.of(false) : Maybe.of(val), ) .orDefault(false); @@ -152,15 +164,15 @@ export const useProvidersDetails = ({ validatorsAddresses.chain((val) => val instanceof Map ? Maybe.of([...val.values()]) - : Maybe.fromNullable(yieldValidators.data) - ) + : Maybe.fromNullable(yieldValidators.data), + ), ) ?? validatorsAddresses.chain((val) => val instanceof Map ? Maybe.of([...val.values()]) - : Maybe.fromNullable(yieldValidators.data) + : Maybe.fromNullable(yieldValidators.data), ), - [validatorsAddresses, validatorsData, yieldValidators.data] + [validatorsAddresses, validatorsData, yieldValidators.data], ); return useMemo>[]>>( @@ -178,8 +190,8 @@ export const useProvidersDetails = ({ selectedProviderYieldId, validatorsData: resolvedValidatorsData.extractNullable() ?? undefined, - }) - ) + }), + ), ).chain((val) => val.length ? Maybe.of(val) @@ -188,8 +200,8 @@ export const useProvidersDetails = ({ validatorAddress: Maybe.empty(), yields: Maybe.fromNullable(yields.data), selectedProviderYieldId, - }).map((v) => [v]) - ) + }).map((v) => [v]), + ), ), [ integrationData, @@ -197,6 +209,6 @@ export const useProvidersDetails = ({ yields.data, selectedProviderYieldId, resolvedValidatorsData, - ] + ], ); }; diff --git a/packages/widget/src/hooks/use-region-code-names.ts b/packages/widget/src/hooks/use-region-code-names.ts index 2ed62088..970b85fc 100644 --- a/packages/widget/src/hooks/use-region-code-names.ts +++ b/packages/widget/src/hooks/use-region-code-names.ts @@ -11,8 +11,8 @@ export const useRegionCodeName = (regionCode: Nullable) => { ( await EitherAsync.liftEither( Maybe.fromNullable(regionCode).toEither( - new Error("missing regionCode") - ) + new Error("missing regionCode"), + ), ).chain((region) => EitherAsync(() => import("../utils/region-iso-3166-codes")) .mapLeft(() => new Error("Failed to load region-iso-3166-codes")) @@ -20,10 +20,10 @@ export const useRegionCodeName = (regionCode: Nullable) => { EitherAsync.liftEither( Maybe.fromNullable( val.countries[region as keyof typeof val.countries] - .subdivisionName - ).toEither(new Error("region not found")) - ) - ) + .subdivisionName, + ).toEither(new Error("region not found")), + ), + ), ) ).unsafeCoerce(), }); diff --git a/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx b/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx index 2b01f97f..29ae9c6e 100644 --- a/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx +++ b/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx @@ -1,7 +1,7 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import React from "react"; import { Box } from "../../components/atoms/box"; import { tokenString } from "../../domain"; +import type { TokenDto } from "../../domain/types/tokens"; import { symbolIcon } from "./style.css"; export const getRewardTokenSymbols = (rewardTokens: TokenDto[]) => @@ -31,7 +31,7 @@ export const getRewardTokenSymbols = (rewardTokens: TokenDto[]) => str: val.symbol, })} - ) + ), ); const maybeAddComma = ({ diff --git a/packages/widget/src/hooks/use-reward-token-details/index.ts b/packages/widget/src/hooks/use-reward-token-details/index.ts index 3d0769fc..0dfee4ab 100644 --- a/packages/widget/src/hooks/use-reward-token-details/index.ts +++ b/packages/widget/src/hooks/use-reward-token-details/index.ts @@ -1,18 +1,23 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { + getYieldProviderDetails, + getYieldRewardTokens, +} from "../../domain/types/yields"; import type { ExtraData } from "../../pages/details/earn-page/state/types"; import { getRewardTokenSymbols } from "./get-reward-token-symbols"; export const useRewardTokenDetails = ( - yieldOpportunity: ExtraData["selectedStake"] + yieldOpportunity: ExtraData["selectedStake"], ) => { return useMemo( () => yieldOpportunity .chain((y) => - Maybe.fromNullable(y.metadata.rewardTokens).chain((rt) => - Maybe.fromNullable(y.metadata.provider).map((p) => ({ rt, p })) - ) + Maybe.fromNullable(getYieldProviderDetails(y)).map((p) => ({ + p, + rt: getYieldRewardTokens(y), + })), ) .map(({ p, rt }) => ({ logoUri: p.logoURI ?? null, @@ -20,6 +25,6 @@ export const useRewardTokenDetails = ( symbols: getRewardTokenSymbols(rt), providerName: p.name ?? null, })), - [yieldOpportunity] + [yieldOpportunity], ); }; diff --git a/packages/widget/src/hooks/use-rewards-summary.ts b/packages/widget/src/hooks/use-rewards-summary.ts index e85447b5..6467126e 100644 --- a/packages/widget/src/hooks/use-rewards-summary.ts +++ b/packages/widget/src/hooks/use-rewards-summary.ts @@ -1,6 +1,5 @@ import { type AddressesDto, - type YieldDto, type YieldRewardsSummaryResponseDto, yieldGetSingleYieldRewardsSummary, } from "@stakekit/api-hooks"; @@ -10,16 +9,17 @@ import { type EnabledRewardsSummaryYieldId, isValidYieldIdForRewardsSummary, } from "../domain/types/rewards"; +import type { Yield } from "../domain/types/yields"; import { useSKQueryClient } from "../providers/query-client"; import { useSKWallet } from "../providers/sk-wallet"; export const useMultiRewardsSummary = ( - yieldIds: YieldDto["id"][], - opts?: { select?: (val: RewardsSummaryResult) => T } + yieldIds: Yield["id"][], + opts?: { select?: (val: RewardsSummaryResult) => T }, ) => { const filteredYieldIds = useMemo( () => yieldIds.filter(isValidYieldIdForRewardsSummary), - [yieldIds] + [yieldIds], ); const { address, additionalAddresses } = useSKWallet(); @@ -46,25 +46,24 @@ export const useMultiRewardsSummary = ( address: address!, additionalAddresses: additionalAddresses ?? undefined, }, - }).then((res) => ({ yieldId: id, data: res })) - ) + }).then((res) => ({ yieldId: id, data: res })), + ), ).then((res) => res.reduce( (acc, next) => { - acc[next.yieldId] = - next.data as unknown as YieldRewardsSummaryResponseDto; + acc[next.yieldId] = next.data; return acc; }, {} as Record< EnabledRewardsSummaryYieldId, YieldRewardsSummaryResponseDto - > - ) + >, + ), ), }); }; -export const useRewardsSummary = (yieldId: YieldDto["id"]) => { +export const useRewardsSummary = (yieldId: Yield["id"]) => { const { address, additionalAddresses } = useSKWallet(); const queryClient = useSKQueryClient(); diff --git a/packages/widget/src/hooks/use-rich-errors.ts b/packages/widget/src/hooks/use-rich-errors.ts index 71d8076e..9ab246e0 100644 --- a/packages/widget/src/hooks/use-rich-errors.ts +++ b/packages/widget/src/hooks/use-rich-errors.ts @@ -36,7 +36,7 @@ export const handleRichErrorResponse = ({ export const attachRichErrorsInterceptor = ( apiClient: AxiosInstance, - i18n: i18n + i18n: i18n, ) => apiClient.interceptors.response.use(undefined, (error) => { handleRichErrorResponse({ @@ -58,7 +58,7 @@ export const useRichErrors = () => { }; }, []), useCallback(() => $richError.value, []), - useCallback(() => $richError.value, []) + useCallback(() => $richError.value, []), ); const resetError = () => $richError.next(null); 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 544a81eb..c6286180 100644 --- a/packages/widget/src/hooks/use-staked-or-liquid-balance.ts +++ b/packages/widget/src/hooks/use-staked-or-liquid-balance.ts @@ -3,13 +3,13 @@ import { useMemo } from "react"; import type { PositionBalancesByType } from "../domain/types/positions"; export const useStakedOrLiquidBalance = ( - positionBalancesByType: Maybe + positionBalancesByType: Maybe, ) => { return useMemo( () => positionBalancesByType.chain((pbbt) => - Maybe.fromNullable(pbbt.get("active")) + Maybe.fromNullable(pbbt.get("active")), ), - [positionBalancesByType] + [positionBalancesByType], ); }; diff --git a/packages/widget/src/hooks/use-summary.tsx b/packages/widget/src/hooks/use-summary.tsx index 2b880a3c..f6393629 100644 --- a/packages/widget/src/hooks/use-summary.tsx +++ b/packages/widget/src/hooks/use-summary.tsx @@ -1,13 +1,14 @@ -import type { StakeKitErrorDto, YieldDto } from "@stakekit/api-hooks"; import type { UseQueryResult } from "@tanstack/react-query"; import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import { createContext, useCallback, useContext, useMemo } from "react"; import { config } from "../config"; import { getBaseToken, getTokenPriceInUSD } from "../domain"; +import type { StakeKitErrorDto } from "../domain/types/errors"; import { getPositionTotalAmount } from "../domain/types/positions"; import type { Prices } from "../domain/types/price"; import type { EnabledRewardsSummaryYieldId } from "../domain/types/rewards"; +import { getYieldRewardRate, type Yield } from "../domain/types/yields"; import { usePositions } from "../pages/details/positions-page/hooks/use-positions"; import { useMultiYields } from "./api/use-multi-yields"; import { usePrices } from "./api/use-prices"; @@ -65,13 +66,13 @@ export const SummaryProvider = ({ const yieldIds = useMemo( () => [...new Set(positionsData.data.map((p) => p.integrationId)).values()], - [positionsData.data] + [positionsData.data], ); const multiYieldsMapQuery = useMultiYields(yieldIds, { select: useCallback( - (val: YieldDto[]) => new Map(val.map((y) => [y.id, y])), - [] + (val: Yield[]) => new Map(val.map((y) => [y.id, y])), + [], ), }); @@ -87,7 +88,7 @@ export const SummaryProvider = ({ return acc; }, {} as RewardsSummaryResult), - [] + [], ), }); @@ -106,7 +107,7 @@ export const SummaryProvider = ({ const positionTotalAmount = getPositionTotalAmount( p.balancesWithAmount, - getBaseToken(yieldDto) + getBaseToken(yieldDto), ); const yields = [...multiYieldsMapQuery.data.values()]; @@ -130,7 +131,7 @@ export const SummaryProvider = ({ const allPositionsSum = allPositions.reduce( (acc, p) => acc.plus(p.usdAmount), - new BigNumber(0) + new BigNumber(0), ); return { @@ -148,7 +149,7 @@ export const SummaryProvider = ({ currency: config.currency, tokenList: useMemo( () => Object.values(rewardsSummaryQuery.data ?? {}).map((v) => v.token), - [rewardsSummaryQuery.data] + [rewardsSummaryQuery.data], ), }, { @@ -165,7 +166,7 @@ export const SummaryProvider = ({ } const rewardsPositions = Object.entries( - rewardsSummaryQuery.data + rewardsSummaryQuery.data, ).flatMap(([integrationId, rewardSummary]) => { const yieldDto = multiYieldsMapQuery.data.get(integrationId); @@ -199,17 +200,17 @@ export const SummaryProvider = ({ const rewardsPositionsTotalSum = rewardsPositions.reduce( (acc, p) => acc.plus(p.total), - new BigNumber(0) + new BigNumber(0), ); const rewardsPositionsLastMonthSum = rewardsPositions.reduce( (acc, p) => acc.plus(p.lastMonth), - new BigNumber(0) + new BigNumber(0), ); const rewardsPositionsLastWeekSum = rewardsPositions.reduce( (acc, p) => acc.plus(p.lastWeek), - new BigNumber(0) + new BigNumber(0), ); return { @@ -219,9 +220,9 @@ export const SummaryProvider = ({ rewardsPositionsLastWeekSum, }; }, - [multiYieldsMapQuery.data, rewardsSummaryQuery.data] + [multiYieldsMapQuery.data, rewardsSummaryQuery.data], ), - } + }, ); const averageApyQuery = useMemo(() => { @@ -240,15 +241,17 @@ export const SummaryProvider = ({ const positionTotalAmount = getPositionTotalAmount( p.balancesWithAmount, - getBaseToken(yieldDto) + getBaseToken(yieldDto), ); const usdAmount = positionTotalAmount.amountUsd; - if (yieldDto.rewardRate > 0 && usdAmount.gt(0)) { + const rewardRate = getYieldRewardRate(yieldDto); + + if (rewardRate > 0 && usdAmount.gt(0)) { return { totalWeightedApy: acc.totalWeightedApy.plus( - usdAmount.times(yieldDto.rewardRate * 100) + usdAmount.times(rewardRate * 100), ), totalValue: acc.totalValue.plus(usdAmount), }; @@ -259,7 +262,7 @@ export const SummaryProvider = ({ { totalWeightedApy: new BigNumber(0), totalValue: new BigNumber(0), - } + }, ); const data = totalValue.gt(0) @@ -277,7 +280,7 @@ export const SummaryProvider = ({ const tokenList = useMemo( () => tokenBalancesScan.data?.map((tb) => tb.token), - [tokenBalancesScan.data] + [tokenBalancesScan.data], ); const availableBalanceSumQuery = usePrices( @@ -302,14 +305,14 @@ export const SummaryProvider = ({ baseToken: tb.token, token: tb.token, prices, - }) + }), ), - BigNumber(0) + BigNumber(0), ); }, - [tokenBalancesScan.data] + [tokenBalancesScan.data], ), - } + }, ); const value = useMemo( @@ -324,7 +327,7 @@ export const SummaryProvider = ({ rewardsPositionsQuery, averageApyQuery, availableBalanceSumQuery, - ] + ], ); return ( diff --git a/packages/widget/src/hooks/use-sync-element-height.ts b/packages/widget/src/hooks/use-sync-element-height.ts index 8cf8748d..a0ee47aa 100644 --- a/packages/widget/src/hooks/use-sync-element-height.ts +++ b/packages/widget/src/hooks/use-sync-element-height.ts @@ -2,7 +2,7 @@ import { useEffect, useRef } from "react"; import { useSavedRef } from "./use-saved-ref"; export const useSyncElementHeight = ( - setCurrentHeight: (height: number) => void + setCurrentHeight: (height: number) => void, ) => { const containerRef = useRef(null); const setCurrentHeightRef = useSavedRef(setCurrentHeight); diff --git a/packages/widget/src/hooks/use-under-maintenance.ts b/packages/widget/src/hooks/use-under-maintenance.ts index d2c04b70..b262bd89 100644 --- a/packages/widget/src/hooks/use-under-maintenance.ts +++ b/packages/widget/src/hooks/use-under-maintenance.ts @@ -34,7 +34,7 @@ export const useUnderMaintenance = () => { class YieldApiResponseError extends Error { constructor( readonly status: number, - override readonly cause?: unknown + override readonly cause?: unknown, ) { super("Yield API health request failed"); } diff --git a/packages/widget/src/hooks/use-validators-config.ts b/packages/widget/src/hooks/use-validators-config.ts index 5323a1dd..b31f8575 100644 --- a/packages/widget/src/hooks/use-validators-config.ts +++ b/packages/widget/src/hooks/use-validators-config.ts @@ -18,8 +18,8 @@ export const useValidatorsConfig = (): ValidatorsConfig => { mergePreferredWithDefault: val.mergePreferredWithDefault ?? true, preferredOnly: val.preferredOnly ?? false, }, - ]) + ]), ) satisfies ValidatorsConfig, - [validatorsConfig] + [validatorsConfig], ); }; diff --git a/packages/widget/src/hooks/use-yield-meta-info.tsx b/packages/widget/src/hooks/use-yield-meta-info.tsx index f714e86d..f4cac646 100644 --- a/packages/widget/src/hooks/use-yield-meta-info.tsx +++ b/packages/widget/src/hooks/use-yield-meta-info.tsx @@ -1,11 +1,17 @@ -import type { TokenDto, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import { MiscNetworks } from "@stakekit/common"; import { List, Maybe } from "purify-ts"; 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 type { TokenDto, YieldTokenDto } from "../domain/types/tokens"; +import type { ValidatorDto } from "../domain/types/validators"; +import { + getYieldMetadata, + getYieldRewardType, + hasYieldFeeConfigurationEnabled, + isEthenaUsdeStaking, + type Yield, +} from "../domain/types/yields"; import { capitalizeFirstLowerRest } from "../utils/text"; export const useYieldMetaInfo = ({ @@ -13,7 +19,7 @@ export const useYieldMetaInfo = ({ validators, tokenDto, }: { - selectedStake: Maybe; + selectedStake: Maybe; validators: { [Key in keyof Pick]?: ValidatorDto[Key]; }[]; @@ -29,9 +35,9 @@ export const useYieldMetaInfo = ({ t("details.selected_validators", { providerName: v.name ?? v.address, count: validators.length - 1, - }) + }), ), - [validators, t] + [validators, t], ); return useMemo(() => { @@ -39,27 +45,27 @@ export const useYieldMetaInfo = ({ typeof ifNotFound >(({ selectedStake: y, tokenDto }) => { const sv = validatorsFormatted.extract(); + const metadata = getYieldMetadata(y); - const haveFeeConfigurationEnabled = y.feeConfigurations.length > 0; + const haveFeeConfigurationEnabled = hasYieldFeeConfigurationEnabled(y); const stakeToken = tokenDto.symbol; const rewardTokens = - y.metadata.rewardTokens + metadata.rewardTokens ?.filter((t) => !t.isPoints) .map((t) => t.symbol) .join(", ") ?? ""; const providerName = - sv ?? - (y.metadata.provider ? y.metadata.provider.name : y.metadata.name); - const rewardSchedule = y.metadata.rewardSchedule; - const cooldownPeriodDays = y.metadata.cooldownPeriod?.days ?? 0; - const warmupPeriodDays = y.metadata.warmupPeriod?.days ?? 0; - const rewardClaiming = y.metadata.rewardClaiming; + sv ?? (metadata.provider ? metadata.provider.name : metadata.name); + const rewardSchedule = metadata.rewardSchedule; + const cooldownPeriodDays = metadata.cooldownPeriod?.days ?? 0; + const warmupPeriodDays = metadata.warmupPeriod?.days ?? 0; + const rewardClaiming = metadata.rewardClaiming; const isCompound = providerName.includes("Compound"); if ( - y.metadata.rewardSchedule === "campaign" && + metadata.rewardSchedule === "campaign" && stakeToken.toUpperCase() === "SUSD" ) { return { @@ -99,7 +105,7 @@ export const useYieldMetaInfo = ({ const def = { campaign: - y.metadata.rewardSchedule === "campaign" ? ( + metadata.rewardSchedule === "campaign" ? ( ) : null, - lockupPeriod: y.metadata.lockupPeriod?.days + lockupPeriod: metadata.lockupPeriod?.days ? t("details.lockup_period", { - count: y.metadata.lockupPeriod?.days, + count: metadata.lockupPeriod?.days, }) : null, extra: - y.rewardType === "variable" + getYieldRewardType(y) === "variable" ? t("details.reward_type_varialbe", { symbol: capitalizeFirstLowerRest(y.token.symbol), }) - : y.metadata.token.network === MiscNetworks.Tezos + : metadata.token.network === MiscNetworks.Tezos ? t("details.extra_tezos") : undefined, }; - switch (y.metadata.type) { + switch (metadata.type) { case "staking": { return { description: null, @@ -243,12 +249,12 @@ export const useYieldMetaInfo = ({ }), withdrawnTime: y.status.exit ? cooldownPeriodDays > 0 - ? (y.metadata.withdrawPeriod?.days ?? 0) > 0 + ? (metadata.withdrawPeriod?.days ?? 0) > 0 ? t("details.liquid_stake.unstake_time_days_with_claim", { unstakeTime: t("details.liquid_stake.unstake_time", { count: cooldownPeriodDays, }), - count: y.metadata.withdrawPeriod?.days, + count: metadata.withdrawPeriod?.days, }) : t("details.liquid_stake.unstake_time", { count: cooldownPeriodDays, diff --git a/packages/widget/src/hooks/use-yield-type.ts b/packages/widget/src/hooks/use-yield-type.ts index 61504681..58e0c1b3 100644 --- a/packages/widget/src/hooks/use-yield-type.ts +++ b/packages/widget/src/hooks/use-yield-type.ts @@ -1,9 +1,8 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; -import { getYieldTypeLabels } from "../domain/types/yields"; +import { getYieldTypeLabels, type Yield } from "../domain/types/yields"; -export const useYieldType = (yieldOpportunity: Maybe) => { +export const useYieldType = (yieldOpportunity: Maybe) => { const { t } = useTranslation(); return yieldOpportunity.chainNullable((s) => getYieldTypeLabels(s, t)); diff --git a/packages/widget/src/main.tsx b/packages/widget/src/main.tsx index fb00655d..c6caa568 100644 --- a/packages/widget/src/main.tsx +++ b/packages/widget/src/main.tsx @@ -69,7 +69,7 @@ const enableMocking = async () => { enableMocking() .then(() => { ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + , ); }) .catch((e) => { diff --git a/packages/widget/src/navigation/containers/animation-layout.tsx b/packages/widget/src/navigation/containers/animation-layout.tsx index ef27efcc..c8993abf 100644 --- a/packages/widget/src/navigation/containers/animation-layout.tsx +++ b/packages/widget/src/navigation/containers/animation-layout.tsx @@ -49,7 +49,7 @@ export const AnimationLayout = ({ children }: PropsWithChildren) => { } return { duration: 0.6, delay: 0.3 }; }) - .map((transition) => ({ height, transition })) + .map((transition) => ({ height, transition })), ) .unsafeCoerce(); diff --git a/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx index f1bf0061..bde6629c 100644 --- a/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx +++ b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx @@ -1,4 +1,3 @@ -import type { ActionDto, YieldDto } from "@stakekit/api-hooks"; import clsx from "clsx"; import { List } from "purify-ts"; import { useTranslation } from "react-i18next"; @@ -7,7 +6,9 @@ import { ContentLoaderSquare } from "../../../components/atoms/content-loader"; import { ListItem } from "../../../components/atoms/list/list-item"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Text } from "../../../components/atoms/typography/text"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useActionListItem } from "../../../pages/details/activity-page/hooks/use-action-list-item"; +import type { ActionYieldDto } from "../../../pages/details/activity-page/types"; import { badgeText, listItemContainer, @@ -22,11 +23,6 @@ import { viaText, } from "./style.css"; -type ActionYieldDto = { - actionData: ActionDto; - yieldData: YieldDto; -}; - export const ActionListItem = ({ action, onActionSelect, @@ -68,7 +64,7 @@ export const ActionListItem = ({ justifyContent="flex-start" alignItems="center" > - + - )) + )), ) .extractNullable()} @@ -129,7 +125,7 @@ export const ActionListItem = ({ ), - + , )} ); @@ -175,7 +171,7 @@ const Badge = ({ noWrap, badgeText({ type: badgeColor ? "white" : "regular", - }) + }), )} > {badgeLabel} diff --git a/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx b/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx index 0dedaa76..915a411c 100644 --- a/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx +++ b/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx @@ -1,10 +1,6 @@ -import { - ActionStatus, - ActionTypes, - type TransactionType, -} from "@stakekit/api-hooks"; import { useSelector } from "@xstate/store/react"; import { Box } from "../../components/atoms/box"; +import { ActionStatus, type TransactionType } from "../../domain/types/action"; import { useActivityComplete } from "../../pages/complete/hooks/use-activity-complete.hook"; import { useComplete } from "../../pages/complete/hooks/use-complete.hook"; import { CompletePageComponent } from "../../pages/complete/pages/common.page"; @@ -17,12 +13,12 @@ export const ActivityDetailsPage = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction + (state) => state.context.selectedAction, ).extractNullable(); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield + (state) => state.context.selectedYield, ).extractNullable(); if (!selectedYield || !selectedAction) { @@ -77,10 +73,9 @@ const ActivityCompletePage = () => { value={{ urls, onViewTransactionClick, - unstakeMatch: selectedAction.type === ActionTypes.UNSTAKE, + unstakeMatch: selectedAction.type === "UNSTAKE", pendingActionMatch: - selectedAction.type !== ActionTypes.STAKE && - selectedAction.type !== ActionTypes.UNSTAKE, + selectedAction.type !== "STAKE" && selectedAction.type !== "UNSTAKE", }} > { network={network} amount={amount} pendingActionType={selectedAction.type} - integrationId={selectedAction.integrationId} + integrationId={selectedAction.yieldId} /> ); diff --git a/packages/widget/src/pages-dashboard/activity/activity.page.tsx b/packages/widget/src/pages-dashboard/activity/activity.page.tsx index 7c30bf8a..01ae4075 100644 --- a/packages/widget/src/pages-dashboard/activity/activity.page.tsx +++ b/packages/widget/src/pages-dashboard/activity/activity.page.tsx @@ -1,4 +1,3 @@ -import { ActionStatus } from "@stakekit/api-hooks"; import { useConnectModal } from "@stakekit/rainbowkit"; import { useSelector } from "@xstate/store/react"; import { List, Maybe } from "purify-ts"; @@ -7,6 +6,7 @@ import { useTranslation } from "react-i18next"; import { Box } from "../../components/atoms/box"; import { Text } from "../../components/atoms/typography/text"; import { GroupedVirtualList } from "../../components/atoms/virtual-list"; +import { ActionStatus } from "../../domain/types/action"; import ListItemBullet from "../../pages/details/activity-page/components/list-item-bullet"; import { useActivityPage } from "../../pages/details/activity-page/hooks/use-activity-page"; import { @@ -138,7 +138,7 @@ const _ActivityPage = () => { const selectedAction = useSelector( activityStore, - (state) => state.context.selectedAction + (state) => state.context.selectedAction, ); // biome-ignore lint: false @@ -171,7 +171,7 @@ const _ActivityPage = () => { selectedAction: val.actionData, selectedYield: val.yieldData, }), - }) + }), ); }, [selectedAction, activityStore, value.activityActions.allItems]); diff --git a/packages/widget/src/pages-dashboard/activity/position-balances.tsx b/packages/widget/src/pages-dashboard/activity/position-balances.tsx index 8678ed06..8a540890 100644 --- a/packages/widget/src/pages-dashboard/activity/position-balances.tsx +++ b/packages/widget/src/pages-dashboard/activity/position-balances.tsx @@ -1,10 +1,10 @@ -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 type { YieldBalanceDto } from "../../domain/types/positions"; +import { getYieldMetadata, type Yield } from "../../domain/types/yields"; import { defaultFormattedNumber } from "../../utils"; export const PositionBalances = ({ @@ -12,11 +12,11 @@ export const PositionBalances = ({ integrationData, }: { yieldBalance: YieldBalanceDto & { tokenPriceInUsd: BigNumber }; - integrationData: YieldDto; + integrationData: Yield; }) => { const { t } = useTranslation(); - const yieldType = integrationData.metadata.type; + const yieldType = getYieldMetadata(integrationData).type; const balanceTypeContext = yieldType === "vault" || yieldType === "lending" diff --git a/packages/widget/src/pages-dashboard/common/components/header.tsx b/packages/widget/src/pages-dashboard/common/components/header.tsx index 0b16ffcd..d8c6ced5 100644 --- a/packages/widget/src/pages-dashboard/common/components/header.tsx +++ b/packages/widget/src/pages-dashboard/common/components/header.tsx @@ -44,7 +44,7 @@ export const Header = () => { gap="2" > {Maybe.fromFalsy( - (isConnected || isConnecting) && chain && account + (isConnected || isConnecting) && chain && account, ) .map(() => ( <> diff --git a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx index 4e34baae..94101b52 100644 --- a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx +++ b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx @@ -2,6 +2,7 @@ import { Maybe } from "purify-ts"; import { Box } from "../../../components/atoms/box"; import { ContentLoaderSquare } from "../../../components/atoms/content-loader"; import { SelectValidator } from "../../../components/molecules/select-validator"; +import { isYieldActionArgRequired } from "../../../domain/types/yields"; import { useSelectValidator } from "../../../pages/details/earn-page/components/select-validator-section/use-select-validator"; import { SelectValidatorTrigger } from "./utila-select-validator-trigger"; @@ -29,8 +30,11 @@ export const UtilaSelectValidatorSection = () => { .map((val) => { const selectedValidatorsArr = [...selectedValidators.values()]; - const multiSelect = - !!val.selectedStake.args.enter.args?.validatorAddresses?.required; + const multiSelect = isYieldActionArgRequired( + val.selectedStake, + "enter", + "validatorAddresses", + ); return ( {item.token.mapOrDefault( (val) => ( - + ), - + , )} - {d.metadata.name} + {getYieldMetadata(d).name} {item.yieldLabelDto .map((label) => { @@ -82,7 +83,7 @@ export const PositionsListItem = memo( `position_details.labels.${label.type as PositionDetailsLabelType}.details`, label.params as | Record - | undefined + | undefined, )} > {t( - `position_details.labels.${label.type as PositionDetailsLabelType}.label` + `position_details.labels.${label.type as PositionDetailsLabelType}.label`, )} @@ -127,7 +128,7 @@ export const PositionsListItem = memo( ? inactiveValidator === "jailed" ? "details.validators_jailed" : "details.validators_inactive" - : "positions.claim_rewards" + : "positions.claim_rewards", )} @@ -148,7 +149,7 @@ export const PositionsListItem = memo( count: Math.max(val.length - 1, 1), })} - )) + )), ) .extractNullable()} @@ -185,7 +186,7 @@ export const PositionsListItem = memo( )) .orDefault( - - + -, )} @@ -223,10 +224,10 @@ export const PositionsListItem = memo( )} ), - + , )} ); - } + }, ); 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 f58a28b5..cc07b1a5 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 @@ -10,7 +10,7 @@ import type { usePositions } from "../../../../pages/details/positions-page/hook import { defaultFormattedNumber } from "../../../../utils"; export const usePositionListItem = ( - item: ReturnType["positionsData"]["data"][number] + item: ReturnType["positionsData"]["data"][number], ) => { const { integrationData, @@ -27,9 +27,9 @@ export const usePositionListItem = ( const rewardsSummary = useMemo( () => Maybe.fromNullable(rewardsSummaryQuery.data?.data).filter((val) => - BigNumber(val.rewards.total).gt(0) + BigNumber(val.rewards.total).gt(0), ), - [rewardsSummaryQuery.data] + [rewardsSummaryQuery.data], ); const prices = usePrices({ @@ -45,7 +45,7 @@ export const usePositionListItem = ( rewardsSummary .map((val) => BigNumber(val.rewards.total)) .map(defaultFormattedNumber), - [rewardsSummary] + [rewardsSummary], ); const totalAmountPriceFormatted = useMemo( @@ -53,7 +53,7 @@ export const usePositionListItem = ( totalAmountUsd .filter((v) => v.isGreaterThan(0)) .map(defaultFormattedNumber), - [totalAmountUsd] + [totalAmountUsd], ); const rewardsAmountPriceFormatted = useMemo( @@ -70,10 +70,10 @@ export const usePositionListItem = ( pricePerShare: null, token: val.rewardsSummary.token, prices: val.prices, - }) + }), ) .map(defaultFormattedNumber), - [rewardsSummary, baseToken, prices] + [rewardsSummary, baseToken, prices], ); return { diff --git a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx index 7cacbed5..3ef3d56d 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx @@ -4,6 +4,7 @@ import { Box } from "../../../components/atoms/box"; import { Button } from "../../../components/atoms/button"; import { Spinner } from "../../../components/atoms/spinner"; import { SelectValidator } from "../../../components/molecules/select-validator"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { AmountBlock } from "../../../pages/position-details/components/amount-block"; import { StaticActionBlock } from "../../../pages/position-details/components/static-action-block"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; @@ -11,7 +12,7 @@ import type { YieldPendingActionType } from "../../../providers/yield-api-client import { container } from "./styles.css"; export const positionDetailsActionsHasContent = ( - val: ReturnType + val: ReturnType, ) => Maybe.fromRecord({ integrationData: val.integrationData, @@ -26,7 +27,7 @@ export const positionDetailsActionsHasContent = ( canChangeUnstakeAmount: val.canChangeUnstakeAmount, unstakeToken: val.unstakeToken, }).map(() => true), - ]).length > 0 + ]).length > 0, ) .isJust(); @@ -104,7 +105,7 @@ export const PositionDetailsActions = () => { label={t( `position_details.pending_action_button.${ val.pendingActionDto.type.toLowerCase() as Lowercase - }` + }`, )} onMaxClick={null} formattedAmount={val.formattedAmount} @@ -117,8 +118,8 @@ export const PositionDetailsActions = () => { onPendingActionClick={onPendingActionClick} yieldId={v.integrationData.id} /> - ) - ) + ), + ), ) .extractNullable()} @@ -151,14 +152,14 @@ export const PositionDetailsActions = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${v.integrationData.metadata.type}` + `position_details.unstake_label.${getYieldMetadata(v.integrationData).type}`, )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} yieldDto={v.integrationData} validators={providersDetails.orDefault([])} /> - ) + ), ) .extractNullable()} 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 005cfef1..6d0138af 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 @@ -9,6 +9,7 @@ import { } from "../../../components/atoms/collapsible"; import { Spinner } from "../../../components/atoms/spinner"; import { Text } from "../../../components/atoms/typography/text"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { PositionBalances } from "../../../pages/position-details/components/position-balances"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; import { ProviderDetails } from "./provider-details"; @@ -57,11 +58,11 @@ export const PositionDetailsInfo = () => { {...p} key={p.address ?? idx} stakeType={t( - `position_details.stake_type.${val.integrationData.metadata.type}` + `position_details.stake_type.${getYieldMetadata(val.integrationData).type}`, )} integrationData={val.integrationData} /> - )) + )), ) .extractNullable()} @@ -96,7 +97,7 @@ export const PositionDetailsInfo = () => { integrationData={val.integrationData} yieldBalance={yb} /> - )) + )), )} diff --git a/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx b/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx index bbfd80c7..d8c87427 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx @@ -1,4 +1,3 @@ -import type { RewardTypes, YieldDto } from "@stakekit/api-hooks"; import { memo } from "react"; import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; @@ -14,6 +13,8 @@ import { Image } from "../../../components/atoms/image"; import { ImageFallback } from "../../../components/atoms/image-fallback"; import { Text } from "../../../components/atoms/typography/text"; import { useMetaInfo } from "../../../components/molecules/select-validator/meta-info"; +import type { RewardTypes } from "../../../domain/types/reward-rate"; +import type { Yield } from "../../../domain/types/yields"; import type { useProvidersDetails } from "../../../hooks/use-provider-details"; import type { GetMaybeJust } from "../../../types/utils"; import { inactiveContainer, noWrap } from "./styles.css"; @@ -25,7 +26,7 @@ export const ProviderDetails = ({ ...providerDetails }: { stakeType: string; - integrationData: YieldDto; + integrationData: Yield; logo: string | undefined; name: string; rewardRateFormatted: string; @@ -89,7 +90,7 @@ export const ProviderDetails = ({ {t( providerDetails.status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive" + : "details.validators_inactive", )} @@ -128,7 +129,7 @@ const ValidatorMeta = memo((props: Parameters[0]) => { {Object.entries(metaInfo) .filter( (val): val is [keyof typeof metaInfo, NonNullable<(typeof val)[1]>] => - !!val[1] + !!val[1], ) .map(([key, val]) => { return ( diff --git a/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx b/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx index 3c597dda..3c9b3e94 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx @@ -2,6 +2,7 @@ import { Just, Maybe } from "purify-ts"; import { Box } from "../../../components/atoms/box"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Text } from "../../../components/atoms/typography/text"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; import { topHeaderYieldName } from "./styles.css"; @@ -18,7 +19,7 @@ export const TopHeader = () => { <> { variant={{ weight: "normal" }} textAlign="center" > - {val.integrationData.metadata.name} + {getYieldMetadata(val.integrationData).name} {t.symbol} diff --git a/packages/widget/src/pages-dashboard/position-details/index.tsx b/packages/widget/src/pages-dashboard/position-details/index.tsx index 87d7c2c9..470d0b9f 100644 --- a/packages/widget/src/pages-dashboard/position-details/index.tsx +++ b/packages/widget/src/pages-dashboard/position-details/index.tsx @@ -17,7 +17,7 @@ import { headerContainer, posistionDetailsInfoContainer } from "./styles.css"; const PositionDetailsPageComponent = () => { const shouldShowActions = positionDetailsActionsHasContent( - usePositionDetails() + usePositionDetails(), ); const location = useSKLocation(); diff --git a/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx b/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx index 07fbcd7c..a46e1375 100644 --- a/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx +++ b/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx @@ -10,6 +10,7 @@ import { TokenIcon } from "../../../components/atoms/token-icon"; import { ToolTip } from "../../../components/atoms/tooltip"; import { Text } from "../../../components/atoms/typography/text"; import type { PositionDetailsLabelType } from "../../../domain/types/positions"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { columnContainer, listItem, @@ -60,11 +61,11 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), - + , )} @@ -83,7 +84,7 @@ export const PositionsListItem = memo( `position_details.labels.${label.type as PositionDetailsLabelType}.details`, label.params as | Record - | undefined + | undefined, )} > {t( - `position_details.labels.${label.type as PositionDetailsLabelType}.label` + `position_details.labels.${label.type as PositionDetailsLabelType}.label`, )} @@ -128,7 +129,7 @@ export const PositionsListItem = memo( ? inactiveValidator === "jailed" ? "details.validators_jailed" : "details.validators_inactive" - : "positions.claim_rewards" + : "positions.claim_rewards", )} @@ -149,7 +150,7 @@ export const PositionsListItem = memo( count: Math.max(val.length - 1, 1), })} - )) + )), ) .extractNullable()} @@ -178,13 +179,13 @@ export const PositionsListItem = memo( {rewardsAmountPriceFormatted .map((v) => <>{v}$) .orDefault( - - + -, )} )) .orDefault( - - + -, )} @@ -222,10 +223,10 @@ export const PositionsListItem = memo( )} ), - + , )} ); - } + }, ); diff --git a/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts b/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts index 529d2a95..7e80d02c 100644 --- a/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts +++ b/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts @@ -1,7 +1,12 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import { useSelector } from "@xstate/store/react"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { + getActionInputToken, + getActionValidatorAddresses, +} from "../../../domain/types/action"; +import type { TokenDto } from "../../../domain/types/tokens"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; import { useYieldType } from "../../../hooks/use-yield-type"; @@ -15,7 +20,7 @@ export const useActivityComplete = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction + (state) => state.context.selectedAction, ).unsafeCoerce(); const amount = useMemo( @@ -23,32 +28,40 @@ export const useActivityComplete = () => { Maybe.fromNullable(selectedAction.amount) .map(defaultFormattedNumber) .unsafeCoerce(), - [selectedAction] + [selectedAction], ); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield + (state) => state.context.selectedYield, ); const yieldType = useYieldType(selectedYield).map((v) => v.type); const inputToken = useMemo( - () => Maybe.fromNullable(selectedAction).map((y) => y.inputToken), - [selectedAction] + () => + Maybe.fromNullable( + getActionInputToken({ + actionDto: selectedAction, + yieldDto: selectedYield.extractNullable() ?? undefined, + }), + ), + [selectedAction, selectedYield], ) as Maybe; const metadata = useMemo( - () => selectedYield.map((y) => y.metadata), - [selectedYield] + () => selectedYield.map(getYieldMetadata), + [selectedYield], ); const network = inputToken.mapOrDefault((y) => y.symbol, ""); const providerDetails = useProvidersDetails({ integrationData: selectedYield, - validatorsAddresses: Maybe.of(selectedAction.validatorAddresses ?? []), - selectedProviderYieldId: Maybe.of(selectedAction.integrationId), + validatorsAddresses: Maybe.of( + getActionValidatorAddresses(selectedAction) ?? [], + ), + selectedProviderYieldId: Maybe.of(selectedAction.yieldId), }); return { diff --git a/packages/widget/src/pages/complete/hooks/use-complete.hook.ts b/packages/widget/src/pages/complete/hooks/use-complete.hook.ts index 044e4584..c423b800 100644 --- a/packages/widget/src/pages/complete/hooks/use-complete.hook.ts +++ b/packages/widget/src/pages/complete/hooks/use-complete.hook.ts @@ -1,7 +1,7 @@ -import type { TransactionType } from "@stakekit/api-hooks"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useLocation, useNavigate } from "react-router"; +import type { TransactionType } from "../../../domain/types/action"; import { useActivityPendingActionMatch } from "../../../hooks/navigation/use-activity-pending-action-match"; import { useActivityReviewMatch } from "../../../hooks/navigation/use-activity-review.match"; import { useActivityUnstakeActionMatch } from "../../../hooks/navigation/use-activity-unstake.match"; @@ -66,8 +66,8 @@ export const useComplete = () => { onClick: () => onClickRef.current(), hide: !!activityReviewMatch, }), - [onClickRef, t, activityReviewMatch, isLedgerLive] - ) + [onClickRef, t, activityReviewMatch, isLedgerLive], + ), ); return { diff --git a/packages/widget/src/pages/complete/pages/activity-complete.page.tsx b/packages/widget/src/pages/complete/pages/activity-complete.page.tsx index c09d5c2c..fefe0a06 100644 --- a/packages/widget/src/pages/complete/pages/activity-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/activity-complete.page.tsx @@ -21,7 +21,7 @@ export const ActivityCompletePage = () => { network={network} amount={amount} pendingActionType={selectedAction.type} - integrationId={selectedAction.integrationId} + integrationId={selectedAction.yieldId} /> ); }; diff --git a/packages/widget/src/pages/complete/pages/common.page.tsx b/packages/widget/src/pages/complete/pages/common.page.tsx index ed0d1bf8..9f604a33 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -1,4 +1,3 @@ -import type { TokenDto, YieldDto, YieldMetadataDto } from "@stakekit/api-hooks"; import { motion } from "motion/react"; import { Just, Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; @@ -9,15 +8,14 @@ import { ImageFallback } from "../../../components/atoms/image-fallback"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Heading } from "../../../components/atoms/typography/heading"; import { Text } from "../../../components/atoms/typography/text"; +import type { YieldPendingActionType } from "../../../domain/types/pending-action"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import { type ExtendedYieldType, isEthenaUsdeStaking, + type YieldMetadata, } from "../../../domain/types/yields"; import { AnimationPage } from "../../../navigation/containers/animation-page"; -import type { - YieldPendingActionType, - 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 +26,7 @@ import { type Props = { token: Maybe; - metadata: Maybe; + metadata: Maybe; network: string; amount: string; pendingActionType?: YieldPendingActionType; @@ -39,7 +37,7 @@ type Props = { }[] >; yieldType: Maybe; - integrationId: YieldDto["id"]; + integrationId: string; }; export const CompletePageComponent = ({ @@ -126,7 +124,7 @@ export const CompletePageComponent = ({ ? "ethena_usde" : undefined, }), - "" + "", ), amount, tokenNetwork: network, @@ -138,9 +136,9 @@ export const CompletePageComponent = ({ context: isEthenaUsdeStaking(integrationId) ? "ethena_usde" : undefined, - } + }, ), - } + }, )} @@ -174,7 +172,7 @@ export const CompletePageComponent = ({ {t("complete.via", { providerName: v.name })} - )) + )), ) .extractNullable() : null} @@ -200,12 +198,16 @@ export const CompletePageComponent = ({ {t("complete.view_transaction", { type: Just(val.type) - .map((v) => - t(`steps.tx_type.${v}`, { - context: isEthenaUsdeStaking(integrationId) - ? "ETHENA_USDE" - : undefined, - }) + .map( + (v) => + t( + `steps.tx_type.${v}` as never, + { + context: isEthenaUsdeStaking(integrationId) + ? "ETHENA_USDE" + : undefined, + } as never, + ) as unknown as string, ) .map(capitalizeFirstLowerRest) .extract(), diff --git a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx index fa1c17b2..fdb344cc 100644 --- a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx @@ -2,6 +2,7 @@ import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useUnstakeOrPendingActionParams } from "../../../hooks/navigation/use-unstake-or-pending-action-params"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { usePositionBalances } from "../../../hooks/use-position-balances"; @@ -21,17 +22,17 @@ export const PendingCompletePage = () => { const pendingRequest = useSelector( usePendingActionStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const integrationData = useMemo( () => Maybe.of(pendingRequest.integrationData), - [pendingRequest.integrationData] + [pendingRequest.integrationData], ); const token = useMemo( () => Maybe.of(pendingRequest.interactedToken), - [pendingRequest.interactedToken] + [pendingRequest.interactedToken], ); useTrackPage("pendingActionCompelete"); @@ -39,19 +40,19 @@ export const PendingCompletePage = () => { const providerDetails = useProvidersDetails({ integrationData, validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [] + p.type === "validators" ? p.validatorsAddresses : [], ), selectedProviderYieldId: Maybe.empty(), }); - const metadata = integrationData.map((d) => d.metadata); + const metadata = integrationData.map(getYieldMetadata); const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( () => Maybe.fromNullable(pendingRequest.requestDto.arguments?.amount) .map((val) => new BigNumber(val ?? 0)) .mapOrDefault((v) => formatNumber(v), ""), - [pendingRequest.requestDto.arguments?.amount] + [pendingRequest.requestDto.arguments?.amount], ); const yieldType = useYieldType(integrationData).map((v) => v.type); diff --git a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx index efd8003c..51330d54 100644 --- a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx @@ -2,6 +2,7 @@ import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; import { useYieldType } from "../../../hooks/use-yield-type"; @@ -14,36 +15,36 @@ export const StakeCompletePage = () => { const enterRequest = useSelector( useEnterStakeStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const selectedStake = useMemo( () => Maybe.of(enterRequest.selectedStake), - [enterRequest.selectedStake] + [enterRequest.selectedStake], ); const selectedToken = useMemo( () => Maybe.of(enterRequest.selectedToken), - [enterRequest.selectedToken] + [enterRequest.selectedToken], ); - const metadata = selectedStake.map((y) => y.metadata); + const metadata = selectedStake.map(getYieldMetadata); const network = selectedToken.mapOrDefault((y) => y.symbol, ""); const amount = useMemo( () => formatNumber( - new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0) + new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0), ), - [enterRequest.requestDto.arguments?.amount] + [enterRequest.requestDto.arguments?.amount], ); const yieldType = useYieldType(selectedStake).map((v) => v.type); const selectedProviderYieldId = useMemo( () => Maybe.fromNullable(enterRequest.requestDto.arguments?.providerId), - [enterRequest.requestDto.arguments?.providerId] + [enterRequest.requestDto.arguments?.providerId], ); const providerDetails = useProvidersDetails({ diff --git a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx index 79b2a927..890c64f3 100644 --- a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx @@ -1,6 +1,7 @@ import { useSelector } from "@xstate/store/react"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useUnstakeOrPendingActionParams } from "../../../hooks/navigation/use-unstake-or-pending-action-params"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { usePositionBalances } from "../../../hooks/use-position-balances"; @@ -19,12 +20,12 @@ export const UnstakeCompletePage = () => { const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const integrationData = useMemo( () => Maybe.of(exitRequest.integrationData), - [exitRequest.integrationData] + [exitRequest.integrationData], ); useTrackPage("unstakeComplete"); @@ -32,21 +33,21 @@ export const UnstakeCompletePage = () => { const providerDetails = useProvidersDetails({ integrationData, validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [] + p.type === "validators" ? p.validatorsAddresses : [], ), selectedProviderYieldId: Maybe.empty(), }); const token = useMemo( () => Maybe.of(exitRequest.unstakeToken), - [exitRequest.unstakeToken] + [exitRequest.unstakeToken], ); - const metadata = integrationData.map((d) => d.metadata); + const metadata = integrationData.map(getYieldMetadata); const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( () => formatNumber(exitRequest.requestDto.arguments?.amount ?? 0), - [exitRequest.requestDto.arguments?.amount] + [exitRequest.requestDto.arguments?.amount], ); const yieldType = useYieldType(integrationData).map((v) => v.type); diff --git a/packages/widget/src/pages/complete/state/index.tsx b/packages/widget/src/pages/complete/state/index.tsx index 8e6a6902..2b7bc856 100644 --- a/packages/widget/src/pages/complete/state/index.tsx +++ b/packages/widget/src/pages/complete/state/index.tsx @@ -1,5 +1,5 @@ -import type { TransactionType } from "@stakekit/api-hooks"; import { createContext, type PropsWithChildren, useContext } from "react"; +import type { TransactionType } from "../../../domain/types/action"; type CompleteCommonContextType = { urls: { @@ -36,7 +36,7 @@ export const useCompleteCommonContext = () => { if (!context) { throw new Error( - "useCompleteCommonContext must be used within a CompleteCommonContextProvider" + "useCompleteCommonContext must be used within a CompleteCommonContextProvider", ); } diff --git a/packages/widget/src/pages/components/footer-outlet/index.tsx b/packages/widget/src/pages/components/footer-outlet/index.tsx index b5f26391..5000eecc 100644 --- a/packages/widget/src/pages/components/footer-outlet/index.tsx +++ b/packages/widget/src/pages/components/footer-outlet/index.tsx @@ -51,7 +51,7 @@ const AnimatedFooterButton = (props: NonNullable) => { const { state } = useMountAnimation(); const [initAnimationFinished, setInitAnimationFinished] = useState( - state.layout + state.layout, ); const { disableInitLayoutAnimation } = useSettings(); @@ -86,7 +86,7 @@ const AnimatedFooterButton = (props: NonNullable) => { animate: {}, initial: { opacity: 0, translateY: "-40px" }, }; - }) + }), ) .unsafeCoerce(); diff --git a/packages/widget/src/pages/components/layout/layout-context.tsx b/packages/widget/src/pages/components/layout/layout-context.tsx index f62cbe8f..42703ee6 100644 --- a/packages/widget/src/pages/components/layout/layout-context.tsx +++ b/packages/widget/src/pages/components/layout/layout-context.tsx @@ -34,12 +34,12 @@ export const CurrentLayoutProvider = ({ children }: PropsWithChildren) => { setState({ pathname, height }); }, - [currentPathnameRef] + [currentPathnameRef], ); const value = useMemo( () => ({ state, setState: _setState }), - [_setState, state] + [_setState, state], ); return ( @@ -54,7 +54,7 @@ export const useCurrentLayout = () => { if (!value) { throw new Error( - "useCurrentLayout must be used within a CurrentLayoutContextProvider" + "useCurrentLayout must be used within a CurrentLayoutContextProvider", ); } diff --git a/packages/widget/src/pages/components/meta-info/index.tsx b/packages/widget/src/pages/components/meta-info/index.tsx index 408be515..23e6a5e6 100644 --- a/packages/widget/src/pages/components/meta-info/index.tsx +++ b/packages/widget/src/pages/components/meta-info/index.tsx @@ -1,4 +1,3 @@ -import type { TokenDto, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import { type JSX, type ReactNode, useMemo } from "react"; import { Box } from "../../../components/atoms/box"; @@ -8,12 +7,15 @@ import { ClockClockWiseIcon } from "../../../components/atoms/icons/clock-clock- import { GifIcon } from "../../../components/atoms/icons/gift"; import { InfoIcon } from "../../../components/atoms/icons/info"; import { Text } from "../../../components/atoms/typography/text"; +import type { TokenDto } from "../../../domain/types/tokens"; +import type { ValidatorDto } from "../../../domain/types/validators"; +import type { Yield } from "../../../domain/types/yields"; import { useYieldMetaInfo } from "../../../hooks/use-yield-meta-info"; import { dotContainer, dotText } from "./styles.css"; type Props = { isLoading?: boolean; - selectedStake: Maybe; + selectedStake: Maybe; selectedValidators: Map; selectedToken: Maybe; }; @@ -52,7 +54,7 @@ export const MetaInfo = ({ { text: lockupPeriod, icon: }, ].filter( (val): val is { text: string | ReactNode; icon: JSX.Element } => - !!val.text + !!val.text, ), [ campaign, @@ -63,7 +65,7 @@ export const MetaInfo = ({ withdrawnTime, extra, lockupPeriod, - ] + ], ); return isLoading ? ( diff --git a/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx b/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx index fdd550c0..00c166ab 100644 --- a/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx +++ b/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx @@ -1,4 +1,3 @@ -import type { ActionDto, YieldDto } from "@stakekit/api-hooks"; import { List } from "purify-ts"; import { useTranslation } from "react-i18next"; import { Box } from "../../../../../components/atoms/box"; @@ -6,6 +5,7 @@ import { ContentLoaderSquare } from "../../../../../components/atoms/content-loa import { ListItem } from "../../../../../components/atoms/list/list-item"; import { TokenIcon } from "../../../../../components/atoms/token-icon"; import { Text } from "../../../../../components/atoms/typography/text"; +import { getYieldMetadata } from "../../../../../domain/types/yields"; import { listItemContainer } from "../../../positions-page/style.css"; import { useActionListItem } from "../../hooks/use-action-list-item"; import { @@ -15,10 +15,7 @@ import { viaText, } from "../../style.css"; -type ActionYieldDto = { - actionData: ActionDto; - yieldData: YieldDto; -}; +import type { ActionYieldDto } from "../../types"; export const ActionListItem = ({ action, @@ -53,7 +50,7 @@ export const ActionListItem = ({ justifyContent="flex-start" alignItems="center" > - + - )) + )), ) .extractNullable()} @@ -117,7 +114,7 @@ export const ActionListItem = ({ ), - + , )} ); diff --git a/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx b/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx index 5352fcc3..51f19939 100644 --- a/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx +++ b/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx @@ -1,8 +1,8 @@ -import { ActionStatus } from "@stakekit/api-hooks"; import { Just, Nothing } from "purify-ts"; import { Box } from "../../../../../components/atoms/box"; import { UtilaFailIcon } from "../../../../../components/atoms/icons/utila/fail"; import { UtilaSuccessIcon } from "../../../../../components/atoms/icons/utila/success"; +import { ActionStatus } from "../../../../../domain/types/action"; import { useSettings } from "../../../../../providers/settings"; const ListItemBullet = ({ @@ -36,8 +36,8 @@ const ListItemBullet = ({ height="3" borderRadius="full" background="tokenSelectBackground" - /> - ) + />, + ), ) .extractNullable(); diff --git a/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts b/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts index dad54fa9..86fe0cc6 100644 --- a/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts +++ b/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts @@ -1,20 +1,15 @@ -import { - type ActionDto, - ActionStatus, - type YieldDto, -} from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { + ActionStatus, + getActionValidatorAddresses, +} from "../../../../domain/types/action"; import { useProvidersDetails } from "../../../../hooks/use-provider-details"; import { defaultFormattedNumber } from "../../../../utils"; import { dateOlderThen7Days } from "../../../../utils/date"; import { capitalizeFirstLetters } from "../../../../utils/formatters"; - -type ActionYieldDto = { - actionData: ActionDto; - yieldData: YieldDto; -}; +import type { ActionYieldDto } from "../types"; type ListItemContainerType = "claim" | "pending" | "actionRequired" | undefined; @@ -33,12 +28,14 @@ export const useActionListItem = (action: ActionYieldDto) => { const integrationData = useMemo( () => Maybe.fromNullable(action.yieldData), - [action.yieldData] + [action.yieldData], ); const providersDetails = useProvidersDetails({ integrationData, - validatorsAddresses: Maybe.of(action.actionData.validatorAddresses ?? []), + validatorsAddresses: Maybe.of( + getActionValidatorAddresses(action.actionData) ?? [], + ), selectedProviderYieldId: Maybe.empty(), }); @@ -48,7 +45,7 @@ export const useActionListItem = (action: ActionYieldDto) => { .map((t) => t.replaceAll("_", " ")) .map(capitalizeFirstLetters) .extract(), - [action] + [action], ); const actionOlderThan7Days = useMemo( @@ -56,7 +53,7 @@ export const useActionListItem = (action: ActionYieldDto) => { Maybe.of(action.actionData.createdAt) .map(dateOlderThen7Days) .orDefault(false), - [action] + [action], ); const badgeLabel = useMemo( @@ -73,7 +70,7 @@ export const useActionListItem = (action: ActionYieldDto) => { .map((status) => status.replaceAll("_", " ")) .map(capitalizeFirstLetters) .extract(), - [action, t, actionOlderThan7Days] + [action, t, actionOlderThan7Days], ); const badgeColor = useMemo( @@ -90,13 +87,13 @@ export const useActionListItem = (action: ActionYieldDto) => { }) .map((status) => BADGE_BG_MAP[status]) .extract(), - [action, actionOlderThan7Days] + [action, actionOlderThan7Days], ); const amount = useMemo( () => Maybe.fromNullable(action.actionData.amount).map(defaultFormattedNumber), - [action] + [action], ); return { diff --git a/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx b/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx index 269b0550..8cb109c1 100644 --- a/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx +++ b/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx @@ -1,8 +1,3 @@ -import { - ActionStatus, - ActionTypes, - type TransactionType, -} from "@stakekit/api-hooks"; import { useConnectModal } from "@stakekit/rainbowkit"; import { Maybe } from "purify-ts"; import { @@ -13,6 +8,10 @@ import { } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; +import { + ActionStatus, + type TransactionType, +} from "../../../../domain/types/action"; import { useActivityActions } from "../../../../hooks/api/use-activity-actions"; import { useActivityContext } from "../../../../providers/activity-provider"; import { useSKWallet } from "../../../../providers/sk-wallet"; @@ -51,13 +50,13 @@ export const ActivityPageContextProvider = ({ const urls = data.actionData.transactions .map((val) => ({ type: val.type, url: val.explorerUrl })) .filter( - (val): val is { type: TransactionType; url: string } => !!val.url + (val): val is { type: TransactionType; url: string } => !!val.url, ); const path = - data.actionData.type === ActionTypes.UNSTAKE + data.actionData.type === "UNSTAKE" ? "unstake" - : data.actionData.type === ActionTypes.STAKE + : data.actionData.type === "STAKE" ? "stake" : "pending"; @@ -91,26 +90,26 @@ export const ActivityPageContextProvider = ({ const actions = useMemo( () => Maybe.fromNullable(activityActions.allItems), - [activityActions.allItems] + [activityActions.allItems], ); const groupedDates = useMemo( () => actions.map((action) => action.map((a) => a.actionData.createdAt)), - [actions] + [actions], ); const [labels, counts] = useMemo( () => groupDateStrings(groupedDates.extract() ?? [], i18n), - [groupedDates, i18n] + [groupedDates, i18n], ); const bulletLines = useMemo( () => counts.reduce( (acc, val) => acc.concat(createSubArray(val)), - [] + [], ), - [counts] + [counts], ); const value = { @@ -133,7 +132,7 @@ export const useActivityPageContext = () => { if (!context) { throw new Error( - "useActivityPageContext must be used within a ActivityPageContext" + "useActivityPageContext must be used within a ActivityPageContext", ); } diff --git a/packages/widget/src/pages/details/activity-page/types.ts b/packages/widget/src/pages/details/activity-page/types.ts index b0ccb094..b62c78e2 100644 --- a/packages/widget/src/pages/details/activity-page/types.ts +++ b/packages/widget/src/pages/details/activity-page/types.ts @@ -1,9 +1,10 @@ -import type { ActionDto, YieldDto } from "@stakekit/api-hooks"; import type { TFunction } from "i18next"; +import type { ActionDto } from "../../../domain/types/action"; +import type { Yield } from "../../../domain/types/yields"; export type ActionYieldDto = { actionData: ActionDto; - yieldData: YieldDto; + yieldData: Yield; }; type DateGroupLabels = "today" | "yesterday" | string; diff --git a/packages/widget/src/pages/details/details-page/components/tabs.tsx b/packages/widget/src/pages/details/details-page/components/tabs.tsx index 9ff6a51e..b9c58484 100644 --- a/packages/widget/src/pages/details/details-page/components/tabs.tsx +++ b/packages/widget/src/pages/details/details-page/components/tabs.tsx @@ -106,7 +106,7 @@ export const AnimatedTabs = (props: TabsProps) => { .map((val) => ({ animate: { ...animateTo, transition: val.transition }, initial: val.initial, - })) + })), ) .unsafeCoerce(); diff --git a/packages/widget/src/pages/details/details-page/details.page.tsx b/packages/widget/src/pages/details/details-page/details.page.tsx index 9a71e50f..951e7eed 100644 --- a/packages/widget/src/pages/details/details-page/details.page.tsx +++ b/packages/widget/src/pages/details/details-page/details.page.tsx @@ -15,7 +15,7 @@ export const Details = () => { return acc; }, 0), - [positionsData.data] + [positionsData.data], ); return ( diff --git a/packages/widget/src/pages/details/earn-page/components/extra-args-selection/index.tsx b/packages/widget/src/pages/details/earn-page/components/extra-args-selection/index.tsx index a72c95d1..08dd056d 100644 --- a/packages/widget/src/pages/details/earn-page/components/extra-args-selection/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/extra-args-selection/index.tsx @@ -1,9 +1,10 @@ -import type { TronResourceType } from "@stakekit/api-hooks"; import { useTranslation } from "react-i18next"; import { Box } from "../../../../../components/atoms/box"; import { Divider } from "../../../../../components/atoms/divider"; import { Dropdown } from "../../../../../components/atoms/dropdown"; import { Text } from "../../../../../components/atoms/typography/text"; +import type { TronResourceType } from "../../../../../domain/types/tron"; +import { getYieldActionArg } from "../../../../../domain/types/yields"; import { useIsDashboard } from "../../../../../pages-dashboard/providers/dashboard-context"; import { useEarnPageContext } from "../../state/earn-page-context"; @@ -16,9 +17,9 @@ export const ExtraArgsSelection = () => { const isDashboard = useIsDashboard(); return selectedStake - .chainNullable((ss) => ss.args.enter.args?.tronResource) + .chainNullable((ss) => getYieldActionArg(ss, "enter", "tronResource")) .map((tronResources) => { - const options = tronResources.options.map((v) => ({ + const options = (tronResources.options ?? []).map((v) => ({ label: v, value: v as TronResourceType, })); diff --git a/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx index 93019a48..e7a6299c 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx @@ -8,7 +8,11 @@ import { Image } from "../../../../../components/atoms/image"; import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { SelectYield } from "../../../../../components/molecules/select-yield"; -import { getYieldProviderYieldIds } from "../../../../../domain/types/yields"; +import { + getYieldProviderDetails, + getYieldProviderYieldIds, + isYieldWithProviderOptions, +} from "../../../../../domain/types/yields"; import { useMultiYields } from "../../../../../hooks/api/use-multi-yields"; import { useEarnPageContext } from "../../state/earn-page-context"; import { @@ -26,7 +30,7 @@ export const SelectProvider = () => { const { t } = useTranslation(); const providerYieldIdOptions = selectedStake - .filter((ss) => !!ss.args.enter.args?.providerId?.required) + .filter(isYieldWithProviderOptions) .map(getYieldProviderYieldIds); const yields = useMultiYields(providerYieldIdOptions.orDefault([])); @@ -35,7 +39,20 @@ export const SelectProvider = () => { selectedProviderYieldId, yields: Maybe.fromNullable(yields.data), }).chainNullable((val) => - val.yields.find((v) => v.id === val.selectedProviderYieldId) + val.yields.find((v) => v.id === val.selectedProviderYieldId), + ); + + const providerSelection = Maybe.fromRecord({ + selectedStake, + providerYieldIdOptions, + selectedProviderYield, + }).chain((val) => + Maybe.fromNullable(getYieldProviderDetails(val.selectedProviderYield)).map( + (provider) => ({ + ...val, + provider, + }), + ), ); return appLoading ? ( @@ -43,11 +60,7 @@ export const SelectProvider = () => { ) : ( - Maybe.fromRecord({ - selectedStake, - providerYieldIdOptions, - selectedProviderYield, - }) + providerSelection .map((val) => ( @@ -73,17 +86,11 @@ export const SelectProvider = () => { { - {val.selectedProviderYield.metadata.provider?.name} + {val.provider.name} diff --git a/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx index 80567ff4..36f508f6 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx @@ -77,7 +77,7 @@ export const SelectTokenSection = () => { !!showUSDeBanner && isLedgerLive && val.selectedTokenAvailableAmount.amount.isZero() && - isUSDeToken(val.selectedToken) + isUSDeToken(val.selectedToken), ) .isJust(); @@ -130,7 +130,7 @@ export const SelectTokenSection = () => { rec: selectTokenSection, variant, state: submitted && stakeAmountIsZero ? "danger" : "default", - }) + }), )} > {variant === "zerion" && ( @@ -173,7 +173,7 @@ export const SelectTokenSection = () => { combineRecipeWithVariant({ rec: selectTokenBalance, variant, - }) + }), )} > {formattedPrice} @@ -194,7 +194,10 @@ export const SelectTokenSection = () => { }} data-state={errorBalance ? "error" : "valid"} className={clsx( - combineRecipeWithVariant({ rec: selectTokenBalance, variant }) + combineRecipeWithVariant({ + rec: selectTokenBalance, + variant, + }), )} > {selectedTokenAvailableAmount @@ -226,7 +229,7 @@ export const SelectTokenSection = () => { )} - ) + ), ) .extractNullable()} diff --git a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx index d16258a4..6179902f 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx @@ -1,4 +1,3 @@ -import type { TokenBalanceScanResponseDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import type { ComponentProps } from "react"; import { memo, useMemo } from "react"; @@ -9,6 +8,7 @@ import { } from "../../../../../components/atoms/select-modal"; import { TokenIcon } from "../../../../../components/atoms/token-icon"; import { Text } from "../../../../../components/atoms/typography/text"; +import type { TokenBalanceScanResponseDto } from "../../../../../domain/types/token-balance"; import { useTrackEvent } from "../../../../../hooks/tracking/use-track-event"; import { defaultFormattedNumber } from "../../../../../utils"; import { selectItemText } from "../../styles.css"; @@ -25,12 +25,12 @@ export const SelectTokenListItem = memo( const formattedAmount = useMemo( () => defaultFormattedNumber(amount), - [amount] + [amount], ); const amountGreaterThanZero = useMemo( () => amount.isGreaterThan(0), - [amount] + [amount], ); const trackEvent = useTrackEvent(); @@ -80,5 +80,5 @@ export const SelectTokenListItem = memo( ); - } + }, ); diff --git a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx index c737003e..1e232e56 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx @@ -47,7 +47,7 @@ export const SelectToken = () => { tokenBalancesData.map((v) => v.filtered).extract() ?? [], })) .extractNullable(), - [selectedToken, tokenBalancesData] + [selectedToken, tokenBalancesData], ); if (!data) return null; @@ -75,7 +75,7 @@ export const SelectToken = () => { combineRecipeWithVariant({ variant, rec: selectTokenButton, - }) + }), )} > { .map((val) => { const selectedValidatorsArr = [...selectedValidators.values()]; - const multiSelect = - !!val.selectedStake.args.enter.args?.validatorAddresses?.required; + const multiSelect = isYieldActionArgRequired( + val.selectedStake, + "enter", + "validatorAddresses", + ); return ( { selectedValidatorsArr={selectedValidatorsArr} multiSelect={multiSelect} isWithProviderOptions={isYieldWithProviderOptions( - val.selectedStake + val.selectedStake, )} /> } diff --git a/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx b/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx index 964f449c..7e0fe1a1 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx @@ -1,5 +1,4 @@ import { Trigger } from "@radix-ui/react-dialog"; -import type { ValidatorDto } from "@stakekit/api-hooks"; import { useTranslation } from "react-i18next"; import { Box } from "../../../../../components/atoms/box"; import { Divider } from "../../../../../components/atoms/divider"; @@ -11,6 +10,7 @@ import { Image } from "../../../../../components/atoms/image"; import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { inactiveContainer } from "../../../../../components/molecules/select-validator/styles.css"; +import type { ValidatorDto } from "../../../../../domain/types/validators"; import { noWrap } from "../../../../details/positions-page/components/styles.css"; import { addValidatorButton, @@ -101,7 +101,7 @@ export const SelectValidatorTrigger = ({ {t( sv.status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive" + : "details.validators_inactive", )} diff --git a/packages/widget/src/pages/details/earn-page/components/select-validator-section/use-select-validator.ts b/packages/widget/src/pages/details/earn-page/components/select-validator-section/use-select-validator.ts index d4ccadc8..1ae82740 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-validator-section/use-select-validator.ts +++ b/packages/widget/src/pages/details/earn-page/components/select-validator-section/use-select-validator.ts @@ -1,4 +1,4 @@ -import type { ValidatorDto } from "@stakekit/api-hooks"; +import type { ValidatorDto } from "../../../../../domain/types/validators"; import { useTrackEvent } from "../../../../../hooks/tracking/use-track-event"; import { useEarnPageContext } from "../../state/earn-page-context"; diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/index.tsx index b12f36b0..9c37357c 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/index.tsx @@ -5,6 +5,7 @@ import { ContentLoaderSquare } from "../../../../../components/atoms/content-loa import { Divider } from "../../../../../components/atoms/divider"; import { ToolTip } from "../../../../../components/atoms/tooltip"; import { Text } from "../../../../../components/atoms/typography/text"; +import { getYieldRewardType } from "../../../../../domain/types/yields"; import { useSettings } from "../../../../../providers/settings"; import { combineRecipeWithVariant } from "../../../../../utils/styles"; import { useEarnPageContext } from "../../state/earn-page-context"; @@ -85,7 +86,7 @@ export const SelectYieldSection = () => { data-testid="estimated-reward__percent" > {selectedStake - .filter((pd) => pd.rewardType === "variable") + .filter((pd) => getYieldRewardType(pd) === "variable") .map(() => ( { groups: val.map((v) => v.title), groupCounts: val.map((v) => v.itemsLength), }; - }) + }), ) .extractNullable(), - [selectedStake, selectedStakeData] + [selectedStake, selectedStakeData], ); const { variant } = useSettings(); @@ -72,7 +73,7 @@ export const SelectOpportunity = () => { rec: selectOpportunityButton, variant, }), - pressAnimation + pressAnimation, )} data-testid="select-opportunity" > @@ -82,7 +83,10 @@ export const SelectOpportunity = () => { justifyContent="center" alignItems="center" > - + {data.ss.token.symbol} diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx index 21e7b24e..37397c25 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx @@ -25,16 +25,16 @@ export const SelectYieldRewardDetails = () => { useEarnPageContext(); const rewardRateDetails = selectedStake.chainNullable( - getYieldRewardRateDetails + getYieldRewardRateDetails, ); const earnYearly = estimatedRewards.mapOrDefault( (e) => `${e.yearly} ${rewardsTokenSymbol}`, - "" + "", ); const earnMonthly = estimatedRewards.mapOrDefault( (e) => `${e.monthly} ${rewardsTokenSymbol}`, - "" + "", ); return ( @@ -163,7 +163,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }) + }), )} > {t(variant === "zerion" ? "details.rewards.yearly" : "shared.yearly")} @@ -174,7 +174,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }) + }), )} > {earnYearly} @@ -195,7 +195,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }) + }), )} > {t("shared.monthly")} @@ -206,7 +206,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }) + }), )} > {earnMonthly} diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx index ad9c5c51..dba429aa 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx @@ -3,6 +3,11 @@ import { Box } from "../../../../../components/atoms/box"; import { Image } from "../../../../../components/atoms/image"; import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; +import { + getYieldMetadata, + getYieldProviderDetails, + isYieldActionArgRequired, +} from "../../../../../domain/types/yields"; import { useEarnPageContext } from "../../state/earn-page-context"; export const StakedVia = () => { @@ -14,13 +19,13 @@ export const StakedVia = () => { .filter( (val) => !!( - val.metadata.type === "staking" && - !val.args.enter.args?.validatorAddress?.required && - !val.args.enter.args?.validatorAddresses?.required && - val.metadata.provider - ) + getYieldMetadata(val).type === "staking" && + !isYieldActionArgRequired(val, "enter", "validatorAddress") && + !isYieldActionArgRequired(val, "enter", "validatorAddresses") && + getYieldProviderDetails(val) + ), ) - .chainNullable((val) => val.metadata.provider) + .chainNullable((val) => getYieldProviderDetails(val)) .map((val) => ( { const perReward = estimatedRewards .map((val) => { @@ -33,7 +33,7 @@ export const useAnimateYieldPercent = ( const transformedMotionValue = useTransform( rewardPercMotionValue, - (val) => `${val.toFixed(2)}%` + (val) => `${val.toFixed(2)}%`, ); return typeof perReward === "string" || config.env.isTestMode diff --git a/packages/widget/src/pages/details/earn-page/earn.page.tsx b/packages/widget/src/pages/details/earn-page/earn.page.tsx index 51b91ae8..7a3d3600 100644 --- a/packages/widget/src/pages/details/earn-page/earn.page.tsx +++ b/packages/widget/src/pages/details/earn-page/earn.page.tsx @@ -111,7 +111,7 @@ export const AnimatedEarnPage = () => { .map((val) => ({ animate: { ...animateTo, transition: val.transition }, initial: val.initial, - })) + })), ) .unsafeCoerce(); diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx index 21860e2a..4fa66c66 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx @@ -1,9 +1,3 @@ -import type { - TokenBalanceScanResponseDto, - TronResourceType, - ValidatorDto, - YieldDto, -} from "@stakekit/api-hooks"; import { useConnectModal } from "@stakekit/rainbowkit"; import { useMutation } from "@tanstack/react-query"; import BigNumber from "bignumber.js"; @@ -28,13 +22,21 @@ import { tokenString, } from "../../../../domain"; import { getInitSelectedValidators } from "../../../../domain/types/stake"; +import type { TokenBalanceScanResponseDto } from "../../../../domain/types/token-balance"; +import type { TronResourceType } from "../../../../domain/types/tron"; +import type { ValidatorDto } from "../../../../domain/types/validators"; import { type ExtendedYieldType, getExtendedYieldType, + getYieldRewardRate, + getYieldRewardTokens, getYieldTypeLabels, getYieldTypesSortRank, isBittensorStaking, isNonZeroRewardRateYield, + isYieldActionArgRequired, + isYieldIntegrationAggregator, + type Yield, } from "../../../../domain/types/yields"; import { useDefaultTokens } from "../../../../hooks/api/use-default-tokens"; import { useStreamMultiYields } from "../../../../hooks/api/use-multi-yields"; @@ -72,7 +74,7 @@ import { usePendingActionDeepLink } from "./use-pending-action-deep-link"; import { useStakeEnterRequestDto } from "./use-stake-enter-request-dto"; const EarnPageContext = createContext( - undefined + undefined, ); export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { @@ -107,7 +109,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const yieldType = useYieldType(selectedStake).mapOrDefault( (y) => y.title, - "" + "", ); const initYieldRes = useInitYield({ selectedToken }); @@ -123,9 +125,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const pointsRewardTokens = useMemo( () => selectedStake - .chainNullable((val) => val.metadata.rewardTokens) + .map(getYieldRewardTokens) .map((val) => val.filter((rt) => rt.isPoints)), - [selectedStake] + [selectedStake], ); const pricesState = useTokensPrices({ @@ -157,10 +159,10 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { token: val.selectedToken, prices: val.prices, pricePerShare: null, - }) + }), ) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""), - [baseToken, pricesState.data, selectedToken, stakeAmount] + [baseToken, pricesState.data, selectedToken, stakeAmount], ); const selectedTokenAvailableAmount = useMemo( @@ -171,7 +173,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { fullFormattedAmount: formatNumber(am), amount: am, })), - [availableAmount, symbol] + [availableAmount, symbol], ); const [stakeSearch, setStakeSearch] = useState(""); @@ -182,7 +184,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const deferredValidatorSearch = useDeferredValue(validatorSearch); const multiYields = useStreamMultiYields( - useMemo(() => availableYields.orDefault([]), [availableYields]) + useMemo(() => availableYields.orDefault([]), [availableYields]), ); const tokenBalancesScan = useTokenBalancesScan(); @@ -211,7 +213,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { tbSet: new Set(), tbWithAmount: [] as TokenBalanceScanResponseDto[], tbWithoutAmount: [] as TokenBalanceScanResponseDto[], - } + }, ); return [ @@ -223,30 +225,34 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { .chain((tb) => Maybe.of(deferredTokenSearch) .chain((val) => - val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty() + val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty(), ) .map((lowerSearch) => ({ all: tb, filtered: tb.filter( (t) => t.token.name.toLowerCase().includes(lowerSearch) || - t.token.symbol.toLowerCase().includes(lowerSearch) + t.token.symbol.toLowerCase().includes(lowerSearch), ), })) - .alt(Maybe.of({ all: tb, filtered: tb })) + .alt(Maybe.of({ all: tb, filtered: tb })), ), - [defaultTokens.data, deferredTokenSearch, tokenBalancesScan.data] + [defaultTokens.data, deferredTokenSearch, tokenBalancesScan.data], ); const selectedStakeData = useMemo>( () => Maybe.of(multiYields) - .map((val) => [...val].sort((a, b) => b.rewardRate - a.rewardRate)) + .map((val) => + [...val].sort( + (a, b) => getYieldRewardRate(b) - getYieldRewardRate(a), + ), + ) .map((val) => val.filter(isNonZeroRewardRateYield)) .chain((yieldDtos) => Maybe.of(deferredStakeSearch) .chain((val) => - val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty() + val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty(), ) .map((lowerSearch) => ({ all: yieldDtos, @@ -255,18 +261,18 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { d.token.name.toLowerCase().includes(lowerSearch) || d.token.symbol.toLowerCase().includes(lowerSearch) || d.metadata.name.toLowerCase().includes(lowerSearch) || - d.metadata.rewardTokens?.some( + getYieldRewardTokens(d).some( (rt) => rt.name.toLowerCase().includes(lowerSearch) || - rt.symbol.toLowerCase().includes(lowerSearch) - ) + rt.symbol.toLowerCase().includes(lowerSearch), + ), ), })) - .alt(Maybe.of({ all: yieldDtos, filteredDtos: yieldDtos })) + .alt(Maybe.of({ all: yieldDtos, filteredDtos: yieldDtos })), ) .map(({ all, filteredDtos }) => { const sorted = [...filteredDtos].sort( - (a, b) => getYieldTypesSortRank(a) - getYieldTypesSortRank(b) + (a, b) => getYieldTypesSortRank(a) - getYieldTypesSortRank(b), ); const groupsWithCounts = [ @@ -281,7 +287,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { items: [curr], }); } else { - acc.get(extendedYieldType)!.items.push(curr); + acc.get(extendedYieldType)?.items.push(curr); } return acc; @@ -291,9 +297,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { { type: ExtendedYieldType; title: ReturnType["title"]; - items: YieldDto[]; + items: Yield[]; } - >() + >(), ) .values(), ].reduce( @@ -307,7 +313,10 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { return acc; }, - new Map() + new Map< + ExtendedYieldType, + { itemsLength: number; title: string } + >(), ); return { @@ -316,7 +325,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { groupsWithCounts, }; }), - [deferredStakeSearch, multiYields, t] + [deferredStakeSearch, multiYields, t], ); const yieldValidators = useYieldValidators({ @@ -354,10 +363,10 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { .map( (stake) => !!( - stake.metadata.isIntegrationAggregator || - stake.args.enter.args?.validatorAddress?.required || - stake.args.enter.args?.validatorAddresses?.required - ) + isYieldIntegrationAggregator(stake) || + isYieldActionArgRequired(stake, "enter", "validatorAddress") || + isYieldActionArgRequired(stake, "enter", "validatorAddresses") + ), ) .orDefault(false); @@ -399,20 +408,20 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { return validators.filter( (validator) => validator.name?.toLowerCase().includes(searchInput) || - validator.address.toLowerCase().includes(searchInput) + validator.address.toLowerCase().includes(searchInput), ); }) .map((validators) => { if (variant === "utila" || variant === "porto") { return [...validators].sort( - (a, b) => (b.apr ?? 0) - (a.apr ?? 0) + (a, b) => (b.apr ?? 0) - (a.apr ?? 0), ); } return validators; - }) + }), ), - [deferredValidatorSearch, selectedStake, variant, yieldValidators.data] + [deferredValidatorSearch, selectedStake, variant, yieldValidators.data], ); const onYieldSearch: SelectModalProps["onSearch"] = (val) => @@ -427,7 +436,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const onTokenBalanceSelect = useCallback( (tokenBalance: TokenBalanceScanResponseDto) => dispatch({ type: "token/select", data: tokenBalance.token }), - [dispatch] + [dispatch], ); const onYieldSelect = (yieldId: string) => { @@ -438,9 +447,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const onValidatorSelect = (item: ValidatorDto) => selectedStake.ifJust((ss) => - ss.args.enter.args?.validatorAddresses?.required + isYieldActionArgRequired(ss, "enter", "validatorAddresses") ? dispatch({ type: "validator/multiselect", data: item }) - : dispatch({ type: "validator/select", data: item }) + : dispatch({ type: "validator/select", data: item }), ); const onValidatorRemove = (item: ValidatorDto) => @@ -506,7 +515,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { selectedStake.ifJust((ss) => { if ( - ss.args.enter.args?.tronResource?.required && + isYieldActionArgRequired(ss, "enter", "tronResource") && tronResource.isNothing() ) { val.errors.tronResource = true; @@ -554,7 +563,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { selectedStake .filter(() => maxIntegrationAmount.isJust() && !isForceMax) .map(() => maxEnterOrExitAmount.toNumber()), - [maxEnterOrExitAmount, maxIntegrationAmount, isForceMax, selectedStake] + [maxEnterOrExitAmount, maxIntegrationAmount, isForceMax, selectedStake], ); const stakeMinAmount = useMemo( @@ -563,7 +572,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { .filter(() => minIntegrationAmount.isJust() && !isForceMax) .map(() => minEnterOrExitAmount.toNumber()) .filter((val) => new BigNumber(val).isGreaterThan(0)), - [minEnterOrExitAmount, minIntegrationAmount, isForceMax, selectedStake] + [minEnterOrExitAmount, minIntegrationAmount, isForceMax, selectedStake], ); const onSelectOpportunityClose = () => setStakeSearch(""); @@ -576,7 +585,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const { state } = useMountAnimation(); const yieldOpportunityLoading = useYieldOpportunity( - selectedStake.extract()?.id + selectedStake.extract()?.id, ).isLoading; const appLoading = @@ -599,7 +608,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const buttonCTAText = useYieldType(selectedStake).mapOrDefault( (v) => v.cta, - "" + "", ); const providersDetails = useProvidersDetails({ @@ -640,9 +649,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { stakeToken: val.selectedToken, yieldDto: val.selectedStake, }), - false + false, ), - [selectedStake, selectedToken] + [selectedStake, selectedToken], ); const selectTokenIsLoading = @@ -689,7 +698,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { label: t( isLedgerLiveAccountPlaceholder ? "init.ledger_add_account" - : "init.connect_wallet" + : "init.connect_wallet", ), onClick: () => connectClickRef.current(), }, @@ -705,8 +714,8 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { isFetching, t, hasNotYieldsForToken, - ] - ) + ], + ), ); const value = { diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx index 72c34d2d..05c19945 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx @@ -1,4 +1,3 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import type { Networks } from "@stakekit/common"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; @@ -13,6 +12,8 @@ import { } from "react"; import { equalTokens } from "../../../../domain"; import { isNetworkWithEnterMinBasedOnPosition } from "../../../../domain/types/stake"; +import type { TokenDto } from "../../../../domain/types/tokens"; +import type { Yield } from "../../../../domain/types/yields"; import { useYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity"; import { useMaxMinYieldAmount } from "../../../../hooks/use-max-min-yield-amount"; import { usePositionsData } from "../../../../hooks/use-positions-data"; @@ -28,10 +29,10 @@ import { useTrackStateEvents } from "./use-track-state-events"; import { onYieldSelectState } from "./utils"; const EarnPageStateContext = createContext<(State & ExtraData) | undefined>( - undefined + undefined, ); const EarnPageDispatchContext = createContext | undefined>( - undefined + undefined, ); const EarnPageStateUsageBoundary = createContext(false); @@ -68,7 +69,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { return Maybe.fromFalsy( state.selectedToken .map((v) => !equalTokens(v, action.data)) - .orDefault(true) + .orDefault(true), ) .chain(() => getInitYield({ selectedToken: action.data }) @@ -76,9 +77,9 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { onYieldSelectState({ yieldDto: val, positionsData: positionsData.data, - }) + }), ) - .alt(Maybe.of(null)) + .alt(Maybe.of(null)), ) .map((val) => ({ ...getInitialState(), @@ -90,13 +91,15 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { case "yield/select": { return Maybe.fromFalsy( - state.selectedStakeId.map((v) => v !== action.data.id).orDefault(true) + state.selectedStakeId + .map((v) => v !== action.data.id) + .orDefault(true), ) .map(() => onYieldSelectState({ yieldDto: action.data, positionsData: positionsData.data, - }) + }), ) .map((val) => ({ ...getInitialState(), @@ -191,13 +194,13 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { const initTokenRes = useInitToken(); const initToken = useMemo( () => Maybe.fromNullable(initTokenRes.data), - [initTokenRes.data] + [initTokenRes.data], ); const initYieldRes = useInitYield({ selectedToken }); const initYield = useMemo( () => Maybe.fromNullable(initYieldRes.data), - [initYieldRes.data] + [initYieldRes.data], ); const { availableAmount, availableYields } = useTokenBalance({ @@ -216,7 +219,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { const selectedStake = useMemo( () => Maybe.fromNullable(yieldOpportunity.data), - [yieldOpportunity.data] + [yieldOpportunity.data], ); /** @@ -224,17 +227,17 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { */ const stakeAmount = useMemo( () => (isForceMax ? maxEnterOrExitAmount : _stakeAmount), - [isForceMax, maxEnterOrExitAmount, _stakeAmount] + [isForceMax, maxEnterOrExitAmount, _stakeAmount], ); const setToken = useCallback( (token: TokenDto) => dispatch({ type: "token/select", data: token }), - [] + [], ); const setYield = useCallback( - (yieldDto: YieldDto) => dispatch({ type: "yield/select", data: yieldDto }), - [] + (yieldDto: Yield) => dispatch({ type: "yield/select", data: yieldDto }), + [], ); /** @@ -270,10 +273,10 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { .filter( (it) => isNetworkWithEnterMinBasedOnPosition(it.network as Networks) && - positionsData.isPending + positionsData.isPending, ) .isJust(), - [initToken, positionsData] + [initToken, positionsData], ); /** @@ -312,7 +315,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { */ useEffect(() => { initToken.ifNothing(() => - selectedToken.ifJust(() => dispatch({ type: "state/reset" })) + selectedToken.ifJust(() => dispatch({ type: "state/reset" })), ); }, [initToken, selectedToken]); @@ -323,7 +326,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { onMaxClick: () => dispatch({ type: "stakeAmount/max", data: maxEnterOrExitAmount }), }), - [maxEnterOrExitAmount] + [maxEnterOrExitAmount], ); const { @@ -374,7 +377,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { availableYields, hasNotYieldsForToken, selectedProviderYieldId, - ] + ], ); return ( diff --git a/packages/widget/src/pages/details/earn-page/state/types.ts b/packages/widget/src/pages/details/earn-page/state/types.ts index 54ebd1e0..5fd3000a 100644 --- a/packages/widget/src/pages/details/earn-page/state/types.ts +++ b/packages/widget/src/pages/details/earn-page/state/types.ts @@ -1,12 +1,10 @@ -import type { - TokenBalanceScanResponseDto, - TokenDto, - TronResourceType, - ValidatorDto, - YieldDto, -} from "@stakekit/api-hooks"; import type BigNumber from "bignumber.js"; import type { Maybe } from "purify-ts"; +import type { TokenBalanceScanResponseDto } from "../../../../domain/types/token-balance"; +import type { TokenDto } from "../../../../domain/types/tokens"; +import type { TronResourceType } from "../../../../domain/types/tron"; +import type { ValidatorDto } from "../../../../domain/types/validators"; +import type { Yield } from "../../../../domain/types/yields"; import type { useEstimatedRewards } from "../../../../hooks/use-estimated-rewards"; import type { useProvidersDetails } from "../../../../hooks/use-provider-details"; import type { useRewardTokenDetails } from "../../../../hooks/use-reward-token-details"; @@ -14,7 +12,7 @@ import type { Action } from "../../../../types/utils"; import type { SelectedStakeData } from "../types"; export type State = { - selectedToken: Maybe; + selectedToken: Maybe; selectedStakeId: Maybe< TokenBalanceScanResponseDto["availableYields"][number] >; @@ -22,11 +20,11 @@ export type State = { stakeAmount: BigNumber; useMaxAmount: boolean; tronResource: Maybe; - selectedProviderYieldId: Maybe; + selectedProviderYieldId: Maybe; }; type TokenBalanceSelectAction = Action<"token/select", TokenDto>; -type YieldSelectAction = Action<"yield/select", YieldDto>; +type YieldSelectAction = Action<"yield/select", Yield>; type StakeAmountChangeAction = Action<"stakeAmount/change", BigNumber>; type StakeAmountMaxAction = Action<"stakeAmount/max", BigNumber>; @@ -40,7 +38,7 @@ type SelectTronResourceAction = Action<"tronResource/select", TronResourceType>; type ProviderYieldIdSelectAction = Action< "providerYieldId/select", - YieldDto["id"] + Yield["id"] >; export type Actions = @@ -57,7 +55,7 @@ export type Actions = export type ExtraData = { actions: { onMaxClick: () => void }; - selectedStake: Maybe; + selectedStake: Maybe; stakeAmountLessThanMin: boolean; stakeAmountGreaterThanMax: boolean; stakeAmountGreaterThanAvailableAmount: boolean; @@ -128,7 +126,7 @@ export type EarnPageContextType = { stakeAmountIsZero: boolean; }; }; - pointsRewardTokens: Maybe; + pointsRewardTokens: Maybe<(TokenDto & { isPoints?: boolean })[]>; selectTokenIsLoading: boolean; selectYieldIsLoading: boolean; selectValidatorIsLoading: boolean; diff --git a/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts b/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts index 7554172d..7ec45c98 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts @@ -18,7 +18,7 @@ export const useAmountValidation = ({ availableAmount .map(() => stakeAmount.isLessThan(minEnterOrExitAmount)) .orDefault(false), - [availableAmount, stakeAmount, minEnterOrExitAmount] + [availableAmount, stakeAmount, minEnterOrExitAmount], ); const stakeAmountGreaterThanMax = useMemo( @@ -26,12 +26,12 @@ export const useAmountValidation = ({ availableAmount .map(() => stakeAmount.isGreaterThan(maxEnterOrExitAmount)) .orDefault(false), - [availableAmount, stakeAmount, maxEnterOrExitAmount] + [availableAmount, stakeAmount, maxEnterOrExitAmount], ); const stakeAmountIsZero = useMemo( () => availableAmount.map(() => stakeAmount.isZero()).orDefault(false), - [stakeAmount, availableAmount] + [stakeAmount, availableAmount], ); const stakeAmountGreaterThanAvailableAmount = useMemo( @@ -39,7 +39,7 @@ export const useAmountValidation = ({ availableAmount .map((val) => stakeAmount.isGreaterThan(val)) .orDefault(false), - [availableAmount, stakeAmount] + [availableAmount, stakeAmount], ); return { diff --git a/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts index fb594af6..33af9918 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts @@ -1,7 +1,7 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useCallback } from "react"; import { tokenString } from "../../../../domain"; +import type { TokenDto } from "../../../../domain/types/tokens"; import { getCachedFirstEligibleYield } from "../../../../hooks/api/use-multi-yields"; import { useSKQueryClient } from "../../../../providers/query-client"; import { useTokenBalancesMap } from "./use-token-balances-map"; @@ -13,13 +13,13 @@ export const useGetInitYield = () => { return useCallback( ({ selectedToken }: { selectedToken: TokenDto }) => Maybe.fromNullable( - tokenBalancesMap.get(tokenString(selectedToken)) + tokenBalancesMap.get(tokenString(selectedToken)), ).chain((val) => getCachedFirstEligibleYield({ queryClient, yieldIds: val.availableYields, - }) + }), ), - [queryClient, tokenBalancesMap] + [queryClient, tokenBalancesMap], ); }; diff --git a/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts b/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts index 0565abc3..b9fc35de 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts @@ -1,6 +1,6 @@ -import type { TokenBalanceScanResponseDto } from "@stakekit/api-hooks"; import { useCallback } from "react"; import { tokenString } from "../../../../domain"; +import type { TokenBalanceScanResponseDto } from "../../../../domain/types/token-balance"; import type { TokenString } from "../../../../domain/types/tokens"; export const useGetTokenBalancesMap = () => @@ -15,8 +15,8 @@ export const useGetTokenBalancesMap = () => new Map([ ...(defaultTokens ?? []).map((v) => [tokenString(v.token), v] as const), ...(tokenBalancesScan ?? []).map( - (v) => [tokenString(v.token), v] as const + (v) => [tokenString(v.token), v] as const, ), ]), - [] + [], ); diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts index 9e46d597..07e237ca 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts @@ -73,12 +73,12 @@ export const useInitToken = () => { defaultTokens: val.defaultTokens, tokenBalances: val.tokenBalancesScan, initQueryParams: Maybe.fromNullable(initParams), - }).toEither(new Error("could not get initial token")) + }).toEither(new Error("could not get initial token")), ).chain((token) => EitherAsync.liftEither( Maybe.fromNullable( - getTokenBalancesMap(val).get(tokenString(token)) - ).toEither(new Error("could not get token balance")) + getTokenBalancesMap(val).get(tokenString(token)), + ).toEither(new Error("could not get token balance")), ) .chain((tokenBalance) => getFirstEligibleYield({ @@ -94,11 +94,11 @@ export const useInitToken = () => { validatorsConfig, preferredTokenYieldsPerNetwork: preferredTokenYieldsPerNetwork ?? null, - }) + }), ) - .map(() => token) - ) - ) + .map(() => token), + ), + ), ) ).unsafeCoerce(), }); diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts index 1e2e3c06..f5ed69ac 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts @@ -1,9 +1,9 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import { useQuery } from "@tanstack/react-query"; import BigNumber from "bignumber.js"; import { EitherAsync, Maybe } from "purify-ts"; import { getTokenBalances } from "../../../../common/get-token-balances"; import { tokenString } from "../../../../domain"; +import type { TokenDto } from "../../../../domain/types/tokens"; import { getFirstEligibleYield } from "../../../../hooks/api/use-multi-yields"; import { getInitParams } from "../../../../hooks/use-init-params"; import { usePositionsData } from "../../../../hooks/use-positions-data"; @@ -53,7 +53,7 @@ export const useInitYield = ({ queryFn: async () => ( await EitherAsync.liftEither( - selectedToken.toEither(new Error("no token selected")) + selectedToken.toEither(new Error("no token selected")), ).chain((token) => getTokenBalances({ additionalAddresses, @@ -65,9 +65,9 @@ export const useInitYield = ({ .chain((val) => EitherAsync.liftEither( Maybe.fromNullable( - getTokenBalancesMap(val).get(tokenString(token)) - ).toEither(new Error("could not get token balance")) - ) + getTokenBalancesMap(val).get(tokenString(token)), + ).toEither(new Error("could not get token balance")), + ), ) .chain((val) => getInitParams({ @@ -89,9 +89,9 @@ export const useInitYield = ({ validatorsConfig, preferredTokenYieldsPerNetwork: preferredTokenYieldsPerNetwork ?? null, - }) - ) - ) + }), + ), + ), ) ).unsafeCoerce(), }); 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 348e8e45..a0b549be 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,7 +1,3 @@ -import type { - AddressWithTokenDtoAdditionalAddresses, - YieldDto, -} from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; import { EitherAsync, Left, Maybe, Right } from "purify-ts"; @@ -9,7 +5,9 @@ import { PAMultiValidatorsRequired, PASingleValidatorRequired, } from "../../../../domain"; +import type { AddressWithTokenDtoAdditionalAddresses } from "../../../../domain/types/addresses"; import { getPositionBalanceDataKey } from "../../../../domain/types/positions"; +import type { Yield } from "../../../../domain/types/yields"; import { getYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity/get-yield-opportunity"; import { getInitParams } from "../../../../hooks/use-init-params"; import { useSKQueryClient } from "../../../../providers/query-client"; @@ -41,8 +39,8 @@ export const usePendingActionDeepLink = () => { ( await EitherAsync.liftEither( Maybe.fromNullable(address).toEither( - new Error("missing wagmi config") - ) + new Error("missing wagmi config"), + ), ).chain((addr) => fn({ isLedgerLive, @@ -51,7 +49,7 @@ export const usePendingActionDeepLink = () => { queryClient, yieldApiFetchClient, externalProviders, - }) + }), ) ).unsafeCoerce(), }); @@ -81,11 +79,11 @@ const fn = ({ const initQueryParams = Maybe.of(val) .filter( ( - v + v, ): v is Override< typeof val, { yieldId: string; pendingaction: string } - > => !!v.yieldId && !!v.pendingaction + > => !!v.yieldId && !!v.pendingaction, ) .toEither(new Error("missing yieldId or pendingaction")); @@ -103,14 +101,14 @@ const fn = ({ body: { address, }, - }) + }), ) .chain((response) => EitherAsync.liftEither( Maybe.fromNullable(response.data).toEither( - new Error("could not get yield balances") - ) - ) + new Error("could not get yield balances"), + ), + ), ) .map((val) => ({ yieldId: initQueryParams.yieldId, @@ -119,7 +117,7 @@ const fn = ({ singleYieldBalances: val.balances, address: address, additionalAddresses: additionalAddresses ?? undefined, - })) + })), ) .chain((data) => EitherAsync.liftEither( @@ -129,14 +127,14 @@ const fn = ({ data.validatorAddress && balance.validator?.address !== data.validatorAddress && !balance.validators?.some( - (validator) => validator.address === data.validatorAddress + (validator) => validator.address === data.validatorAddress, ) ) { continue; } const pendingAction = balance.pendingActions.find( - (pa) => pa.type === data.pendingaction + (pa) => pa.type === data.pendingaction, ); if (pendingAction) { @@ -149,7 +147,7 @@ const fn = ({ } return Left(new Error("no pending action found")); - }) + }), ) .chain((val) => getYieldOpportunity({ @@ -157,20 +155,20 @@ const fn = ({ yieldId: data.yieldId, queryClient, yieldApiFetchClient, - }).map((yieldOp) => ({ ...val, yieldOp })) + }).map((yieldOp) => ({ ...val, yieldOp })), ) .chain< Error, | { type: "positionDetails"; - yieldOp: YieldDto; + yieldOp: Yield; pendingAction: YieldPendingActionDto; balance: YieldBalanceDto; balanceId: string; } | { type: "review"; - yieldOp: YieldDto; + yieldOp: Yield; balance: YieldBalanceDto; balanceId: string; pendingActionDto: GetEitherRight< @@ -181,7 +179,7 @@ const fn = ({ PAMultiValidatorsRequired(val.pendingAction) || PASingleValidatorRequired(val.pendingAction) ? EitherAsync.liftEither( - Right({ type: "positionDetails", ...val }) + Right({ type: "positionDetails", ...val }), ) : EitherAsync.liftEither( preparePendingActionRequestDto({ @@ -193,14 +191,14 @@ const fn = ({ yieldBalance: val.balance, pendingActionDto: val.pendingAction, selectedValidators: [], - }) + }), ).map((res) => ({ yieldOp: val.yieldOp, pendingActionDto: res, type: "review", balanceId: val.balanceId, balance: val.balance, - })) - ) + })), + ), ); }); diff --git a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts index c2c98736..0228fb85 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts @@ -1,9 +1,16 @@ -import type { AddressesDto, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import { Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; +import type { YieldCreateActionDto } from "../../../../domain/types/action"; +import type { AddressesDto } from "../../../../domain/types/addresses"; +import type { ValidatorDto } from "../../../../domain/types/validators"; +import { + getYieldActionArg, + getYieldGasFeeToken, + isYieldIntegrationAggregator, + type Yield, +} from "../../../../domain/types/yields"; import { useSKWallet } from "../../../../providers/sk-wallet"; import { withAdditionalAddresses } from "../../../../providers/yield-api-client-provider/request-helpers"; -import type { YieldCreateActionDto } from "../../../../providers/yield-api-client-provider/types"; import { useEarnPageState } from "./earn-page-state-context"; export const useStakeEnterRequestDto = () => { @@ -26,10 +33,10 @@ export const useStakeEnterRequestDto = () => { selectedToken, }).map<{ addresses: AddressesDto; - gasFeeToken: YieldDto["token"]; + gasFeeToken: Yield["token"]; dto: YieldCreateActionDto; selectedValidators: Map; - selectedStake: YieldDto; + selectedStake: Yield; }>(({ address, selectedStake, selectedToken }) => { const validatorsOrProvider = Just(selectedStake) .chain< @@ -45,19 +52,24 @@ export const useStakeEnterRequestDto = () => { >((val) => { const validators = [...selectedValidators.values()]; - if (val.metadata.isIntegrationAggregator) { + if (isYieldIntegrationAggregator(val)) { return List.head(validators).map((v) => ({ providerId: v.providerId, })); } - if (val.args.enter.args?.validatorAddresses?.required) { + if ( + getYieldActionArg(val, "enter", "validatorAddresses")?.required + ) { return Just({ validatorAddresses: validators.map((v) => v.address), }); } - const subnetIdRequired = - !!selectedStake.args.enter.args?.subnetId?.required; + const subnetIdRequired = !!getYieldActionArg( + selectedStake, + "enter", + "subnetId", + )?.required; return List.head(validators).map((v) => ({ validatorAddress: v.address, @@ -69,7 +81,7 @@ export const useStakeEnterRequestDto = () => { return { selectedValidators, selectedStake: selectedStake, - gasFeeToken: selectedStake.metadata.gasFeeToken, + gasFeeToken: getYieldGasFeeToken(selectedStake), addresses: { address, additionalAddresses: additionalAddresses ?? undefined, @@ -103,6 +115,6 @@ export const useStakeEnterRequestDto = () => { useMaxAmount, tronResource, selectedProviderYieldId, - ] + ], ); }; diff --git a/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts b/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts index c383afe8..2737c7b5 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts @@ -1,8 +1,8 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import type { Maybe } from "purify-ts"; import { useMemo } from "react"; import { tokenString } from "../../../../domain"; +import type { TokenDto } from "../../../../domain/types/tokens"; import { useTokenBalancesMap } from "./use-token-balances-map"; export const useTokenBalance = ({ @@ -15,19 +15,19 @@ export const useTokenBalance = ({ const tokenBalance = useMemo( () => selectedToken.chainNullable((val) => - tokenBalancesMap.get(tokenString(val)) + tokenBalancesMap.get(tokenString(val)), ), - [selectedToken, tokenBalancesMap] + [selectedToken, tokenBalancesMap], ); const availableAmount = useMemo( () => tokenBalance.map((v) => new BigNumber(v.amount)), - [tokenBalance] + [tokenBalance], ); const availableYields = useMemo( () => tokenBalance.map((v) => v.availableYields), - [tokenBalance] + [tokenBalance], ); return { diff --git a/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts b/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts index 18ff759f..3aa9a4bd 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts @@ -1,6 +1,7 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import { useEffect } from "react"; +import type { TokenDto } from "../../../../domain/types/tokens"; +import type { Yield } from "../../../../domain/types/yields"; import { useTrackEvent } from "../../../../hooks/tracking/use-track-event"; export const useTrackStateEvents = ({ @@ -8,7 +9,7 @@ export const useTrackStateEvents = ({ initYield, }: { initToken: Maybe; - initYield: Maybe; + initYield: Maybe; }) => { const trackEvent = useTrackEvent(); @@ -23,7 +24,7 @@ export const useTrackStateEvents = ({ tokenName: v.token.name, tokenAddress: v.token.address, tokenSymbol: v.token.symbol, - }) + }), ); }, [initYield, trackEvent]); @@ -38,7 +39,7 @@ export const useTrackStateEvents = ({ address: v.address, symbol: v.symbol, decimals: v.decimals, - }) + }), ); }, [initToken, trackEvent]); }; diff --git a/packages/widget/src/pages/details/earn-page/state/utils.ts b/packages/widget/src/pages/details/earn-page/state/utils.ts index 7edf87c5..73150a9d 100644 --- a/packages/widget/src/pages/details/earn-page/state/utils.ts +++ b/packages/widget/src/pages/details/earn-page/state/utils.ts @@ -1,14 +1,14 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import { List, Maybe } from "purify-ts"; import type { PositionsData } from "../../../../domain/types/positions"; import { getMinStakeAmount } from "../../../../domain/types/stake"; +import { getYieldActionArg, type Yield } from "../../../../domain/types/yields"; import type { State } from "./types"; export const onYieldSelectState = ({ yieldDto, positionsData, }: { - yieldDto: YieldDto; + yieldDto: Yield; positionsData: PositionsData; }): Pick< State, @@ -22,11 +22,11 @@ export const onYieldSelectState = ({ stakeAmount: getMinStakeAmount(yieldDto, positionsData), selectedValidators: new Map(), tronResource: Maybe.fromFalsy( - yieldDto.args.enter.args?.tronResource?.required + getYieldActionArg(yieldDto, "enter", "tronResource")?.required, ).map(() => "ENERGY"), selectedProviderYieldId: Maybe.fromNullable( - yieldDto.args.enter.args?.providerId + getYieldActionArg(yieldDto, "enter", "providerId"), ) - .filter((val) => val.required) - .chain((val) => List.head(val.options)), + .filter((val) => !!val.required && !!val.options?.length) + .chain((val) => List.head(val.options ?? [])), }); diff --git a/packages/widget/src/pages/details/earn-page/types.ts b/packages/widget/src/pages/details/earn-page/types.ts index 9cc45873..cff6fd89 100644 --- a/packages/widget/src/pages/details/earn-page/types.ts +++ b/packages/widget/src/pages/details/earn-page/types.ts @@ -1,9 +1,8 @@ -import type { YieldDto } from "@stakekit/api-hooks"; -import type { ExtendedYieldType } from "../../../domain/types/yields"; +import type { ExtendedYieldType, Yield } from "../../../domain/types/yields"; export type SelectedStakeData = { - all: YieldDto[]; - filtered: YieldDto[]; + all: Yield[]; + filtered: Yield[]; groupsWithCounts: Map< ExtendedYieldType, { 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..0ddd6296 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 @@ -10,6 +10,7 @@ import { TokenIcon } from "../../../../components/atoms/token-icon"; import { ToolTip } from "../../../../components/atoms/tooltip"; import { Text } from "../../../../components/atoms/typography/text"; import type { PositionDetailsLabelType } from "../../../../domain/types/positions"; +import { getYieldMetadata } from "../../../../domain/types/yields"; import { usePositionListItem } from "../hooks/use-position-list-item"; import type { usePositions } from "../hooks/use-positions"; import { @@ -57,11 +58,11 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), - + , )} - | undefined + | undefined, )} > {t( - `position_details.labels.${label.type as PositionDetailsLabelType}.label` + `position_details.labels.${label.type as PositionDetailsLabelType}.label`, )} @@ -130,7 +131,7 @@ export const PositionsListItem = memo( ? inactiveValidator === "jailed" ? "details.validators_jailed" : "details.validators_inactive" - : "positions.claim_rewards" + : "positions.claim_rewards", )} @@ -151,7 +152,7 @@ export const PositionsListItem = memo( count: Math.max(val.length - 1, 1), })} - )) + )), ) .extractNullable()} @@ -219,10 +220,10 @@ export const PositionsListItem = memo( )} ), - + , )} ); - } + }, ); 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 3ea1c9cb..e38ee33c 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 @@ -3,6 +3,7 @@ import { List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { getBaseToken } from "../../../../domain"; import { getPositionTotalAmount } from "../../../../domain/types/positions"; +import { getYieldRewardType } from "../../../../domain/types/yields"; import { useYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity"; import { useProvidersDetails } from "../../../../hooks/use-provider-details"; import { defaultFormattedNumber } from "../../../../utils"; @@ -10,18 +11,18 @@ import { getRewardRateFormatted } from "../../../../utils/formatters"; import type { usePositions } from "./use-positions"; export const usePositionListItem = ( - item: ReturnType["positionsData"]["data"][number] + item: ReturnType["positionsData"]["data"][number], ) => { const yieldOpportunity = useYieldOpportunity(item.integrationId); const integrationData = useMemo( () => Maybe.fromNullable(yieldOpportunity.data), - [yieldOpportunity.data] + [yieldOpportunity.data], ); const providersDetails = useProvidersDetails({ integrationData, validatorsAddresses: Maybe.of( - item.type === "validators" ? item.validatorsAddresses : [] + item.type === "validators" ? item.validatorsAddresses : [], ), selectedProviderYieldId: Maybe.empty(), }); @@ -34,17 +35,17 @@ export const usePositionListItem = ( rewardRateAverage: val.providersDetails .reduce( (acc, val) => acc.plus(new BigNumber(val.rewardRate || 0)), - new BigNumber(0) + new BigNumber(0), ) .dividedBy(val.providersDetails.length), })) .map((val) => getRewardRateFormatted({ rewardRate: val.rewardRateAverage.toNumber(), - rewardType: val.integrationData.rewardType, - }) + rewardType: getYieldRewardType(val.integrationData), + }), ), - [integrationData, providersDetails] + [integrationData, providersDetails], ); const inactiveValidator = useMemo( @@ -54,31 +55,31 @@ export const usePositionListItem = ( .chainNullable((val) => val.status) .map((v) => v as Exclude) .extractNullable(), - [providersDetails] + [providersDetails], ); const tokenToDisplay = item.token; const baseToken = useMemo( () => integrationData.map((y) => getBaseToken(y)), - [integrationData] + [integrationData], ); const amounts = useMemo( () => baseToken.map((b) => getPositionTotalAmount(item.balancesWithAmount, b)), - [item.balancesWithAmount, baseToken] + [item.balancesWithAmount, baseToken], ); const totalAmount = useMemo(() => amounts.map((v) => v.amount), [amounts]); const totalAmountUsd = useMemo( () => amounts.map((v) => v.amountUsd), - [amounts] + [amounts], ); const totalAmountFormatted = useMemo( () => totalAmount.map(defaultFormattedNumber), - [totalAmount] + [totalAmount], ); return { 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 c1dec633..befb08f9 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,8 +1,8 @@ -import type { YieldBalanceLabelDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { compare, Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { createSelector } from "reselect"; +import type { YieldBalanceLabelDto } from "../../../../domain/types/token-balance"; import { usePositionsData } from "../../../../hooks/use-positions-data"; import { useSettings } from "../../../../providers/settings"; import type { SettingsContextType } from "../../../../providers/settings/types"; @@ -22,7 +22,7 @@ export const usePositions = () => { positionsData: _positionsData.data, variant, }), - [_positionsData.data, variant] + [_positionsData.data, variant], ); const positionsData = { ..._positionsData, data: positionsDataMapped }; @@ -36,7 +36,7 @@ export const usePositions = () => { const listData = useMemo( () => ["header" as const, ...positionsData.data], - [positionsData.data] + [positionsData.data], ); return { @@ -64,8 +64,8 @@ const positionsTableDataSelector = createSelector( value.balances.filter((v) => Just(new BigNumber(v.amount)) .filter((v) => !v.isZero() && !v.isNaN()) - .isJust() - ) + .isJust(), + ), ) .filter((v) => !!v.length) .ifJust((v) => @@ -80,11 +80,11 @@ const positionsTableDataSelector = createSelector( List.sort( (a, b) => compare(priorityOrder[a.type], priorityOrder[b.type]), - value.balances - ) + value.balances, + ), ).map((v) => v.token), actionRequired: v.some( - (b) => b.type === "locked" || b.type === "claimable" + (b) => b.type === "locked" || b.type === "claimable", ), pointsRewardTokenBalances: v .filter((v) => !!v.token.isPoints) @@ -94,10 +94,10 @@ const positionsTableDataSelector = createSelector( })), hasPendingClaimRewards: v.some((balance) => balance.pendingActions.some( - (action) => action.type === "CLAIM_REWARDS" - ) + (action) => action.type === "CLAIM_REWARDS", + ), ), - }) + }), ); }); @@ -116,8 +116,8 @@ const positionsTableDataSelector = createSelector( } & ( | { type: "validators"; validatorsAddresses: string[] } | { type: "default" } - ))[] - ) + ))[], + ), ) .map((val) => variant === "zerion" @@ -126,9 +126,9 @@ const positionsTableDataSelector = createSelector( if (b.hasPendingClaimRewards) return 1; return 0; }) - : val + : val, ) - .unsafeCoerce() + .unsafeCoerce(), ); const priorityOrder: Record = { 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 d6f201ad..3605f234 100644 --- a/packages/widget/src/pages/position-details/components/amount-block.tsx +++ b/packages/widget/src/pages/position-details/components/amount-block.tsx @@ -1,4 +1,3 @@ -import type { TokenDto, ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Just, Maybe } from "purify-ts"; import { useMemo } from "react"; @@ -13,8 +12,10 @@ import { } from "../../../components/atoms/number-input"; import { Text } from "../../../components/atoms/typography/text"; import * as AmountToggle from "../../../components/molecules/amount-toggle"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; +import type { ValidatorDto } from "../../../domain/types/validators"; +import type { Yield } from "../../../domain/types/yields"; 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"; @@ -33,7 +34,7 @@ type AmountBlockProps = { | { variant: "unstake"; unstakeToken: TokenDto | YieldTokenDto; - yieldDto: YieldDto; + yieldDto: Yield; validators: { [Key in keyof Pick< ValidatorDto, @@ -65,7 +66,7 @@ export const AmountBlock = ({ const minMaxUnstakeAmount = Maybe.fromPredicate( (v) => v.variant === "unstake", - rest as Extract + rest as Extract, ) .map( (val) => @@ -73,17 +74,17 @@ export const AmountBlock = ({ val.unstakeMinAmount .map( (v) => - `${t("shared.min")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}` + `${t("shared.min")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}`, ) .extractNullable(), val.unstakeMaxAmount .map( (v) => - `${t("shared.max")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}` + `${t("shared.max")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}`, ) .extractNullable(), val.unstakeIsGreaterOrLessIntegrationLimitError, - ] as const + ] as const, ) .filter((val) => val.some(Boolean)) .map(([min, max, error]) => ( @@ -218,7 +219,7 @@ const UnstakeInfo = ({ yieldDto, unstakeToken, }: { - yieldDto: YieldDto; + yieldDto: Yield; validators: { [Key in keyof Pick]?: ValidatorDto[Key]; }[]; @@ -256,6 +257,6 @@ const UnstakeInfo = ({ )) .extractNullable(), - [withdrawnTime, withdrawnNotAvailable, positionLocked] + [withdrawnTime, withdrawnNotAvailable, positionLocked], ); }; 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 deb8d991..fc58cd43 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,3 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { isPast } from "date-fns"; import { useMemo } from "react"; @@ -6,7 +5,8 @@ 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 type { YieldBalanceDto } from "../../../domain/types/positions"; +import { getYieldMetadata, type Yield } from "../../../domain/types/yields"; import { defaultFormattedNumber } from "../../../utils"; import { formatDurationUntilDate } from "../../../utils/date"; @@ -15,7 +15,7 @@ export const PositionBalances = ({ integrationData, }: { yieldBalance: YieldBalanceDto & { tokenPriceInUsd: BigNumber }; - integrationData: YieldDto; + integrationData: Yield; }) => { const { t } = useTranslation(); @@ -42,7 +42,7 @@ export const PositionBalances = ({ return t("position_details.unstaking_duration", { duration }); }, [yieldBalance.date, yieldBalance.type, t]); - const yieldType = integrationData.metadata.type; + const yieldType = getYieldMetadata(integrationData).type; const balanceTypeContext = yieldType === "vault" || yieldType === "lending" diff --git a/packages/widget/src/pages/position-details/components/provider-details.tsx b/packages/widget/src/pages/position-details/components/provider-details.tsx index e46cf9c4..c572723b 100644 --- a/packages/widget/src/pages/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages/position-details/components/provider-details.tsx @@ -1,4 +1,3 @@ -import type { RewardTypes, YieldDto } from "@stakekit/api-hooks"; import { memo } from "react"; import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; @@ -14,6 +13,8 @@ import { Image } from "../../../components/atoms/image"; import { ImageFallback } from "../../../components/atoms/image-fallback"; import { Text } from "../../../components/atoms/typography/text"; import { useMetaInfo } from "../../../components/molecules/select-validator/meta-info"; +import type { RewardTypes } from "../../../domain/types/reward-rate"; +import type { Yield } from "../../../domain/types/yields"; import type { useProvidersDetails } from "../../../hooks/use-provider-details"; import type { GetMaybeJust } from "../../../types/utils"; import { inactiveContainer, noWrap } from "../styles.css"; @@ -30,7 +31,7 @@ export const ProviderDetails = ({ > & { isFirst: boolean; stakeType: string; - integrationData: YieldDto; + integrationData: Yield; logo: string | undefined; name: string; rewardRateFormatted: string; @@ -96,7 +97,7 @@ export const ProviderDetails = ({ {t( providerDetails.status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive" + : "details.validators_inactive", )} @@ -135,7 +136,7 @@ const ValidatorMeta = memo((props: Parameters[0]) => { {Object.entries(metaInfo) .filter( (val): val is [keyof typeof metaInfo, NonNullable<(typeof val)[1]>] => - !!val[1] + !!val[1], ) .map(([key, val]) => { return ( 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 61ba274d..c4807983 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,10 +1,9 @@ -import type { 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 { isEthenaUsdeStaking, type Yield } from "../../../domain/types/yields"; import type { YieldBalanceDto, YieldPendingActionDto, @@ -21,7 +20,7 @@ type StaticActionBlockProps = { onPendingActionClick: ReturnType< typeof usePositionDetails >["onPendingActionClick"]; - yieldId: YieldDto["id"]; + yieldId: Yield["id"]; }; export const StaticActionBlock = ({ @@ -59,7 +58,7 @@ export const StaticActionBlock = ({ context: isEthenaUsdeStaking(yieldId) ? "ethena_usde" : undefined, - } + }, ), }} components={{ @@ -92,7 +91,7 @@ export const StaticActionBlock = ({ {t( `position_details.pending_action_button.${ pendingActionDto.type.toLowerCase() as Lowercase - }` + }`, )} 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 4c0cee9e..ab62e20a 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,4 +1,3 @@ -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"; @@ -9,6 +8,8 @@ import { PASingleValidatorRequired, } from "../../../domain"; import { isPendingActionAmountRequired } from "../../../domain/types/pending-action"; +import type { ValidatorDto } from "../../../domain/types/validators"; +import type { Yield } from "../../../domain/types/yields"; 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"; @@ -55,7 +56,7 @@ export const usePendingActions = () => { balance.pendingActions.map((pa) => { const amount = Maybe.fromPredicate( (v) => v, - isPendingActionAmountRequired(pa) + isPendingActionAmountRequired(pa), ).chain(() => Maybe.fromNullable( pendingActionsState.get( @@ -63,9 +64,9 @@ export const usePendingActions = () => { balanceType: balance.type, token: balance.token, actionType: pa.type, - }) - ) - ).altLazy(() => Maybe.of(new BigNumber(0))) + }), + ), + ).altLazy(() => Maybe.of(new BigNumber(0))), ); const formattedAmount = Maybe.fromRecord({ @@ -81,7 +82,7 @@ export const usePendingActions = () => { prices: val.prices, pricePerShare: null, baseToken: val.baseToken, - }) + }), ) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""); @@ -91,9 +92,9 @@ export const usePendingActions = () => { pendingActionDto: pa, yieldBalance: balance, }; - }) - ) - ) + }), + ), + ), ), [ pendingActionsState, @@ -101,11 +102,11 @@ export const usePendingActions = () => { positionBalancesByType, reducedStakedOrLiquidBalance, baseToken, - ] + ], ); const onPendingActionAmountChange = ( - data: PendingActionAmountChange["data"] + data: PendingActionAmountChange["data"], ) => { pendingActionDispatch({ type: "pendingAction/amount/change", data }); }; @@ -133,9 +134,9 @@ export const usePendingActions = () => { PAMultiValidatorsRequired(p.pendingActionDto) || PASingleValidatorRequired(p.pendingActionDto) ), - pa - ) - ) + pa, + ), + ), ) .ifJust((val) => { selectValidatorModalShown.current = true; @@ -175,7 +176,7 @@ export const usePendingActions = () => { pendingActionDto, yieldBalance, selectedValidators: [], - }) + }), ); }; @@ -185,7 +186,7 @@ export const usePendingActions = () => { .chain((val) => { if (!validatorAddressesHandling.showValidatorsModal) { return Left( - new Error("missing validatorAddressesHandling.showValidatorsModal") + new Error("missing validatorAddressesHandling.showValidatorsModal"), ); } if (!selectedValidators.length) { @@ -217,7 +218,7 @@ export const usePendingActions = () => { yieldBalance, selectedValidators, }); - } + }, ); }; @@ -234,7 +235,7 @@ export const usePendingActions = () => { yieldBalance, selectedValidators, }: { - integrationData: YieldDto; + integrationData: Yield; 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 64cb3052..9ad6eeb5 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 @@ -1,4 +1,3 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import { useMutation } from "@tanstack/react-query"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; @@ -6,6 +5,8 @@ import { useMemo } from "react"; import { useNavigate } from "react-router"; import { equalTokens } from "../../../domain"; import { isForceMaxAmount } from "../../../domain/types/stake"; +import type { TokenDto } from "../../../domain/types/tokens"; +import { getYieldActionArg } from "../../../domain/types/yields"; import { useYieldValidators } from "../../../hooks/api/use-yield-validators"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useBaseToken } from "../../../hooks/use-base-token"; @@ -44,20 +45,20 @@ export const usePositionDetails = () => { const unstakeMaxAmount = useMemo( () => integrationData - .chainNullable((val) => val.args.exit?.args?.amount) + .chainNullable((val) => getYieldActionArg(val, "exit", "amount")) .filter((val) => !isForceMaxAmount(val)) .chainNullable((val) => val.maximum), - [integrationData] + [integrationData], ); const unstakeMinAmount = useMemo( () => integrationData - .chainNullable((val) => val.args.exit?.args?.amount) + .chainNullable((val) => getYieldActionArg(val, "exit", "amount")) .filter((val) => !isForceMaxAmount(val)) .map(() => minUnstakeAmount.toNumber()) .filter((val) => new BigNumber(val).isGreaterThan(0)), - [integrationData, minUnstakeAmount] + [integrationData, minUnstakeAmount], ); const onClickHandler = useMutation({ @@ -121,10 +122,10 @@ export const usePositionDetails = () => { positionBalances.data .map((balanceData) => balanceData.rewardRate) .extractNullable(), - [positionBalances.data] + [positionBalances.data], ); - const canUnstake = integrationData.filter((d) => !!d.args.exit).isJust(); + const canUnstake = integrationData.filter((d) => !!d.status.exit).isJust(); const onUnstakeAmountChange = (value: BigNumber) => dispatch({ type: "unstake/amount/change", data: value }); @@ -134,7 +135,7 @@ export const usePositionDetails = () => { reducedStakedOrLiquidBalance .map((val) => val.amountUsd) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""), - [reducedStakedOrLiquidBalance] + [reducedStakedOrLiquidBalance], ); const onMaxClick = () => { @@ -147,7 +148,7 @@ export const usePositionDetails = () => { const unstakeAvailable = integrationData.mapOrDefault( (d) => d.status.exit, - false + false, ); const { @@ -171,21 +172,21 @@ export const usePositionDetails = () => { (yb) => !yb.token.isPoints && !!yb.validator?.pricePerShare && - !equalTokens(yb.token, v.baseToken) + !equalTokens(yb.token, v.baseToken), ) .forEach((yb) => { acc.set( yb.token.symbol, `1 ${yb.token.symbol} = ${defaultFormattedNumber( - new BigNumber(yb.validator?.pricePerShare ?? 0) - )} ${v.baseToken.symbol}` + new BigNumber(yb.validator?.pricePerShare ?? 0), + )} ${v.baseToken.symbol}`, ); }); return acc; - }, new Map()) + }, new Map()), ), - [integrationData, positionBalancesByType, baseToken] + [integrationData, positionBalancesByType, baseToken], ); const unstakeDisabled = yieldOpportunity.isLoading || !unstakeAvailable; 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 a3615b46..42d7b5a0 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 @@ -1,9 +1,15 @@ -import type { AddressesDto, YieldDto } from "@stakekit/api-hooks"; import { Just, List, Maybe } from "purify-ts"; import { useMemo } from "react"; +import type { YieldCreateActionDto } from "../../../domain/types/action"; +import type { AddressesDto } from "../../../domain/types/addresses"; +import { + getYieldActionArg, + getYieldGasFeeToken, + isYieldIntegrationAggregator, + type Yield, +} from "../../../domain/types/yields"; import { useSKWallet } from "../../../providers/sk-wallet"; import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; -import type { YieldCreateActionDto } from "../../../providers/yield-api-client-provider/types"; import { useUnstakeOrPendingActionState } from "../state"; export const useStakeExitRequestDto = () => { @@ -23,7 +29,7 @@ export const useStakeExitRequestDto = () => { stakedOrLiquidBalances, }).map<{ addresses: AddressesDto; - gasFeeToken: YieldDto["token"]; + gasFeeToken: Yield["token"]; dto: YieldCreateActionDto; }>((val) => { const validatorsOrProvider = Just(null) @@ -38,35 +44,41 @@ export const useStakeExitRequestDto = () => { > | Pick, "providerId"> >(() => { - if (val.integrationData.metadata.isIntegrationAggregator) { + if (isYieldIntegrationAggregator(val.integrationData)) { return List.find( (b) => !!b.validator?.providerId, - val.stakedOrLiquidBalances + val.stakedOrLiquidBalances, ).map((b) => ({ providerId: b.validator?.providerId, validatorAddress: b.validator?.address, })); } if ( - val.integrationData.args.exit?.args?.validatorAddresses?.required + getYieldActionArg( + val.integrationData, + "exit", + "validatorAddresses", + )?.required ) { return List.find( (b) => !!b.validators?.length, - val.stakedOrLiquidBalances + val.stakedOrLiquidBalances, ).map((b) => ({ validatorAddresses: b.validators?.map((validator) => validator.address) ?? [], })); } if ( - val.integrationData.args.exit?.args?.validatorAddress?.required + getYieldActionArg(val.integrationData, "exit", "validatorAddress") + ?.required ) { return List.find( (b) => !!b.validator?.address, - val.stakedOrLiquidBalances + val.stakedOrLiquidBalances, ).map((b) => { const subnetId = Maybe.fromNullable( - val.integrationData.args.exit?.args?.subnetId?.required + getYieldActionArg(val.integrationData, "exit", "subnetId") + ?.required, ) .chainNullable(() => b.validator) .map((validator) => validator.subnetId) @@ -84,7 +96,7 @@ export const useStakeExitRequestDto = () => { .orDefault({}); return { - gasFeeToken: val.integrationData.metadata.gasFeeToken, + gasFeeToken: getYieldGasFeeToken(val.integrationData), addresses: { address: val.address, additionalAddresses: additionalAddresses ?? undefined, @@ -110,6 +122,6 @@ export const useStakeExitRequestDto = () => { integrationData, unstakeAmount, unstakeUseMaxAmount, - ] + ], ); }; diff --git a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts index ee6a412e..bba8f966 100644 --- a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts +++ b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts @@ -1,4 +1,3 @@ -import type { TransactionVerificationMessageDto } from "@stakekit/api-hooks"; import { transactionGetTransactionVerificationMessageForNetwork } from "@stakekit/api-hooks"; import { useMachine } from "@xstate/react"; import type { SnapshotFromStore } from "@xstate/store"; @@ -7,6 +6,7 @@ import { EitherAsync, Maybe } from "purify-ts"; import { type RefObject, useState } from "react"; import { assign, setup } from "xstate"; import { getValidStakeSessionTx } from "../../../domain"; +import type { TransactionVerificationMessageDto } from "../../../domain/types/transaction"; import type { SKWallet } from "../../../domain/types/wallet"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useSavedRef } from "../../../hooks/use-saved-ref"; @@ -22,7 +22,7 @@ export const useUnstakeMachine = ({ onDone }: { onDone: () => void }) => { const exitStore = useExitStakeStore(); const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const yieldApiFetchClient = useYieldApiFetchClient(); @@ -74,7 +74,7 @@ const getMachine = ( } >; }> - > + >, ) => setup({ types: { @@ -140,8 +140,8 @@ const getMachine = ( }); if ( - val.integrationData.args.exit?.args?.signatureVerification - ?.required + val.integrationData.__fallback__.args.exit?.args + ?.signatureVerification?.required ) { self.send({ type: "__GET_VERIFICATION_MESSAGE__", val }); } else { @@ -174,7 +174,7 @@ const getMachine = ( loading: { entry: ({ self, context }) => EitherAsync.liftEither( - context.data.toEither(new Error("Missing init values")) + context.data.toEither(new Error("Missing init values")), ) .chain((val) => EitherAsync(() => @@ -186,11 +186,11 @@ const getMachine = ( additionalAddresses: val.addresses.additionalAddresses ?? undefined, }, - } - ) + }, + ), ).mapLeft( - () => new Error("Failed to get verification message") - ) + () => new Error("Failed to get verification message"), + ), ) .caseOf({ Right(v) { @@ -242,8 +242,8 @@ const getMachine = ( entry: ({ self, context }) => EitherAsync.liftEither( context.transactionVerificationMessageDto.toEither( - new Error("Missing transaction verification message") - ) + new Error("Missing transaction verification message"), + ), ) .chain((val) => ref.current.signMessage(val.message)) .caseOf({ @@ -306,11 +306,11 @@ const getMachine = ( }, } as typeof val.requestDto.arguments, }, - }) as typeof val.data + }) as typeof val.data, ) - .orDefault(val) + .orDefault(val), ) - .toEither(new Error("Missing params")) + .toEither(new Error("Missing params")), ) .chain((val) => EitherAsync(() => @@ -319,18 +319,18 @@ const getMachine = ( fetchClient: ref.current.yieldApiFetchClient, requestDto: val.requestDto, yieldDto: val.integrationData, - }) + }), ) .mapLeft(() => new Error("Stake exit error")) .chain((actionDto) => - EitherAsync.liftEither(getValidStakeSessionTx(actionDto)) + EitherAsync.liftEither(getValidStakeSessionTx(actionDto)), ) .ifRight((result) => ref.current.exitStore.send({ type: "setActionDto", data: result, - }) - ) + }), + ), ) .caseOf({ Right() { 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 f75e4a2c..4d0b2a7c 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,7 +1,7 @@ -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 { ValidatorDto } from "../../../domain/types/validators"; import type { YieldBalanceDto, YieldPendingActionDto, @@ -93,7 +93,7 @@ const reducer = (state: State, action: Actions): State => { return { ...state, multiSelect: isPendingActionValidatorAddressesRequired( - action.data.pendingActionDto + action.data.pendingActionDto, ), selectedValidators: newSelectedValidators, pendingActionDto: action.data.pendingActionDto, @@ -120,7 +120,7 @@ export const useValidatorAddressesHandling = () => { const closeModal = useCallback( () => dispatch({ type: "validator/close" }), - [] + [], ); const openModal = useCallback( @@ -128,13 +128,13 @@ export const useValidatorAddressesHandling = () => { yieldBalance: YieldBalanceDto; pendingActionDto: YieldPendingActionDto; }) => dispatch({ type: "validator/open", data: args }), - [] + [], ); const onItemClick = useCallback( (validator: ValidatorDto["address"]) => dispatch({ type: "validator/multiselect", data: validator }), - [] + [], ); const modalState: SelectModalProps["state"] = useMemo( @@ -142,7 +142,7 @@ export const useValidatorAddressesHandling = () => { isOpen: state.showValidatorsModal, setOpen: (value) => !value && closeModal(), }), - [closeModal, state.showValidatorsModal] + [closeModal, state.showValidatorsModal], ); return { diff --git a/packages/widget/src/pages/position-details/hooks/utils.ts b/packages/widget/src/pages/position-details/hooks/utils.ts index 08609efc..3140de40 100644 --- a/packages/widget/src/pages/position-details/hooks/utils.ts +++ b/packages/widget/src/pages/position-details/hooks/utils.ts @@ -1,22 +1,19 @@ -import type { - PendingActionDto as LegacyPendingActionDto, - YieldBalanceDto as LegacyYieldBalanceDto, - ValidatorDto, - YieldDto, -} from "@stakekit/api-hooks"; import type { Either } from "purify-ts"; import { List, Maybe } from "purify-ts"; +import type { YieldCreateManageActionDto } from "../../../domain/types/action"; import { type AnyPendingActionDto, isPendingActionAmountRequired, isPendingActionValidatorAddressesRequired, isPendingActionValidatorAddressRequired, + type YieldPendingActionType, } from "../../../domain/types/pending-action"; +import type { ValidatorDto } from "../../../domain/types/validators"; import type { SKWallet } from "../../../domain/types/wallet"; +import { getYieldGasFeeToken, type Yield } from "../../../domain/types/yields"; import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; import type { YieldBalanceDto, - YieldCreateManageActionDto, YieldTokenDto, } from "../../../providers/yield-api-client-provider/types"; import type { State } from "../state/types"; @@ -25,7 +22,7 @@ import { getBalanceTokenActionType } from "../state/utils"; type AnyYieldBalanceDto = { amount: string; token: YieldTokenDto; - type: LegacyYieldBalanceDto["type"] | YieldBalanceDto["type"]; + type: YieldBalanceDto["type"]; }; export const preparePendingActionRequestDto = ({ @@ -42,14 +39,14 @@ export const preparePendingActionRequestDto = ({ additionalAddresses: SKWallet["additionalAddresses"]; pendingActionDto: AnyPendingActionDto; yieldBalance: AnyYieldBalanceDto; - integration: YieldDto; + integration: Yield; selectedValidators: ValidatorDto["address"][]; }): Either< Error, { requestDto: YieldCreateManageActionDto; - integrationData: YieldDto; - gasFeeToken: YieldDto["token"]; + integrationData: Yield; + gasFeeToken: Yield["token"]; address: NonNullable; additionalAddresses: | NonNullable @@ -62,17 +59,16 @@ export const preparePendingActionRequestDto = ({ const args: NonNullable = { amount: Maybe.fromPredicate( Boolean, - isPendingActionAmountRequired(pendingActionDto) + isPendingActionAmountRequired(pendingActionDto), ) .chainNullable(() => pendingActionsState.get( getBalanceTokenActionType({ balanceType: yieldBalance.type as YieldBalanceDto["type"], token: yieldBalance.token, - actionType: - pendingActionDto.type as LegacyPendingActionDto["type"], - }) - ) + actionType: pendingActionDto.type as YieldPendingActionType, + }), + ), ) .map((v) => v.toString()) .alt(Maybe.of(yieldBalance.amount)) @@ -89,7 +85,7 @@ export const preparePendingActionRequestDto = ({ return { requestDto: { - action: pendingActionDto.type as LegacyPendingActionDto["type"], + action: pendingActionDto.type as YieldPendingActionType, address: val, arguments: withAdditionalAddresses({ additionalAddresses, @@ -100,7 +96,7 @@ export const preparePendingActionRequestDto = ({ }, address: val, additionalAddresses: additionalAddresses ?? undefined, - gasFeeToken: integration.metadata.gasFeeToken, + gasFeeToken: getYieldGasFeeToken(integration), integrationData: integration, }; }); 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 4b732070..add14972 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -9,6 +9,7 @@ import { Text } from "../../components/atoms/typography/text"; import { RewardRateBreakdown } from "../../components/molecules/reward-rate-breakdown"; import { SelectValidator } from "../../components/molecules/select-validator"; import { getRewardTypeFromRateType } from "../../domain/types/reward-rate"; +import { getYieldMetadata } from "../../domain/types/yields"; import { useTrackPage } from "../../hooks/tracking/use-track-page"; import { AnimationPage } from "../../navigation/containers/animation-page"; import type { YieldPendingActionType } from "../../providers/yield-api-client-provider/types"; @@ -90,7 +91,11 @@ const PositionDetails = () => { alignItems="center" > [0]["metadata"] + } token={t} tokenLogoHw="14" /> @@ -130,7 +135,7 @@ const PositionDetails = () => { {getRewardRateFormatted({ rewardRate: personalizedRewardRate.total, rewardType: getRewardTypeFromRateType( - personalizedRewardRate.rateType + personalizedRewardRate.rateType, ), })} @@ -159,11 +164,11 @@ const PositionDetails = () => { personalizedRewardRate ? undefined : p.rewardType } stakeType={t( - `position_details.stake_type.${integrationData.metadata.type}` + `position_details.stake_type.${getYieldMetadata(integrationData).type}`, )} integrationData={integrationData} /> - )) + )), ) .extractNullable()} @@ -177,7 +182,7 @@ const PositionDetails = () => { integrationData={integrationData} yieldBalance={yb} /> - )) + )), )} {liquidTokensToNativeConversion @@ -236,7 +241,7 @@ const PositionDetails = () => { label={t( `position_details.pending_action_button.${ val.pendingActionDto.type.toLowerCase() as Lowercase - }` + }`, )} onMaxClick={null} formattedAmount={val.formattedAmount} @@ -249,8 +254,8 @@ const PositionDetails = () => { onPendingActionClick={onPendingActionClick} yieldId={integrationData.id} /> - ) - ) + ), + ), ) .extractNullable()} {/* Unstake */} @@ -282,14 +287,14 @@ const PositionDetails = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${integrationData.metadata.type}` + `position_details.unstake_label.${getYieldMetadata(integrationData).type}`, )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} yieldDto={integrationData} validators={providersDetails.orDefault([])} /> - ) + ), ) .extractNullable()} diff --git a/packages/widget/src/pages/position-details/state/index.tsx b/packages/widget/src/pages/position-details/state/index.tsx index bbd7d34c..9363ced9 100644 --- a/packages/widget/src/pages/position-details/state/index.tsx +++ b/packages/widget/src/pages/position-details/state/index.tsx @@ -11,7 +11,7 @@ import { } from "react"; import { config } from "../../../config"; import { getPendingActionAmountConfig } from "../../../domain/types/pending-action"; -import { isERC4626 } from "../../../domain/types/yields"; +import { getYieldActionArg, isERC4626 } from "../../../domain/types/yields"; import { usePrices } from "../../../hooks/api/use-prices"; import { useYieldOpportunity } from "../../../hooks/api/use-yield-opportunity"; import { useUnstakeOrPendingActionParams } from "../../../hooks/navigation/use-unstake-or-pending-action-params"; @@ -54,7 +54,7 @@ export const UnstakeOrPendingActionProvider = ({ const integrationData = useMemo( () => Maybe.fromNullable(yieldOpportunity.data), - [yieldOpportunity.data] + [yieldOpportunity.data], ); const baseToken = useBaseToken(integrationData); @@ -96,8 +96,8 @@ export const UnstakeOrPendingActionProvider = ({ ], })) .extractNullable(), - [positionBalances, baseToken] - ) + [positionBalances, baseToken], + ), ); /** @@ -108,7 +108,7 @@ export const UnstakeOrPendingActionProvider = ({ }); const stakedOrLiquidBalances = useStakedOrLiquidBalance( - positionBalancesByType + positionBalancesByType, ); const reducedStakedOrLiquidBalance = useMemo( @@ -118,7 +118,7 @@ export const UnstakeOrPendingActionProvider = ({ (acc, next) => { acc.amount = acc.amount.plus(new BigNumber(next.amount)); acc.amountUsd = acc.amountUsd.plus( - new BigNumber(next.amountUsd ?? 0) + new BigNumber(next.amountUsd ?? 0), ); acc.token = next.token; @@ -128,10 +128,10 @@ export const UnstakeOrPendingActionProvider = ({ amountUsd: new BigNumber(0), amount: new BigNumber(0), token: b[0].token, - } - ) + }, + ), ), - [stakedOrLiquidBalances] + [stakedOrLiquidBalances], ); const unstakeToken = useMemo( @@ -139,7 +139,7 @@ export const UnstakeOrPendingActionProvider = ({ stakedOrLiquidBalances .chain((balances) => List.head(balances)) .map((v) => v.token), - [stakedOrLiquidBalances] + [stakedOrLiquidBalances], ); const { @@ -156,7 +156,10 @@ export const UnstakeOrPendingActionProvider = ({ const canChangeUnstakeAmount = integrationData.map( (d) => - !!(!isForceMax && (d.args.exit?.args?.amount?.required || isERC4626(d))) + !!( + !isForceMax && + (getYieldActionArg(d, "exit", "amount")?.required || isERC4626(d)) + ), ); const positionBalancesByTypePendingActions = useMemo( @@ -178,14 +181,14 @@ export const UnstakeOrPendingActionProvider = ({ actionType: p.type, }), { pendingAction: p, balance: b }, - ] as const - ) - ) - ) + ] as const, + ), + ), + ), ) - .orDefault([]) + .orDefault([]), ), - [positionBalancesByType] + [positionBalancesByType], ); const getCorrectPendingActionAmount = ({ @@ -204,14 +207,14 @@ export const UnstakeOrPendingActionProvider = ({ const key = getBalanceTokenActionType({ actionType, balanceType, token }); return Maybe.fromNullable( - positionBalancesByTypePendingActions.get(key) + positionBalancesByTypePendingActions.get(key), ).mapOrDefault((val) => { const newMap = new Map(state); newMap.set(key, amount); const amountConfig = getPendingActionAmountConfig(val.pendingAction); const max = new BigNumber( - amountConfig?.maximum ?? Number.POSITIVE_INFINITY + amountConfig?.maximum ?? Number.POSITIVE_INFINITY, ); const min = new BigNumber(amountConfig?.minimum ?? 0); @@ -294,7 +297,7 @@ export const UnstakeOrPendingActionProvider = ({ canChangeUnstakeAmount, isForceMax, reducedStakedOrLiquidBalance, - ] + ], ); const unstakeAmountValid = useMemo( @@ -302,17 +305,17 @@ export const UnstakeOrPendingActionProvider = ({ unstakeAmount.isGreaterThanOrEqualTo(minEnterOrExitAmount) && unstakeAmount.isLessThanOrEqualTo(maxEnterOrExitAmount) && !unstakeAmount.isZero(), - [maxEnterOrExitAmount, minEnterOrExitAmount, unstakeAmount] + [maxEnterOrExitAmount, minEnterOrExitAmount, unstakeAmount], ); const unstakeIsGreaterThanMax = useMemo( () => unstakeAmount.isGreaterThan(maxEnterOrExitAmount), - [unstakeAmount, maxEnterOrExitAmount] + [unstakeAmount, maxEnterOrExitAmount], ); const unstakeIsLessThanMin = useMemo( () => unstakeAmount.isLessThan(minEnterOrExitAmount), - [unstakeAmount, minEnterOrExitAmount] + [unstakeAmount, minEnterOrExitAmount], ); const unstakeIsGreaterOrLessIntegrationLimitError = useMemo( @@ -320,7 +323,7 @@ export const UnstakeOrPendingActionProvider = ({ maxIntegrationAmount .map((v) => unstakeAmount.isGreaterThan(v)) .orDefault(false) || unstakeIsLessThanMin, - [unstakeAmount, unstakeIsLessThanMin, maxIntegrationAmount] + [unstakeAmount, unstakeIsLessThanMin, maxIntegrationAmount], ); const unstakeAmountError = useMemo( @@ -333,7 +336,7 @@ export const UnstakeOrPendingActionProvider = ({ unstakeIsLessThanMin, unstakeIsGreaterThanMax, unstakeIsGreaterOrLessIntegrationLimitError, - ] + ], ); const value: State & ExtraData = useMemo( @@ -374,7 +377,7 @@ export const UnstakeOrPendingActionProvider = ({ pendingActionType, unstakeIsGreaterOrLessIntegrationLimitError, minEnterOrExitAmount, - ] + ], ); return ( @@ -390,7 +393,7 @@ export const useUnstakeOrPendingActionState = () => { const state = useContext(UnstakeOrPendingActionContext); if (state === undefined) { throw new Error( - "useUnstakeOrPendingActionState must be used within a UnstakeContextProvider" + "useUnstakeOrPendingActionState must be used within a UnstakeContextProvider", ); } @@ -401,7 +404,7 @@ export const useUnstakeOrPendingActionDispatch = () => { const dispatch = useContext(UnstakeOrPendingActionDispatchContext); if (dispatch === undefined) { throw new Error( - "useUnstakeOrPendingActionDispatch must be used within a UnstakeContextProvider" + "useUnstakeOrPendingActionDispatch must be used within a UnstakeContextProvider", ); } diff --git a/packages/widget/src/pages/position-details/state/types.ts b/packages/widget/src/pages/position-details/state/types.ts index 91da291d..0fcbec22 100644 --- a/packages/widget/src/pages/position-details/state/types.ts +++ b/packages/widget/src/pages/position-details/state/types.ts @@ -1,18 +1,21 @@ -import type { 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"; +import type { YieldPendingActionType } from "../../../domain/types/pending-action"; +import type { + PositionBalancesByType, + YieldBalanceType, +} from "../../../domain/types/positions"; import type { Prices } from "../../../domain/types/price"; -import type { TokenString } from "../../../domain/types/tokens"; +import type { + TokenDto, + TokenString, + YieldTokenDto, +} from "../../../domain/types/tokens"; +import type { Yield } from "../../../domain/types/yields"; 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, - YieldPendingActionType, - YieldTokenDto, -} from "../../../providers/yield-api-client-provider/types"; import type { Action } from "../../../types/utils"; type UnstakeAmountChange = Action<"unstake/amount/change", BigNumber>; @@ -44,7 +47,7 @@ export type State = { export type ExtraData = { pendingActionType: Maybe; - integrationData: Maybe; + integrationData: Maybe; positionBalances: ReturnType; yieldOpportunity: ReturnType; positionBalancesByType: Maybe; diff --git a/packages/widget/src/pages/position-details/state/utils.ts b/packages/widget/src/pages/position-details/state/utils.ts index bec195ba..91208f70 100644 --- a/packages/widget/src/pages/position-details/state/utils.ts +++ b/packages/widget/src/pages/position-details/state/utils.ts @@ -1,5 +1,5 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import { tokenString } from "../../../domain"; +import type { TokenDto } from "../../../domain/types/tokens"; import type { YieldBalanceType, YieldPendingActionType, diff --git a/packages/widget/src/pages/review/hooks/use-action-review.hook.ts b/packages/widget/src/pages/review/hooks/use-action-review.hook.ts index 96c1b7e3..77a49004 100644 --- a/packages/widget/src/pages/review/hooks/use-action-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-action-review.hook.ts @@ -1,13 +1,16 @@ -import { - ActionTypes, - type TokenDto, - TransactionStatus, -} from "@stakekit/api-hooks"; import { useSelector } from "@xstate/store/react"; import { List, Maybe } from "purify-ts"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; +import { + type ActionType, + ActionTypes, + getActionInputToken, + TransactionStatus, +} from "../../../domain/types/action"; +import type { TokenDto } from "../../../domain/types/tokens"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useYieldType } from "../../../hooks/use-yield-type"; import { useActivityContext } from "../../../providers/activity-provider"; @@ -26,25 +29,33 @@ export const useActionReview = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction + (state) => state.context.selectedAction, ).unsafeCoerce(); - const inputToken = useMemo( - () => Maybe.of(selectedAction.inputToken), - [selectedAction] - ) as Maybe; - const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield + (state) => state.context.selectedYield, ).unsafeCoerce(); + const inputToken = useMemo( + () => + Maybe.fromNullable( + getActionInputToken({ + actionDto: selectedAction, + yieldDto: selectedYield, + }), + ), + [selectedAction, selectedYield], + ) as Maybe; + const transactions = useMemo( () => Maybe.fromNullable(selectedAction) .map((a) => a.transactions) - .map((tx) => tx.sort((a, b) => a.stepIndex - b.stepIndex)), - [selectedAction] + .map((tx) => + tx.sort((a, b) => (a.stepIndex ?? 0) - (b.stepIndex ?? 0)), + ), + [selectedAction], ); const onViewTransactionClick = (url: string) => @@ -54,11 +65,11 @@ export const useActionReview = () => { const stakeTitle = useYieldType(Maybe.of(selectedYield)).mapOrDefault( (y) => y.review, - "" + "", ); const unstakeTitle = useMemo(() => { - switch (selectedYield.metadata.type) { + switch (getYieldMetadata(selectedYield).type) { case "staking": case "liquid-staking": return t("position_details.unstake") as string; @@ -72,10 +83,10 @@ export const useActionReview = () => { () => t( `position_details.pending_action_button.${ - selectedAction.type.toLowerCase() as Lowercase - }` as const - ), - [selectedAction.type, t] + selectedAction.type.toLowerCase() as Lowercase + }` as never, + ) as string, + [selectedAction.type, t], ); const title = useMemo( @@ -85,7 +96,7 @@ export const useActionReview = () => { : selectedAction.type === ActionTypes.UNSTAKE ? unstakeTitle : pendingActionTitle, - [selectedAction, stakeTitle, unstakeTitle, pendingActionTitle] + [selectedAction, stakeTitle, unstakeTitle, pendingActionTitle], ); const amount = useMemo( @@ -93,7 +104,7 @@ export const useActionReview = () => { Maybe.fromNullable(selectedAction.amount) .map(defaultFormattedNumber) .extractNullable(), - [selectedAction] + [selectedAction], ); const path = useMemo( @@ -103,7 +114,7 @@ export const useActionReview = () => { : selectedAction.type === ActionTypes.STAKE ? "stake" : "pending", - [selectedAction] + [selectedAction], ); const labelKey: LabelKey = useMemo( @@ -112,16 +123,16 @@ export const useActionReview = () => { .chain((txs) => List.find( (tx) => tx.status === TransactionStatus.WAITING_FOR_SIGNATURE, - txs + txs, ).chain((tx) => - List.findIndex((i) => i === tx, txs) + List.indexOf(tx, txs) .chainNullable((index) => txs[index - 1]) .filter((prevTx) => prevTx.status === TransactionStatus.CONFIRMED) - .map(() => "continue" as LabelKey) - ) + .map(() => "continue" as LabelKey), + ), ) .orDefault("retry"), - [transactions] + [transactions], ); const actionOlderThan7Days = useMemo( @@ -129,7 +140,7 @@ export const useActionReview = () => { Maybe.of(selectedAction.createdAt) .map(dateOlderThen7Days) .orDefault(false), - [selectedAction] + [selectedAction], ); useRegisterFooterButton( @@ -141,8 +152,8 @@ export const useActionReview = () => { isLoading: false, hide: actionOlderThan7Days, }), - [navigate, path, labelKey, actionOlderThan7Days, t] - ) + [navigate, path, labelKey, actionOlderThan7Days, t], + ), ); return { diff --git a/packages/widget/src/pages/review/hooks/use-fees.ts b/packages/widget/src/pages/review/hooks/use-fees.ts index f77e3988..36963209 100644 --- a/packages/widget/src/pages/review/hooks/use-fees.ts +++ b/packages/widget/src/pages/review/hooks/use-fees.ts @@ -1,10 +1,10 @@ -import type { FeeConfigurationDto, TokenDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { Just, type Maybe } from "purify-ts"; import { useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import type { FeeConfigurationDto } from "../../../domain/types/fees"; import type { Prices } from "../../../domain/types/price"; -import type { YieldTokenDto } from "../../../providers/yield-api-client-provider/types"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import { bpsToAmount, bpsToPercentage } from "../../../utils"; import { getFeesInUSD } from "../../../utils/formatters"; import type { FeesBps } from "../types"; @@ -39,17 +39,17 @@ export const useFees = ({ prices, token, }), - [amount, token, prices] + [amount, token, prices], ); const getBpsInPercentage = useCallback( (val: number) => `${bpsToPercentage(val)}%`, - [] + [], ); const getPercentAmount = useCallback( (val: string) => amount.multipliedBy(val).dividedBy(100), - [amount] + [amount], ); const getPercentInUsd = useCallback( @@ -59,7 +59,7 @@ export const useFees = ({ prices, token, }), - [getPercentAmount, prices, token] + [getPercentAmount, prices, token], ); const depositFee = useMemo( @@ -80,7 +80,7 @@ export const useFees = ({ inPercentage: `${val}%`, explanation: t("review.deposit_fee_explanation"), label: t("review.deposit_fee"), - })) + })), ), [ feeConfigDto, @@ -89,7 +89,7 @@ export const useFees = ({ getPercentInUsd, t, yieldFee, - ] + ], ); const managementFee = useMemo( @@ -110,7 +110,7 @@ export const useFees = ({ inPercentage: `${val}%`, explanation: t("review.management_fee_explanation"), label: t("review.management_fee"), - })) + })), ), [ feeConfigDto, @@ -119,7 +119,7 @@ export const useFees = ({ getPercentInUsd, t, yieldFee, - ] + ], ); const performanceFee = useMemo( @@ -140,7 +140,7 @@ export const useFees = ({ inPercentage: `${val}%`, explanation: t("review.performance_fee_explanation"), label: t("review.performance_fee"), - })) + })), ), [ feeConfigDto, @@ -149,7 +149,7 @@ export const useFees = ({ getPercentInUsd, t, yieldFee, - ] + ], ); return { depositFee, managementFee, performanceFee }; diff --git a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts index 415ebd80..15889223 100644 --- a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts @@ -7,6 +7,7 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import type { RewardTokenDetails } from "../../../components/molecules/reward-token-details"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; import { getRewardTokenSymbols } from "../../../hooks/use-reward-token-details/get-reward-token-symbols"; @@ -26,7 +27,7 @@ export const usePendingActionReview = () => { const pendingRequest = useSelector( pendingActionStore, - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const actionPreviewQuery = useQuery({ @@ -47,29 +48,28 @@ export const usePendingActionReview = () => { Maybe.fromNullable(actionPreviewQuery.data) .map((actionDto) => actionDto.transactions.reduce( - (acc, transaction) => - acc.plus(transaction.gasEstimate?.amount ?? 0), - new BigNumber(0) - ) + (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), + new BigNumber(0), + ), ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), - [actionPreviewQuery.data] + [actionPreviewQuery.data], ); const amount = useMemo( () => new BigNumber(pendingRequest.requestDto.arguments?.amount ?? 0), - [pendingRequest.requestDto.arguments?.amount] + [pendingRequest.requestDto.arguments?.amount], ); const interactedToken = useMemo( () => Maybe.of(pendingRequest.interactedToken), - [pendingRequest.interactedToken] + [pendingRequest.interactedToken], ); const integrationData = useMemo( () => Maybe.of(pendingRequest.integrationData), - [pendingRequest.integrationData] + [pendingRequest.integrationData], ); const pricesState = useTokensPrices({ @@ -93,10 +93,10 @@ export const usePendingActionReview = () => { t( `position_details.pending_action_button.${ pendingRequest.requestDto.action.toLowerCase() as Lowercase - }` as const - ) + }` as const, + ), ), - [pendingRequest.requestDto.action, t] + [pendingRequest.requestDto.action, t], ); const navigate = useNavigate(); @@ -108,7 +108,7 @@ export const usePendingActionReview = () => { prices: Maybe.fromNullable(pricesState.data), yieldDto: integrationData, }), - [integrationData, pendingTxGas, pricesState.data] + [integrationData, pendingTxGas, pricesState.data], ); const actionPendingMutation = useMutation({ @@ -127,11 +127,11 @@ export const usePendingActionReview = () => { const rewardTokenDetailsProps = useMemo( () => integrationData - .chainNullable((v) => - v.metadata.provider - ? { provider: v.metadata.provider, rest: v } - : null - ) + .chainNullable((v) => { + const provider = getYieldProviderDetails(v); + + return provider ? { provider, rest: v } : null; + }) .map((v) => { const rewardToken = Maybe.of({ logoUri: v.provider.logoURI, @@ -146,7 +146,7 @@ export const usePendingActionReview = () => { rewardToken, } satisfies ComponentProps; }), - [integrationData, pendingRequest.requestDto.action] + [integrationData, pendingRequest.requestDto.action], ); const onClickRef = useSavedRef(onClick); @@ -159,8 +159,8 @@ export const usePendingActionReview = () => { disabled: false, isLoading: actionPendingMutation.isPending, }), - [onClickRef, t, actionPendingMutation.isPending] - ) + [onClickRef, t, actionPendingMutation.isPending], + ), ); const metaInfo: MetaInfoProps = useMemo(() => ({ showMetaInfo: false }), []); diff --git a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts index da7f9d2e..d98ef0eb 100644 --- a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts @@ -5,6 +5,7 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useEstimatedRewards } from "../../../hooks/use-estimated-rewards"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; @@ -26,14 +27,14 @@ export const useStakeReview = () => { const enterRequest = useSelector( enterStore, - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const yieldApiFetchClient = useYieldApiFetchClient(); const stakeAmount = useMemo( () => new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0), - [enterRequest] + [enterRequest], ); const actionPreviewQuery = useQuery({ @@ -44,7 +45,6 @@ export const useStakeReview = () => { createEnterAction({ addresses: enterRequest.addresses, fetchClient: yieldApiFetchClient, - inputToken: enterRequest.selectedToken, requestDto: enterRequest.requestDto, yieldDto: enterRequest.selectedStake, }), @@ -55,14 +55,13 @@ export const useStakeReview = () => { Maybe.fromNullable(actionPreviewQuery.data) .map((actionDto) => actionDto.transactions.reduce( - (acc, transaction) => - acc.plus(transaction.gasEstimate?.amount ?? 0), - new BigNumber(0) - ) + (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), + new BigNumber(0), + ), ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), - [actionPreviewQuery.data] + [actionPreviewQuery.data], ); const gasCheckWarning = useGasWarningCheck({ @@ -77,16 +76,16 @@ export const useStakeReview = () => { const selectedStake = useMemo( () => Maybe.of(enterRequest.selectedStake), - [enterRequest.selectedStake] + [enterRequest.selectedStake], ); const selectedToken = useMemo( () => Maybe.of(enterRequest.selectedToken), - [enterRequest.selectedToken] + [enterRequest.selectedToken], ); const selectedProviderYieldId = useMemo( () => Maybe.fromNullable(enterRequest.requestDto.arguments?.providerId), - [enterRequest.requestDto.arguments?.providerId] + [enterRequest.requestDto.arguments?.providerId], ); const rewardToken = useRewardTokenDetails(selectedStake); @@ -98,13 +97,13 @@ export const useStakeReview = () => { }); const yieldType = useYieldType(selectedStake).mapOrDefault( (y) => y.review, - "" + "", ); const amount = useMemo(() => formatNumber(stakeAmount), [stakeAmount]); const interestRate = useMemo( () => estimatedRewards.mapOrDefault((r) => r.percentage.toString(), ""), - [estimatedRewards] + [estimatedRewards], ); const pricesState = useTokensPrices({ @@ -119,7 +118,7 @@ export const useStakeReview = () => { prices: Maybe.fromNullable(pricesState.data), yieldDto: selectedStake, }), - [pricesState.data, selectedStake, stakeEnterTxGas] + [pricesState.data, selectedStake, stakeEnterTxGas], ); const { depositFee, managementFee, performanceFee } = useFees({ @@ -139,15 +138,15 @@ export const useStakeReview = () => { }; } ).mechanics?.fee ?? null, - [enterRequest.selectedStake] + [enterRequest.selectedStake], ), prices: useMemo( () => Maybe.fromNullable(pricesState.data), - [pricesState.data] + [pricesState.data], ), }); - const metadata = selectedStake.map((y) => y.metadata); + const metadata = selectedStake.map(getYieldMetadata); const navigate = useNavigate(); @@ -176,8 +175,8 @@ export const useStakeReview = () => { label: t("shared.confirm"), onClick: () => onClickRef.current(), }), - [onClickRef, t, enterMutation.isPending] - ) + [onClickRef, t, enterMutation.isPending], + ), ); const { variant } = useSettings(); @@ -194,18 +193,18 @@ export const useStakeReview = () => { }, } : { showMetaInfo: false }) satisfies MetaInfoProps, - [selectedStake, selectedToken, enterRequest.selectedValidators, variant] + [selectedStake, selectedToken, enterRequest.selectedValidators, variant], ); const commissionFee = useMemo( () => selectedStake - .chainNullable((y) => y.metadata.commission) + .chainNullable((y) => getYieldMetadata(y).commission) .map((commission) => - commission.reduce((acc, curr) => acc + curr.value, 0) + commission.reduce((acc, curr) => acc + curr.value, 0), ) .map((val) => `${APToPercentage(val)}%`), - [selectedStake] + [selectedStake], ); return { diff --git a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts index 02e968e0..709b824b 100644 --- a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts @@ -7,6 +7,10 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import type { RewardTokenDetails } from "../../../components/molecules/reward-token-details"; +import { + getYieldMetadata, + getYieldProviderDetails, +} from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; import { getRewardTokenSymbols } from "../../../hooks/use-reward-token-details/get-reward-token-symbols"; @@ -23,7 +27,7 @@ import type { MetaInfoProps } from "../pages/common-page/common.page"; export const useUnstakeActionReview = () => { const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const yieldApiFetchClient = useYieldApiFetchClient(); @@ -46,24 +50,23 @@ export const useUnstakeActionReview = () => { Maybe.fromNullable(actionPreviewQuery.data) .map((actionDto) => actionDto.transactions.reduce( - (acc, transaction) => - acc.plus(transaction.gasEstimate?.amount ?? 0), - new BigNumber(0) - ) + (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), + new BigNumber(0), + ), ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), - [actionPreviewQuery.data] + [actionPreviewQuery.data], ); const interactedToken = useMemo( () => Maybe.of(exitRequest.unstakeToken), - [exitRequest.unstakeToken] + [exitRequest.unstakeToken], ); const integrationData = useMemo( () => Maybe.of(exitRequest.integrationData), - [exitRequest.integrationData] + [exitRequest.integrationData], ); const pricesState = useTokensPrices({ @@ -73,7 +76,7 @@ export const useUnstakeActionReview = () => { const amount = useMemo( () => new BigNumber(exitRequest.requestDto.arguments?.amount ?? 0), - [exitRequest.requestDto.arguments?.amount] + [exitRequest.requestDto.arguments?.amount], ); const gasWarningCheck = useGasWarningCheck({ @@ -89,7 +92,7 @@ export const useUnstakeActionReview = () => { const formattedAmount = useMemo(() => formatNumber(amount), [amount]); const title: Maybe = integrationData.map((d) => { - switch (d.metadata.type) { + switch (getYieldMetadata(d).type) { case "staking": case "liquid-staking": return t("position_details.unstake") as string; @@ -108,13 +111,15 @@ export const useUnstakeActionReview = () => { prices: Maybe.fromNullable(pricesState.data), yieldDto: integrationData, }), - [integrationData, pricesState.data, stakeExitTxGas] + [integrationData, pricesState.data, stakeExitTxGas], ); const rewardTokenDetailsProps = integrationData - .chainNullable((v) => - v.metadata.provider ? { provider: v.metadata.provider, rest: v } : null - ) + .chainNullable((v) => { + const provider = getYieldProviderDetails(v); + + return provider ? { provider, rest: v } : null; + }) .map((v) => { const rewardToken = Maybe.of({ logoUri: v.provider.logoURI, @@ -162,8 +167,8 @@ export const useUnstakeActionReview = () => { disabled: false, isLoading: unstakeIsLoading, }), - [onClickRef, t, unstakeIsLoading] - ) + [onClickRef, t, unstakeIsLoading], + ), ); return { diff --git a/packages/widget/src/pages/review/pages/action-review.page.tsx b/packages/widget/src/pages/review/pages/action-review.page.tsx index 5dbf07fc..c739154f 100644 --- a/packages/widget/src/pages/review/pages/action-review.page.tsx +++ b/packages/widget/src/pages/review/pages/action-review.page.tsx @@ -6,6 +6,7 @@ import { Divider } from "../../../components/atoms/divider"; import { InfoIcon } from "../../../components/atoms/icons/info"; import { ToolTip } from "../../../components/atoms/tooltip"; import { Text } from "../../../components/atoms/typography/text"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { AnimationPage } from "../../../navigation/containers/animation-page"; import { capitalizeFirstLetters } from "../../../utils/formatters"; @@ -32,7 +33,7 @@ export const ActionReviewPage = () => { Maybe.fromNullable(selectedYield.token) .map((val) => `${amount} ${val.symbol}`) .extractNullable(), - [amount, selectedYield.token] + [amount, selectedYield.token], ); return ( @@ -40,7 +41,7 @@ export const ActionReviewPage = () => { @@ -81,7 +82,7 @@ export const ActionReviewPage = () => { .extractNullable()} - )) + )), ) .extractNullable()} 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 20569484..eff8f78e 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 @@ -1,4 +1,3 @@ -import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; import type { Maybe } from "purify-ts"; import type { ComponentProps, ReactNode } from "react"; import { Trans, useTranslation } from "react-i18next"; @@ -10,9 +9,10 @@ import { ToolTip } from "../../../../components/atoms/tooltip"; import { Text } from "../../../../components/atoms/typography/text"; import { WarningBox } from "../../../../components/atoms/warning-box"; import type { RewardTokenDetails } from "../../../../components/molecules/reward-token-details"; +import type { TokenDto, YieldTokenDto } from "../../../../domain/types/tokens"; +import type { YieldMetadata } from "../../../../domain/types/yields"; 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"; @@ -27,7 +27,7 @@ type ReviewPageProps = { fee: string; title: string; token: Maybe; - metadata: Maybe; + metadata: Maybe; info: ReactNode; rewardTokenDetailsProps: Maybe>; isGasCheckError: boolean; 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 662d5406..bec2fbf1 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 @@ -1,4 +1,3 @@ -import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; import { motion } from "motion/react"; import { Maybe } from "purify-ts"; import type { ComponentProps, ReactNode } from "react"; @@ -8,13 +7,17 @@ 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 type { + TokenDto, + YieldTokenDto, +} from "../../../../../domain/types/tokens"; +import type { YieldMetadata } from "../../../../../domain/types/yields"; import { headingStyles } from "../../style.css"; type Props = { title: string; token: Maybe; - metadata: Maybe; + metadata: Maybe; info: ReactNode; rewardTokenDetailsProps?: Maybe>; }; diff --git a/packages/widget/src/pages/review/pages/pending-review.page.tsx b/packages/widget/src/pages/review/pages/pending-review.page.tsx index 8cd53ff0..657719ac 100644 --- a/packages/widget/src/pages/review/pages/pending-review.page.tsx +++ b/packages/widget/src/pages/review/pages/pending-review.page.tsx @@ -1,5 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { usePendingActionReview } from "../hooks/use-pending-review.hook"; import { ReviewPage } from "./common-page/common.page"; @@ -21,7 +22,7 @@ export const PendingReviewPage = () => { const info = useMemo( () => token.map((val) => `${amount} ${val.symbol}`).extractNullable(), - [amount, token] + [amount, token], ); const { depositFee, managementFee, performanceFee, feeConfigLoading } = @@ -32,7 +33,7 @@ export const PendingReviewPage = () => { performanceFee: Maybe.empty(), feeConfigLoading: false, }), - [] + [], ); return ( @@ -45,7 +46,7 @@ export const PendingReviewPage = () => { performanceFee={performanceFee} feeConfigLoading={feeConfigLoading} info={info} - metadata={integrationData.map((val) => val.metadata)} + metadata={integrationData.map(getYieldMetadata)} token={token} isGasCheckError={isGasCheckWarning} loading={gasCheckLoading} diff --git a/packages/widget/src/pages/review/pages/stake-review.page.tsx b/packages/widget/src/pages/review/pages/stake-review.page.tsx index 39f3d82f..b668432a 100644 --- a/packages/widget/src/pages/review/pages/stake-review.page.tsx +++ b/packages/widget/src/pages/review/pages/stake-review.page.tsx @@ -46,12 +46,12 @@ export const StakeReviewPage = () => { /> )) .extractNullable(), - [amount, interestRate, token] + [amount, interestRate, token], ); const rewardTokenDetailsProps = useMemo( () => Maybe.of({ rewardToken, type: "stake" as const }), - [rewardToken] + [rewardToken], ); return ( diff --git a/packages/widget/src/pages/review/pages/unstake-review.page.tsx b/packages/widget/src/pages/review/pages/unstake-review.page.tsx index db55b463..6fcb52d9 100644 --- a/packages/widget/src/pages/review/pages/unstake-review.page.tsx +++ b/packages/widget/src/pages/review/pages/unstake-review.page.tsx @@ -1,5 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { getYieldMetadata } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { UnstakeSignPopup } from "../../position-details/components/unstake-sign-popup"; import { useUnstakeActionReview } from "../hooks/use-unstake-review.hook"; @@ -25,7 +26,7 @@ export const UnstakeReviewPage = () => { const info = useMemo( () => token.map((val) => `${amount} ${val.symbol}`).extractNullable(), - [amount, token] + [amount, token], ); const { depositFee, managementFee, performanceFee, feeConfigLoading } = @@ -36,7 +37,7 @@ export const UnstakeReviewPage = () => { performanceFee: Maybe.empty(), feeConfigLoading: false, }), - [] + [], ); return ( @@ -50,7 +51,7 @@ export const UnstakeReviewPage = () => { performanceFee={performanceFee} feeConfigLoading={feeConfigLoading} info={info} - metadata={integrationData.map((d) => d.metadata)} + metadata={integrationData.map(getYieldMetadata)} token={token} isGasCheckError={isGasCheckWarning} loading={gasCheckLoading} diff --git a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts index e081fe80..bba5b22f 100644 --- a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts +++ b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts @@ -1,9 +1,9 @@ -import type { ActionDto, TransactionDto } from "@stakekit/api-hooks"; import { useMachine } from "@xstate/react"; import { EitherAsync, Left, List, Maybe, Right } from "purify-ts"; import { type RefObject, useMemo, useState } from "react"; import { assign, emit, setup } from "xstate"; import { isTxError } from "../../../domain"; +import type { ActionDto, TransactionDto } from "../../../domain/types/action"; import type { ActionMeta } from "../../../domain/types/wallets/generic-wallet"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useSavedRef } from "../../../hooks/use-saved-ref"; @@ -52,11 +52,11 @@ type SignRes = export const useStepsMachine = ({ transactions, - integrationId, + yieldId, actionMeta, }: { transactions: ActionDto["transactions"]; - integrationId: ActionDto["integrationId"]; + yieldId: ActionDto["yieldId"]; actionMeta: ActionMeta; }) => { const { signTransaction, signMessage } = useSKWallet(); @@ -64,13 +64,14 @@ export const useStepsMachine = ({ const trackEvent = useTrackEvent(); const sortedTransactions = useMemo( - () => [...transactions].sort((a, b) => a.stepIndex - b.stepIndex), - [transactions] + () => + [...transactions].sort((a, b) => (a.stepIndex ?? 0) - (b.stepIndex ?? 0)), + [transactions], ); const machineParams = useSavedRef({ transactions: sortedTransactions, - integrationId, + yieldId, trackEvent, signMessage, signTransaction, @@ -85,18 +86,18 @@ const getMachine = ( ref: Readonly< RefObject<{ transactions: ActionDto["transactions"]; - integrationId: ActionDto["integrationId"]; + yieldId: ActionDto["yieldId"]; trackEvent: ReturnType; signMessage: ReturnType["signMessage"]; signTransaction: ReturnType["signTransaction"]; actionMeta: ActionMeta; yieldApiFetchClient: ReturnType; }> - > + >, ) => { const initContext = getInitContext( ref.current.transactions, - ref.current.integrationId + ref.current.yieldId, ); return setup({ @@ -174,8 +175,8 @@ const getMachine = ( signedTx: event.val.data.signedTx, }, } - : val - ) + : val, + ), ) .orDefault(context.txStates), }), @@ -198,8 +199,8 @@ const getMachine = ( signError: event.val, }, } - : val - ) + : val, + ), ) .orDefault(context.txStates), }), @@ -210,7 +211,7 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx].tx) - .toEither(new SignError({ network: "unknown", txId: "unknown" })) + .toEither(new SignError({ network: "unknown", txId: "unknown" })), ) .chain< SendTransactionError | TransactionDecodeError | SignError, @@ -226,14 +227,19 @@ const getMachine = ( new SignError({ network: tx.network, txId: tx.id, - }) - ) + }), + ), ); } if (tx.isMessage) { + const unsignedMessage = + typeof tx.unsignedTransaction === "string" + ? tx.unsignedTransaction + : JSON.stringify(tx.unsignedTransaction); + return ref.current - .signMessage(tx.unsignedTransaction) + .signMessage(unsignedMessage) .map((val) => ({ type: "regular" as const, data: { signedTx: val, broadcasted: false }, @@ -243,7 +249,7 @@ const getMachine = ( new SignError({ network: tx.network, txId: tx.id, - }) + }), ); } @@ -263,7 +269,9 @@ const getMachine = ( annotatedTransaction: tx.annotatedTransaction, structuredTransaction: tx.structuredTransaction, }, - network: tx.network, + network: tx.network as Parameters< + typeof ref.current.signTransaction + >[0]["network"], }) .map((val) => ({ ...val, @@ -275,7 +283,7 @@ const getMachine = ( txId: tx.id, network: tx.network, yieldId: context.yieldId, - }) + }), ) .map((val) => ({ type: "regular", data: val })); }) @@ -317,8 +325,8 @@ const getMachine = ( signError: null, }, } - : val - ) + : val, + ), ) .orDefault(context.txStates), }), @@ -339,8 +347,8 @@ const getMachine = ( signError: null, }, } - : val - ) + : val, + ), ) .orDefault(context.txStates), }), @@ -350,7 +358,7 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx]) - .toEither(new Error("missing tx")) + .toEither(new Error("missing tx")), ) .chain((currentTx) => { if (currentTx.meta.broadcasted) { @@ -359,7 +367,7 @@ const getMachine = ( fetchClient: ref.current.yieldApiFetchClient, hash: currentTx.meta.signedTx!, transactionId: currentTx.tx.id, - }) + }), ) .mapLeft(() => new SubmitHashError()) .ifRight(() => { @@ -377,7 +385,7 @@ const getMachine = ( fetchClient: ref.current.yieldApiFetchClient, signedTransaction: currentTx.meta.signedTx!, transactionId: currentTx.tx.id, - }) + }), ) .mapLeft(() => new SubmitError()) .ifRight(() => { @@ -433,8 +441,8 @@ const getMachine = ( signError: null, }, } - : val - ) + : val, + ), ) .orDefault(context.txStates), }), @@ -445,14 +453,14 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx]) - .toEither(new Error("missing tx")) + .toEither(new Error("missing tx")), ) .chain((currentTx) => EitherAsync(() => getTransaction({ fetchClient: ref.current.yieldApiFetchClient, transactionId: currentTx.tx.id, - }) + }), ) .map((res) => ({ url: res.explorerUrl, @@ -466,14 +474,14 @@ const getMachine = ( new SignError({ txId: currentTx.tx.id, network: currentTx.tx.network, - }) + }), ) : Right({ url: val.url, isConfirmed: val.status === "CONFIRMED", - }) - ) - ) + }), + ), + ), ) .caseOf({ Left: (l) => { @@ -504,14 +512,14 @@ const getMachine = ( done: true, }, } - : val - ) + : val, + ), ) .orDefault(context.txStates); const newCurrentTxMeta = List.findIndex( (val) => !val.meta.done, - newTxStates + newTxStates, ) .map((idx) => ({ idx, @@ -569,7 +577,7 @@ const getMachine = ( const getInitContext = ( transactions: ActionDto["transactions"], - integrationId: ActionDto["integrationId"] + yieldId: ActionDto["yieldId"], ) => { if (!transactions.length) { return { @@ -602,7 +610,7 @@ const getInitContext = ( enabled: false, txStates, currentTxMeta: null, - yieldId: integrationId, + yieldId, }; } @@ -615,6 +623,6 @@ const getInitContext = ( enabled: true, txStates, currentTxMeta, - yieldId: integrationId, + yieldId, }; }; diff --git a/packages/widget/src/pages/steps/hooks/use-steps.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps.hook.ts index 48c9bbb6..f2f2a393 100644 --- a/packages/widget/src/pages/steps/hooks/use-steps.hook.ts +++ b/packages/widget/src/pages/steps/hooks/use-steps.hook.ts @@ -1,7 +1,8 @@ -import type { ActionDto, TransactionType } from "@stakekit/api-hooks"; import { useEffect, useLayoutEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; +import type { ActionDto, TransactionType } from "../../../domain/types/action"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import type { ActionMeta } from "../../../domain/types/wallets/generic-wallet"; import { useInvalidateTokenBalances } from "../../../hooks/api/use-token-balances-scan"; import { useInvalidateYieldBalances } from "../../../hooks/api/use-yield-balances-scan"; @@ -14,12 +15,14 @@ import type { TxState } from "./use-steps-machine.hook"; import { useStepsMachine } from "./use-steps-machine.hook"; export const useSteps = ({ + inputToken, session, onSignSuccess, providersDetails, }: { onSignSuccess?: () => void; session: ActionDto; + inputToken?: TokenDto | YieldTokenDto; providersDetails: ReturnType; }) => { const navigate = useNavigate(); @@ -31,7 +34,7 @@ export const useSteps = ({ actionId: session.id, actionType: session.type, amount: session.amount, - inputToken: session.inputToken, + inputToken, providersDetails: providersDetails .map((providerDetail) => providerDetail.map((v) => ({ @@ -41,16 +44,16 @@ export const useSteps = ({ rewardType: v.rewardType, website: v.website, logo: v.logo, - })) + })), ) .orDefault([]), }), - [session, providersDetails] + [session, providersDetails, inputToken], ); const [machineState, send, actorRef] = useStepsMachine({ transactions: session.transactions, - integrationId: session.integrationId, + yieldId: session.yieldId, actionMeta, }); @@ -68,7 +71,7 @@ export const useSteps = ({ */ useEffect(() => { const sub = actorRef.on("signSuccess", () => - callbacksRef.current.onSignSuccess?.() + callbacksRef.current.onSignSuccess?.(), ); return () => { @@ -105,7 +108,7 @@ export const useSteps = ({ urls: machineState.context.txStates .map((val) => ({ type: val.tx.type, url: val.meta.url })) .filter( - (val): val is { type: TransactionType; url: string } => !!val.url + (val): val is { type: TransactionType; url: string } => !!val.url, ), }, relative: "path", @@ -158,7 +161,7 @@ export const useSteps = ({ machineState.context.currentTxMeta, machineState.context.txStates, machineState.value, - ] + ], ); const { t } = useTranslation(); @@ -177,8 +180,8 @@ export const useSteps = ({ variant: "secondary", } : null, - [txStates.length, t, onClickRef] - ) + [txStates.length, t, onClickRef], + ), ); return { diff --git a/packages/widget/src/pages/steps/pages/activity-steps.page.tsx b/packages/widget/src/pages/steps/pages/activity-steps.page.tsx index c946e7e3..327fdc2c 100644 --- a/packages/widget/src/pages/steps/pages/activity-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/activity-steps.page.tsx @@ -1,6 +1,10 @@ import { useSelector } from "@xstate/store/react"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +import { + getActionInputToken, + getActionValidatorAddresses, +} from "../../../domain/types/action"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; import { useActivityContext } from "../../../providers/activity-provider"; @@ -13,24 +17,31 @@ export const ActivityStepsPage = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction + (state) => state.context.selectedAction, ).unsafeCoerce(); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield + (state) => state.context.selectedYield, ).unsafeCoerce(); const providersDetails = useProvidersDetails({ integrationData: useMemo(() => Maybe.of(selectedYield), [selectedYield]), validatorsAddresses: useMemo( - () => Maybe.of(selectedAction.validatorAddresses ?? []), - [selectedAction.validatorAddresses] + () => Maybe.of(getActionValidatorAddresses(selectedAction) ?? []), + [selectedAction], ), selectedProviderYieldId: Maybe.empty(), }); return ( - + ); }; diff --git a/packages/widget/src/pages/steps/pages/common.page.tsx b/packages/widget/src/pages/steps/pages/common.page.tsx index 86e310e4..b195400a 100644 --- a/packages/widget/src/pages/steps/pages/common.page.tsx +++ b/packages/widget/src/pages/steps/pages/common.page.tsx @@ -1,9 +1,10 @@ -import type { ActionDto } from "@stakekit/api-hooks"; import { motion } from "motion/react"; import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; import { Button } from "../../../components/atoms/button"; import { Heading } from "../../../components/atoms/typography/heading"; +import type { ActionDto } from "../../../domain/types/action"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import type { useProvidersDetails } from "../../../hooks/use-provider-details"; import { AnimationPage } from "../../../navigation/containers/animation-page"; import { useIsDashboard } from "../../../pages-dashboard/providers/dashboard-context"; @@ -15,12 +16,14 @@ import { TxState } from "./tx-state"; type StepsPageProps = { session: ActionDto; + inputToken?: TokenDto | YieldTokenDto; onSignSuccess?: () => void; providersDetails: ReturnType; }; export const StepsPage = ({ session, + inputToken, onSignSuccess, providersDetails, }: StepsPageProps) => { @@ -28,6 +31,7 @@ export const StepsPage = ({ const isDashboard = useIsDashboard(); const { retry, txStates } = useSteps({ + inputToken, session, onSignSuccess, providersDetails, diff --git a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx index e7fcb46e..86ff60cc 100644 --- a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx @@ -13,7 +13,7 @@ export const PendingStepsPage = () => { const pendingRequest = useSelector( usePendingActionStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const { plain } = useUnstakeOrPendingActionParams(); @@ -26,16 +26,17 @@ export const PendingStepsPage = () => { const providersDetails = useProvidersDetails({ integrationData: useMemo( () => Maybe.of(pendingRequest.integrationData), - [pendingRequest.integrationData] + [pendingRequest.integrationData], ), validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [] + p.type === "validators" ? p.validatorsAddresses : [], ), selectedProviderYieldId: Maybe.empty(), }); return ( diff --git a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx index ea71c918..16d6f38d 100644 --- a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx @@ -14,7 +14,7 @@ export const StakeStepsPage = () => { const enterRequest = useSelector( useEnterStakeStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const onSignSuccess = () => @@ -26,17 +26,18 @@ export const StakeStepsPage = () => { const providersDetails = useProvidersDetails({ integrationData: useMemo( () => Maybe.of(enterRequest.selectedStake), - [enterRequest.selectedStake] + [enterRequest.selectedStake], ), validatorsAddresses: useMemo( () => Maybe.of(enterRequest.selectedValidators), - [enterRequest.selectedValidators] + [enterRequest.selectedValidators], ), selectedProviderYieldId: Maybe.empty(), }); return ( { {t("steps.tx_of", { count: count.total, current: count.current, - type: t(`steps.tx_type.${txState.tx.type}`, { - context: isEthenaUsdeStaking(session.integrationId) - ? "ETHENA_USDE" - : undefined, - }), + type: t( + `steps.tx_type.${txState.tx.type}` as never, + { + context: isEthenaUsdeStaking(session.yieldId) + ? "ETHENA_USDE" + : undefined, + } as never, + ) as unknown as string, })} diff --git a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx index 2c0ab520..d7b75cd0 100644 --- a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx @@ -13,7 +13,7 @@ export const UnstakeStepsPage = () => { const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data + (state) => state.context.data, ).unsafeCoerce(); const { plain } = useUnstakeOrPendingActionParams(); @@ -25,16 +25,17 @@ export const UnstakeStepsPage = () => { const providersDetails = useProvidersDetails({ integrationData: useMemo( () => Maybe.of(exitRequest.integrationData), - [exitRequest.integrationData] + [exitRequest.integrationData], ), validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [] + p.type === "validators" ? p.validatorsAddresses : [], ), selectedProviderYieldId: Maybe.empty(), }); return ( diff --git a/packages/widget/src/providers/activity-provider/index.tsx b/packages/widget/src/providers/activity-provider/index.tsx index 23eaa620..e0a1348d 100644 --- a/packages/widget/src/providers/activity-provider/index.tsx +++ b/packages/widget/src/providers/activity-provider/index.tsx @@ -1,19 +1,20 @@ -import type { ActionDto, YieldDto } from "@stakekit/api-hooks"; import { createStore } from "@xstate/store"; import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; +import type { ActionDto } from "../../domain/types/action"; +import type { Yield } from "../../domain/types/yields"; const store = createStore({ context: { selectedAction: Maybe.empty() as Maybe, - selectedYield: Maybe.empty() as Maybe, + selectedYield: Maybe.empty() as Maybe, }, on: { setSelectedAction: ( _, event: { - data: Maybe<{ selectedAction: ActionDto; selectedYield: YieldDto }>; - } + data: Maybe<{ selectedAction: ActionDto; selectedYield: Yield }>; + }, ) => ({ selectedAction: event.data.map(({ selectedAction }) => selectedAction), selectedYield: event.data.map(({ selectedYield }) => selectedYield), @@ -36,7 +37,7 @@ export const useActivityContext = () => { if (!value) { throw new Error( - "useActivityContext must be used within a ActivityProvider" + "useActivityContext must be used within a ActivityProvider", ); } diff --git a/packages/widget/src/providers/api/get-enabled-networks.ts b/packages/widget/src/providers/api/get-enabled-networks.ts index 7d4c1797..a46da5cc 100644 --- a/packages/widget/src/providers/api/get-enabled-networks.ts +++ b/packages/widget/src/providers/api/get-enabled-networks.ts @@ -19,8 +19,8 @@ export const getEnabledNetworks = ({ queryFn: async () => new Set( (await getResponseData(yieldApiFetchClient.GET("/v1/networks"))).map( - (network) => network.id as Networks - ) + (network) => network.id as Networks, + ), ), - }) + }), ).mapLeft(() => new Error("Could not get enabled networks")); diff --git a/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts b/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts index 915d3790..5b5aec47 100644 --- a/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts +++ b/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts @@ -166,13 +166,13 @@ const registryIdsToSKCosmosNetworks: Record = supportedCosmosChains.map((key) => [ skCosmosNetworksToRegistryIds[key], key, - ]) + ]), ); const registryIdsSet = new Set(Object.values(skCosmosNetworksToRegistryIds)); const chainMapper = ( - val: T + val: T, ): WithWagmiName => { let wagmiName = val.chain_name[0].toUpperCase() + val.chain_name.slice(1); @@ -202,7 +202,7 @@ const chainMapper = ( }; const assetMapper = ( - val: WithWagmiName> + val: WithWagmiName>, ) => { if (val.chain_id === "comdex-1") { val.assets[1].coingecko_id = "harbor-2"; @@ -223,7 +223,7 @@ export const getRegistryIdsToSKCosmosNetworks = (): typeof registryIdsToSKCosmosNetworks => registryIdsToSKCosmosNetworks; const filteredCosmosChainNames = new Map( - cosmosRegistryChains.map((c) => [c.chain_name, c.chain_id]) + cosmosRegistryChains.map((c) => [c.chain_name, c.chain_id]), ); const filterMissingChainName = (val: AssetList) => !!val.chain_name; diff --git a/packages/widget/src/providers/cosmos/chains/index.ts b/packages/widget/src/providers/cosmos/chains/index.ts index f26aed03..51b91d51 100644 --- a/packages/widget/src/providers/cosmos/chains/index.ts +++ b/packages/widget/src/providers/cosmos/chains/index.ts @@ -6,7 +6,7 @@ import { getNetworkLogo, getTokenLogo } from "../../../utils"; import type { CosmosChainsAssets } from "./types"; export const getWagmiChain = ( - chain: CosmosChainsAssets + chain: CosmosChainsAssets, ): Chain & { cosmosChainName: string } => ({ id: chain.chain_id as unknown as number, iconUrl: Just(chain.chain_id) diff --git a/packages/widget/src/providers/cosmos/config.ts b/packages/widget/src/providers/cosmos/config.ts index dfe38824..cfedfc48 100644 --- a/packages/widget/src/providers/cosmos/config.ts +++ b/packages/widget/src/providers/cosmos/config.ts @@ -31,7 +31,7 @@ const queryFn = async ({ } >((networks) => { const chainsToUse = supportedCosmosChains.filter((chain) => - networks.has(chain) + networks.has(chain), ); if (!chainsToUse.length) { @@ -40,7 +40,7 @@ const queryFn = async ({ cosmosChainsMap: {}, cosmosWagmiChains: [], connector: Maybe.empty(), - }) + }), ); } @@ -70,14 +70,14 @@ const queryFn = async ({ wagmiChain: getWagmiChain(next), }, }; - }, {} as CosmosChainsMap) - ).filter(([_, v]) => networks.has(v.skChainName)) + }, {} as CosmosChainsMap), + ).filter(([_, v]) => networks.has(v.skChainName)), ); return { cosmosChainsMap, cosmosWagmiChains: Object.values(cosmosChainsMap).map( - (val) => val.wagmiChain + (val) => val.wagmiChain, ), }; }) @@ -87,12 +87,12 @@ const queryFn = async ({ (e) => new Error("Could not import cosmos wallet manager", { cause: e, - }) + }), ) .map((v) => - v.getWalletManager({ cosmosChainsMap, forceWalletConnectOnly }) + v.getWalletManager({ cosmosChainsMap, forceWalletConnectOnly }), ) - .map((val) => ({ ...val, cosmosWagmiChains, cosmosChainsMap })) + .map((val) => ({ ...val, cosmosWagmiChains, cosmosChainsMap })), ) .chain((v) => EitherAsync(() => v.walletManager.onMounted()) @@ -100,7 +100,7 @@ const queryFn = async ({ EitherAsync(() => { // @ts-expect-error return cosmosWalletManager._restoreAccounts().catch(() => {}); - }) + }), ) .mapLeft((e) => { console.log(e); @@ -112,9 +112,9 @@ const queryFn = async ({ cosmosWagmiChains: v.cosmosWagmiChains, connector: Maybe.fromPredicate( () => !!v.cosmosWagmiChains.length, - v.connector + v.connector, ), - })) + })), ); }) .caseOf({ @@ -128,7 +128,7 @@ export const getConfig = (opts: Parameters[0]) => staleTime, queryKey, queryFn: () => queryFn(opts), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get cosmos config"); diff --git a/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts b/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts index 34d07bec..018677ea 100644 --- a/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts +++ b/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts @@ -22,5 +22,5 @@ export type ExtraProps = ConnectorWithFilteredChains & { export type CosmosConnector = Connector & ExtraProps; export const isCosmosConnector = ( - connector: Connector + connector: Connector, ): connector is CosmosConnector => connector.type === configMeta.type; diff --git a/packages/widget/src/providers/cosmos/cosmos-connector.ts b/packages/widget/src/providers/cosmos/cosmos-connector.ts index a41844b0..bfde43f8 100644 --- a/packages/widget/src/providers/cosmos/cosmos-connector.ts +++ b/packages/widget/src/providers/cosmos/cosmos-connector.ts @@ -60,7 +60,7 @@ export const createCosmosConnector = ({ const initCw = wallet.chainWalletMap.get( cosmosChainsMap.cosmos?.chain.chain_name ?? - Object.values(cosmosChainsMap)[0].chain.chain_name + Object.values(cosmosChainsMap)[0].chain.chain_name, ); if (!initCw) throw new Error("Chain wallet not found"); @@ -92,7 +92,7 @@ export const createCosmosConnector = ({ }); const connect: ReturnType["connect"] = async ( - args + args, ) => { config.emitter.emit("message", { type: "connecting" }); @@ -168,7 +168,7 @@ export const createCosmosConnector = ({ }; const newCw = wallet.getChainWallet( - cosmosChain.cosmosChainName + cosmosChain.cosmosChainName, ) as ChainWalletBase; if (!newCw) throw new Error("Chain wallet not found"); @@ -232,11 +232,11 @@ export const createCosmosConnector = ({ tx: string; }) => EitherAsync(() => - cw.client.signDirect!( + cw.client.signDirect?.( cw.chainId, cw.address!, - SignDoc.decode(fromHex(tx)) as unknown as DirectSignDoc // accountNumber bigint/Long issue - ) + SignDoc.decode(fromHex(tx)) as unknown as DirectSignDoc, // accountNumber bigint/Long issue + ), ) .mapLeft((e) => { console.log(e); @@ -248,8 +248,8 @@ export const createCosmosConnector = ({ authInfoBytes: val.signed.authInfoBytes, bodyBytes: val.signed.bodyBytes, signatures: [decodeSignature(val.signature).signature], - }).finish() - ) + }).finish(), + ), ); const getChainId: ReturnType["getChainId"] = diff --git a/packages/widget/src/providers/cosmos/wallet-connect/client.ts b/packages/widget/src/providers/cosmos/wallet-connect/client.ts index 8b0911f8..df2de22c 100644 --- a/packages/widget/src/providers/cosmos/wallet-connect/client.ts +++ b/packages/widget/src/providers/cosmos/wallet-connect/client.ts @@ -5,7 +5,7 @@ export class WalletConnectClient extends WCClient { this.signClient?.pairing .getAll() .forEach((p) => - this.signClient?.core.pairing.disconnect({ topic: p.topic }) + this.signClient?.core.pairing.disconnect({ topic: p.topic }), ); return super.disconnect(); diff --git a/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts b/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts index 0ebdcd2f..38da78b8 100644 --- a/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts +++ b/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts @@ -7,7 +7,7 @@ import { WalletConnectClient } from "./client"; export class WalletConnectWallet extends WCWallet { constructor( walletInfo: Wallet, - preferredEndpoints?: EndpointOptions["endpoints"] + preferredEndpoints?: EndpointOptions["endpoints"], ) { super(walletInfo, ChainWalletConnect, WalletConnectClient); this.preferredEndpoints = preferredEndpoints; diff --git a/packages/widget/src/providers/cosmos/wallet-connect/registry.ts b/packages/widget/src/providers/cosmos/wallet-connect/registry.ts index 3be18e1a..1f7d9aa7 100644 --- a/packages/widget/src/providers/cosmos/wallet-connect/registry.ts +++ b/packages/widget/src/providers/cosmos/wallet-connect/registry.ts @@ -41,7 +41,7 @@ export const walletConnectInfo: Wallet = { appUrl: string, wcUri: string, _os: OS | undefined, - _name: string + _name: string, ): string => { const plainAppUrl = appUrl.replaceAll("/", "").replaceAll(":", ""); const encodedWcUrl = encodeURIComponent(wcUri); diff --git a/packages/widget/src/providers/cosmos/wallet-manager.ts b/packages/widget/src/providers/cosmos/wallet-manager.ts index 1e711316..913426c5 100644 --- a/packages/widget/src/providers/cosmos/wallet-manager.ts +++ b/packages/widget/src/providers/cosmos/wallet-manager.ts @@ -51,8 +51,8 @@ export const getWalletManager = ({ { cosmosWagmiChains: [] as Chain[], chains: [] as CosmosChainsAssets[], - } - ) + }, + ), ) .map((val) => ({ ...val, @@ -60,7 +60,7 @@ export const getWalletManager = ({ // Put cosmos first registryIdsToSKCosmosNetworks[a.chain_id] === CosmosNetworks.Cosmos ? -1 - : 1 + : 1, ), })) .unsafeCoerce(); @@ -73,7 +73,7 @@ export const getWalletManager = ({ wallet: w, cosmosChainsMap, cosmosWagmiChains, - }) + }), ), }; @@ -93,7 +93,7 @@ export const getWalletManager = ({ projectId: config.walletConnectV2.projectId, customStoragePrefix: "cosmoswalletconnect_", }, - } + }, ), }; }; diff --git a/packages/widget/src/providers/enter-stake-store/index.tsx b/packages/widget/src/providers/enter-stake-store/index.tsx index c2a0912d..c0d62c00 100644 --- a/packages/widget/src/providers/enter-stake-store/index.tsx +++ b/packages/widget/src/providers/enter-stake-store/index.tsx @@ -1,20 +1,20 @@ -import type { - ActionDto, - AddressesDto, - TokenDto, - ValidatorDto, - YieldDto, -} from "@stakekit/api-hooks"; import { createStore } from "@xstate/store"; import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; -import type { YieldCreateActionDto } from "../yield-api-client-provider/types"; +import type { + ActionDto, + YieldCreateActionDto, +} from "../../domain/types/action"; +import type { AddressesDto } from "../../domain/types/addresses"; +import type { TokenDto } from "../../domain/types/tokens"; +import type { ValidatorDto } from "../../domain/types/validators"; +import type { Yield } from "../../domain/types/yields"; type InitData = { requestDto: YieldCreateActionDto; addresses: AddressesDto; - gasFeeToken: YieldDto["token"]; - selectedStake: YieldDto; + gasFeeToken: Yield["token"]; + selectedStake: Yield; selectedValidators: Map; selectedToken: TokenDto; }; @@ -37,7 +37,7 @@ const store = createStore({ }); const EnterStakeStoreContext = createContext( - undefined + undefined, ); export const EnterStakeStoreProvider = ({ children }: PropsWithChildren) => { @@ -53,7 +53,7 @@ export const useEnterStakeStore = () => { if (!value) { throw new Error( - "useEnterStakeStore must be used within a EnterStakeStoreProvider" + "useEnterStakeStore must be used within a EnterStakeStoreProvider", ); } diff --git a/packages/widget/src/providers/ethereum/config.ts b/packages/widget/src/providers/ethereum/config.ts index 7b91a30d..99061eec 100644 --- a/packages/widget/src/providers/ethereum/config.ts +++ b/packages/widget/src/providers/ethereum/config.ts @@ -40,12 +40,12 @@ const queryFn = async ({ const filteredEvmChainsMap: Partial = typeSafeObjectFromEntries( typeSafeObjectEntries(evmChainsMap).filter(([_, v]) => - networks.has(v.skChainName) - ) + networks.has(v.skChainName), + ), ); const evmChains = Object.values(filteredEvmChainsMap).map( - (val) => val.wagmiChain + (val) => val.wagmiChain, ); const portoWallet: WalletList[number]["wallets"][number] = (args) => ({ @@ -93,7 +93,7 @@ export const getConfig = (opts: Parameters[0]) => staleTime: Number.POSITIVE_INFINITY, queryKey: [config.appPrefix, "evm-config"], queryFn: () => queryFn(opts), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get evm config"); diff --git a/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts b/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts index 2b6c4f96..f4ce18bb 100644 --- a/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts +++ b/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts @@ -152,7 +152,7 @@ const safeWalletWC: CommonWalletOptions = Maybe.of(safeWallet()) qrCode: "https://app.safe.global/", }, chainGroup: evmChainGroup, - }) satisfies CommonWalletOptions + }) satisfies CommonWalletOptions, ) .unsafeCoerce(); @@ -194,7 +194,7 @@ export const createFineryWallets: (evmChains: Chain[]) => { // copperConnectProvider: (typeof providers)[number] | undefined; mpcVaultProvider: (typeof providers)[number] | undefined; cactusProvider: (typeof providers)[number] | undefined; - } + }, ); const cactusLinkWallet: WalletList[number]["wallets"][number] = () => ({ @@ -286,8 +286,8 @@ export const createFineryWallets: (evmChains: Chain[]) => { id: `${w.id}-wc`, rdns: `${w.rdns}-wc`, }), - evmChains - ) + evmChains, + ), ); const primaryWallets: WalletList[number]["wallets"] = [ diff --git a/packages/widget/src/providers/ethereum/utils.ts b/packages/widget/src/providers/ethereum/utils.ts index 6cb242c0..11fbce26 100644 --- a/packages/widget/src/providers/ethereum/utils.ts +++ b/packages/widget/src/providers/ethereum/utils.ts @@ -4,7 +4,7 @@ import { createConnector } from "wagmi"; export const passCorrectChainsToWallet = ( wallet: WalletList[number]["wallets"][number], - chains: Chain[] + chains: Chain[], ): WalletList[number]["wallets"][number] => (props) => { const w = wallet(props); @@ -16,7 +16,7 @@ export const passCorrectChainsToWallet = w.createConnector(walletDetails)({ ...config, chains: chains as [Chain, ...Chain[]], - }) + }), ), }; }; diff --git a/packages/widget/src/providers/exit-stake-store/index.tsx b/packages/widget/src/providers/exit-stake-store/index.tsx index 44dde61b..ae354908 100644 --- a/packages/widget/src/providers/exit-stake-store/index.tsx +++ b/packages/widget/src/providers/exit-stake-store/index.tsx @@ -1,24 +1,21 @@ -import type { - ActionDto, - AddressesDto, - TokenDto, - YieldDto, -} from "@stakekit/api-hooks"; 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 { + ActionDto, YieldCreateActionDto, - YieldTokenDto, -} from "../yield-api-client-provider/types"; +} from "../../domain/types/action"; +import type { AddressesDto } from "../../domain/types/addresses"; +import type { TokenDto, YieldTokenDto } from "../../domain/types/tokens"; +import type { Yield } from "../../domain/types/yields"; type InitData = { requestDto: YieldCreateActionDto; addresses: AddressesDto; - gasFeeToken: YieldDto["token"]; + gasFeeToken: Yield["token"]; unstakeAmount: BigNumber; - integrationData: YieldDto; + integrationData: Yield; unstakeToken: TokenDto | YieldTokenDto; }; @@ -40,7 +37,7 @@ const store = createStore({ }); const ExitStakeStoreContext = createContext( - undefined + undefined, ); export const ExitStakeStoreProvider = ({ children }: PropsWithChildren) => { @@ -56,7 +53,7 @@ export const useExitStakeStore = () => { if (!value) { throw new Error( - "useExitStakeStore must be used within a ExitStakeStoreProvider" + "useExitStakeStore must be used within a ExitStakeStoreProvider", ); } diff --git a/packages/widget/src/providers/external-provider/index.ts b/packages/widget/src/providers/external-provider/index.ts index 550127f4..a77184ff 100644 --- a/packages/widget/src/providers/external-provider/index.ts +++ b/packages/widget/src/providers/external-provider/index.ts @@ -29,11 +29,11 @@ type ExtraProps = ConnectorWithFilteredChains & type ExternalConnector = Connector & ExtraProps; export const isExternalProviderConnector = ( - connector: Connector + connector: Connector, ): connector is ExternalConnector => connector.id === configMeta.id; export const externalProviderConnector = ( - variant: RefObject + variant: RefObject, ): WalletList[number] => ({ groupName: "External Providers", wallets: [ @@ -54,8 +54,8 @@ export const externalProviderConnector = ( .map((val) => new Set(val)) .mapOrDefault( (val) => connectorConfig.chains.filter((c) => val.has(c.id)), - connectorConfig.chains as [Chain, ...Chain[]] - ) + connectorConfig.chains as [Chain, ...Chain[]], + ), ); if ($filteredChains.getValue().length === 0) { @@ -71,7 +71,7 @@ export const externalProviderConnector = ( async () => $filteredChains.getValue()[0].id; const connect: ReturnType["connect"] = async ( - args + args, ) => { connectorConfig.emitter.emit("message", { type: "connecting" }); @@ -94,11 +94,11 @@ export const externalProviderConnector = ( await EitherAsync.liftEither( List.find( (c) => c.id === chainId, - connectorConfig.chains as unknown as Array - ).toEither(new Error("Chain not found")) + connectorConfig.chains as unknown as Array, + ).toEither(new Error("Chain not found")), ) .chain((chain) => - provider.switchChain({ chainId }).map(() => chain) + provider.switchChain({ chainId }).map(() => chain), ) .ifRight((chain) => onChainChanged(chain.id.toString())) ).unsafeCoerce(); @@ -140,8 +140,8 @@ export const externalProviderConnector = ( .mapOrDefault( (val) => connectorConfig.chains.filter((c) => val.has(c.id)), - connectorConfig.chains as [Chain, ...Chain[]] - ) + connectorConfig.chains as [Chain, ...Chain[]], + ), ); // If the current chain is not in the supported chains, switch to the first supported chain @@ -149,7 +149,7 @@ export const externalProviderConnector = ( $filteredChains.getValue().every((c) => c.id !== currentChainId) ) { getChainId().then((chainId) => - onChainChanged(chainId.toString()) + onChainChanged(chainId.toString()), ); } }; diff --git a/packages/widget/src/providers/ledger/config.ts b/packages/widget/src/providers/ledger/config.ts index fbc13172..03a57aa8 100644 --- a/packages/widget/src/providers/ledger/config.ts +++ b/packages/widget/src/providers/ledger/config.ts @@ -20,12 +20,12 @@ const queryFn = async ({ wallets: WalletList[number]["wallets"]; } | null> => { return EitherAsync.liftEither( - Maybe.fromFalsy(isLedgerDappBrowserProvider()).toEither(null) + Maybe.fromFalsy(isLedgerDappBrowserProvider()).toEither(null), ) .chain(() => EitherAsync(() => import("./ledger-connector")) .mapLeft(() => new Error("Could not import ledger-connector")) - .map((v) => v.ledgerLiveConnector({ enabledChainsMap, queryParams })) + .map((v) => v.ledgerLiveConnector({ enabledChainsMap, queryParams })), ) .chainLeft((e) => EitherAsync.liftEither(e ? Left(e) : Right(null))) .caseOf({ @@ -35,14 +35,14 @@ const queryFn = async ({ }; export const getConfig = ( - opts: Parameters[0] & { queryClient: QueryClient } + opts: Parameters[0] & { queryClient: QueryClient }, ) => EitherAsync(() => opts.queryClient.fetchQuery({ staleTime, queryKey, queryFn: () => queryFn(opts), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get ledger live config"); diff --git a/packages/widget/src/providers/ledger/ledger-connector.ts b/packages/widget/src/providers/ledger/ledger-connector.ts index a71a07d3..daa4a63f 100644 --- a/packages/widget/src/providers/ledger/ledger-connector.ts +++ b/packages/widget/src/providers/ledger/ledger-connector.ts @@ -45,7 +45,7 @@ const createLedgerLiveConnector = ({ const $currentAccount = new BehaviorSubject(undefined); const $currentAccountId = $currentAccount.pipe( - map((v) => v?.parentAccountId ?? v?.id) + map((v) => v?.parentAccountId ?? v?.id), ); let ledgerAccounts: Account[] = []; const $accountsOnCurrentChain = new BehaviorSubject([]); @@ -79,7 +79,7 @@ const createLedgerLiveConnector = ({ .map((val) => ({ accounts: val, accountsMap: new Map( - val.map((v) => [v.id, v]) + val.map((v) => [v.id, v]), ), })) .map((val) => @@ -91,8 +91,8 @@ const createLedgerLiveConnector = ({ currency: parentAcc.currency, })) .orDefault(acc) - : acc - ) + : acc, + ), ) .mapLeft((e) => { console.log(e); @@ -111,8 +111,8 @@ const createLedgerLiveConnector = ({ filteredSkSupportedChainsToCurrencyIdMap = new Map( [...filteredSupportedLedgerFamiliesWithCurrency.values()].flatMap((v) => - [...v.values()].map((v) => [v.chain.id, v.currencyId]) - ) + [...v.values()].map((v) => [v.chain.id, v.currencyId]), + ), ); filteredSkSupportedChainsValues = @@ -132,7 +132,7 @@ const createLedgerLiveConnector = ({ return acc; }, - { enabled: [] as Chain[], disabled: [] as Chain[] } + { enabled: [] as Chain[], disabled: [] as Chain[] }, ); // Set chains to expose for switcher @@ -147,7 +147,7 @@ const createLedgerLiveConnector = ({ if (!family) return acc; const itemMap = filteredSupportedLedgerFamiliesWithCurrency.get( - family as SupportedLedgerLiveFamilies + family as SupportedLedgerLiveFamilies, ); if (!family || !itemMap) return acc; @@ -160,7 +160,7 @@ const createLedgerLiveConnector = ({ return acc; }, - [] as { account: Account; chainItem: ChainItem }[] + [] as { account: Account; chainItem: ChainItem }[], ) .sort((a, b) => { const aPriority = @@ -175,7 +175,7 @@ const createLedgerLiveConnector = ({ const defaultChain = Maybe.fromNullable( filteredSupportedLedgerFamiliesWithCurrency .get("ethereum") - ?.get("ethereum") + ?.get("ethereum"), ).extractNullable(); if (!defaultChain) throw new Error("Default chain not found"); @@ -204,7 +204,7 @@ const createLedgerLiveConnector = ({ } return { type: "accountId", accountId: accId } as const; - }) + }), ); const accountWithChain = preferredAccount @@ -215,15 +215,15 @@ const createLedgerLiveConnector = ({ } return v.account.id === pa.accountId; - }, accountsWithChain) + }, accountsWithChain), ) .altLazy(() => Maybe.fromNullable(queryParams.network).chain((network) => List.find( (v) => v.chainItem.skChainName === network, - accountsWithChain - ) - ) + accountsWithChain, + ), + ), ) .altLazy(() => List.head(accountsWithChain)) .toEither(new Error("Account not found")) @@ -253,7 +253,7 @@ const createLedgerLiveConnector = ({ Maybe.fromNullable(currentChain) .toEither(new Error("Current chain not found")) .map((val) => - ledgerAccounts.filter((a) => a.currency === val.currencyId) + ledgerAccounts.filter((a) => a.currency === val.currencyId), ); const onAccountsChanged: ReturnType["onAccountsChanged"] = @@ -266,7 +266,7 @@ const createLedgerLiveConnector = ({ }; const onChainChanged: ReturnType["onChainChanged"] = ( - chainId + chainId, ) => { config.emitter.emit("change", { chainId: skNormalizeChainId(chainId) }); }; @@ -304,22 +304,22 @@ const createLedgerLiveConnector = ({ const requestAndSwitchAccount = (chain: Chain) => EitherAsync.liftEither( Maybe.fromNullable( - filteredSkSupportedChainsToCurrencyIdMap?.get(chain.id) - ).toEither(new Error("Chain not found")) + filteredSkSupportedChainsToCurrencyIdMap?.get(chain.id), + ).toEither(new Error("Chain not found")), ) .chain((currencyId) => EitherAsync(() => - walletApiClient.account.request({ currencyIds: [currencyId] }) + walletApiClient.account.request({ currencyIds: [currencyId] }), ).mapLeft((e) => { console.log(e); return new Error("could not request account"); - }) + }), ) .chain((account) => { ledgerAccounts.push(account); $filteredChains.next([...$filteredChains.value, chain]); $disabledChains.next( - $disabledChains.value.filter((c) => c.id !== chain.id) + $disabledChains.value.filter((c) => c.id !== chain.id), ); return EitherAsync(() => switchChain({ chainId: chain.id })); }) diff --git a/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts b/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts index 2b3a1f50..cda49ad4 100644 --- a/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts +++ b/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts @@ -30,5 +30,5 @@ export type ExtraProps = ConnectorWithFilteredChains & { type LedgerLiveConnector = Connector & ExtraProps; export const isLedgerLiveConnector = ( - connector: Connector + connector: Connector, ): connector is LedgerLiveConnector => connector.id === configMeta.id; diff --git a/packages/widget/src/providers/ledger/utils.ts b/packages/widget/src/providers/ledger/utils.ts index 2b49bb26..ba83e4a9 100644 --- a/packages/widget/src/providers/ledger/utils.ts +++ b/packages/widget/src/providers/ledger/utils.ts @@ -46,7 +46,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ return acc; }, - { accountsFamilies: new Set(), accountsCurrencies: new Set() } + { accountsFamilies: new Set(), accountsCurrencies: new Set() }, ); const v = typeSafeObjectEntries(supportedLedgerFamiliesWithCurrency).reduce( @@ -82,7 +82,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ if ( ledgerChainPriority.has( - item.skChainName as unknown as SupportedSKChains + item.skChainName as unknown as SupportedSKChains, ) ) { // biome-ignore lint: false @@ -95,7 +95,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ // biome-ignore lint: false return { ...acc, [k]: filtered }; }, - {} as MappedSupportedLedgerFamiliesWithCurrency + {} as MappedSupportedLedgerFamiliesWithCurrency, ); type V = typeof v; @@ -129,7 +129,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ enabled: boolean; } > - >() + >(), ); }; @@ -151,9 +151,9 @@ export const getLedgerCurrencies = (walletAPIClient: WalletAPIClient) => EitherAsync(() => walletAPIClient.currency.list({ currencyIds: Object.values(supportedLedgerFamiliesWithCurrency).flatMap( - (chain) => Object.values(chain).map((currency) => currency.currencyId) + (chain) => Object.values(chain).map((currency) => currency.currencyId), ), - }) + }), ) .map((val) => { return val.reduce( @@ -169,7 +169,7 @@ export const getLedgerCurrencies = (walletAPIClient: WalletAPIClient) => { cryptoCurrency: new Map(), tokenCurrency: [] } as { cryptoCurrency: Map; tokenCurrency: ERC20TokenCurrency[]; - } + }, ); }) .map((v) => { diff --git a/packages/widget/src/providers/misc/cardano-connector-meta.ts b/packages/widget/src/providers/misc/cardano-connector-meta.ts index becafdc6..3a608f1b 100644 --- a/packages/widget/src/providers/misc/cardano-connector-meta.ts +++ b/packages/widget/src/providers/misc/cardano-connector-meta.ts @@ -21,5 +21,5 @@ export type StorageItem = { }; export const isCardanoConnector = ( - connector: Connector + connector: Connector, ): connector is CardanoConnector => connector.type === "cardanoWallet"; diff --git a/packages/widget/src/providers/misc/cardano-connector.ts b/packages/widget/src/providers/misc/cardano-connector.ts index e6027cca..16d3b2e9 100644 --- a/packages/widget/src/providers/misc/cardano-connector.ts +++ b/packages/widget/src/providers/misc/cardano-connector.ts @@ -79,13 +79,13 @@ const createCardanoConnector = ({ getChainId: async () => cardano.id, isAuthorized: async () => { const isDisconnected = await config.storage?.getItem( - "cardano.disconnected" + "cardano.disconnected", ); if (isDisconnected) return false; const lastConnectedWallet = await config.storage?.getItem( - "cardano.lastConnectedWallet" + "cardano.lastConnectedWallet", ); if (!lastConnectedWallet) return false; diff --git a/packages/widget/src/providers/misc/config.ts b/packages/widget/src/providers/misc/config.ts index 324208cc..6c3099c7 100644 --- a/packages/widget/src/providers/misc/config.ts +++ b/packages/widget/src/providers/misc/config.ts @@ -38,25 +38,25 @@ const queryFn = async ({ }>[]; }> => { const miscChainsEntries = typeSafeObjectEntries( - miscChainsMap + miscChainsMap, ).filter(([_, v]) => enabledNetworks.has(v.skChainName)); const filteredMiscChainsMap: Partial = typeSafeObjectFromEntries(miscChainsEntries); const miscChains = Object.values(filteredMiscChainsMap).map( - (val) => val.wagmiChain + (val) => val.wagmiChain, ); return Promise.all([ MaybeAsync.liftMaybe(Maybe.fromFalsy(filteredMiscChainsMap.tron)).chain( () => MaybeAsync(() => import("./tron-connector")).map((v) => - v.getTronConnectors({ forceWalletConnectOnly }) - ) + v.getTronConnectors({ forceWalletConnectOnly }), + ), ), MaybeAsync.liftMaybe( - Maybe.fromFalsy(filteredMiscChainsMap.solana && !config.env.isTestMode) + Maybe.fromFalsy(filteredMiscChainsMap.solana && !config.env.isTestMode), ).chain(() => MaybeAsync(() => import("./solana-connector")).map((v) => v.getSolanaConnectors({ @@ -64,19 +64,19 @@ const queryFn = async ({ wallets: solanaWallets, connection: solanaConnection, variant, - }) - ) + }), + ), ), MaybeAsync.liftMaybe(Maybe.fromFalsy(filteredMiscChainsMap.cardano)).chain( () => MaybeAsync(() => import("./cardano-connector")).map((v) => - v.getCardanoConnectors() - ) + v.getCardanoConnectors(), + ), ), MaybeAsync.liftMaybe(Maybe.fromFalsy(filteredMiscChainsMap.ton)).chain(() => MaybeAsync(() => import("./ton-connector")).map((v) => - v.getTonConnectors({ tonConnectManifestUrl }) - ) + v.getTonConnectors({ tonConnectManifestUrl }), + ), ), ]).then((connectors) => ({ miscChainsMap: filteredMiscChainsMap, @@ -86,14 +86,14 @@ const queryFn = async ({ }; export const getConfig = ( - opts: Parameters[0] & { queryClient: QueryClient } + opts: Parameters[0] & { queryClient: QueryClient }, ) => EitherAsync(() => opts.queryClient.fetchQuery({ staleTime, queryKey, queryFn: () => queryFn(opts), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get misc config"); diff --git a/packages/widget/src/providers/misc/solana-connector-meta.ts b/packages/widget/src/providers/misc/solana-connector-meta.ts index a5b1934e..1890b0c4 100644 --- a/packages/widget/src/providers/misc/solana-connector-meta.ts +++ b/packages/widget/src/providers/misc/solana-connector-meta.ts @@ -49,6 +49,6 @@ export type StorageItem = { "solana.disconnected": boolean }; type SolanaConnector = Connector & ExtraProps; export const isSolanaConnector = ( - connector: Connector + connector: Connector, ): connector is SolanaConnector => !!("isSolanaConnector" in connector && connector.isSolanaConnector); diff --git a/packages/widget/src/providers/misc/solana-connector.ts b/packages/widget/src/providers/misc/solana-connector.ts index 4a9982ec..b83e4489 100644 --- a/packages/widget/src/providers/misc/solana-connector.ts +++ b/packages/widget/src/providers/misc/solana-connector.ts @@ -57,14 +57,14 @@ const createSolanaConnector = ({ solanaTx = Transaction.from(buffer); } catch (legacyErr) { throw new Error( - `Failed to deserialize Solana transaction. VersionedTransaction error: ${versionedError instanceof Error ? versionedError.message : String(versionedError)}. Legacy Transaction error: ${legacyErr instanceof Error ? legacyErr.message : String(legacyErr)}` + `Failed to deserialize Solana transaction. VersionedTransaction error: ${versionedError instanceof Error ? versionedError.message : String(versionedError)}. Legacy Transaction error: ${legacyErr instanceof Error ? legacyErr.message : String(legacyErr)}`, ); } } const signed = await solanaWallet.adapter.sendTransaction( solanaTx, - connection + connection, ); return signed; }, @@ -100,7 +100,7 @@ const createSolanaConnector = ({ getChainId: async () => solana.id, isAuthorized: async () => { const isDisconnected = await config.storage?.getItem( - "solana.disconnected" + "solana.disconnected", ); if (isDisconnected) return false; @@ -149,7 +149,7 @@ export const getSolanaConnectors = ({ .filter((w) => variant === "porto" ? w.adapter instanceof WalletConnectWalletAdapter - : true + : true, ) .map((w) => () => ({ id: w.adapter.name, diff --git a/packages/widget/src/providers/misc/ton-connector-meta.ts b/packages/widget/src/providers/misc/ton-connector-meta.ts index e1e50b36..fd4897bc 100644 --- a/packages/widget/src/providers/misc/ton-connector-meta.ts +++ b/packages/widget/src/providers/misc/ton-connector-meta.ts @@ -17,5 +17,5 @@ export type StorageItem = { }; export const isTonConnector = ( - connector: Connector + connector: Connector, ): connector is TonConnector => connector.type === "tonWallet"; diff --git a/packages/widget/src/providers/misc/ton-connector.ts b/packages/widget/src/providers/misc/ton-connector.ts index c468f1bd..99bd0886 100644 --- a/packages/widget/src/providers/misc/ton-connector.ts +++ b/packages/widget/src/providers/misc/ton-connector.ts @@ -25,7 +25,7 @@ import { const createTonConnector = ( walletDetailsParams: WalletDetailsParams, - manifestUrl: string | undefined + manifestUrl: string | undefined, ) => createConnector((config) => { const tonconnectUI = new TonConnectUI({ @@ -67,10 +67,10 @@ const createTonConnector = ( Either.encase(() => JSON.parse(tx)).chain((val) => unsignedTonTransactionTonConnectCodec .decode(val) - .mapLeft((e) => new Error(e)) - ) + .mapLeft((e) => new Error(e)), + ), ).then(({ message }) => - loadMessageRelaxed(Cell.fromBase64(message).beginParse()) + loadMessageRelaxed(Cell.fromBase64(message).beginParse()), ); const info = parsedTx.info as CommonMessageInfoRelaxedInternal; @@ -104,7 +104,7 @@ const createTonConnector = ( () => new Promise((resolve, reject) => { deferred = { resolve, reject }; - }) + }), ) .then((wallet) => { deferred = null; @@ -112,7 +112,7 @@ const createTonConnector = ( })); const userFriendlyAddress = toUserFriendlyAddress( - wallet.account.address + wallet.account.address, ); return { diff --git a/packages/widget/src/providers/misc/tron-connector-meta.ts b/packages/widget/src/providers/misc/tron-connector-meta.ts index 3b77f2b5..55c615bf 100644 --- a/packages/widget/src/providers/misc/tron-connector-meta.ts +++ b/packages/widget/src/providers/misc/tron-connector-meta.ts @@ -33,6 +33,6 @@ type TronConnector = Connector & ExtraProps; export type StorageItem = { "tron.disconnected": boolean }; export const isTronConnector = ( - connector: Connector + connector: Connector, ): connector is TronConnector => Object.values(configMeta).some((val) => val.id === connector.id); diff --git a/packages/widget/src/providers/misc/tron-connector.ts b/packages/widget/src/providers/misc/tron-connector.ts index 1d0b809f..3482257f 100644 --- a/packages/widget/src/providers/misc/tron-connector.ts +++ b/packages/widget/src/providers/misc/tron-connector.ts @@ -58,8 +58,8 @@ const createTronConnector = ({ return ( await EitherAsync.liftEither( Maybe.fromNullable([adapter.address as Address]).toEither( - new Error("No account found") - ) + new Error("No account found"), + ), ) ).unsafeCoerce(); }, diff --git a/packages/widget/src/providers/mount-animation/index.tsx b/packages/widget/src/providers/mount-animation/index.tsx index 38c664a3..5d8cf8c1 100644 --- a/packages/widget/src/providers/mount-animation/index.tsx +++ b/packages/widget/src/providers/mount-animation/index.tsx @@ -45,21 +45,21 @@ type ContextValue = { }; const MountAnimationContext = createContext( - undefined + undefined, ); const removeDelay = delayAPIRequests(); export const MountAnimationProvider = ({ children }: PropsWithChildren) => { const onMountAnimationCompleteRef = useSavedRef( - useSettings().onMountAnimationComplete + useSettings().onMountAnimationComplete, ); const { dashboardVariant } = useSettings(); const [state, dispatch] = useReducer( reducer, - dashboardVariant ? { earnPage: true, layout: true } : initialState() + dashboardVariant ? { earnPage: true, layout: true } : initialState(), ); useEffect(() => { @@ -81,7 +81,7 @@ export const MountAnimationProvider = ({ children }: PropsWithChildren) => { const value = useMemo( () => ({ dispatch, state, mountAnimationFinished }) satisfies ContextValue, - [mountAnimationFinished, state] + [mountAnimationFinished, state], ); return ( @@ -96,7 +96,7 @@ export const useMountAnimation = () => { if (!context) { throw new Error( - "useMountAnimation must be used within a MountAnimationProvider" + "useMountAnimation must be used within a MountAnimationProvider", ); } diff --git a/packages/widget/src/providers/pending-action-store/index.tsx b/packages/widget/src/providers/pending-action-store/index.tsx index 13f179f0..50d86346 100644 --- a/packages/widget/src/providers/pending-action-store/index.tsx +++ b/packages/widget/src/providers/pending-action-store/index.tsx @@ -1,23 +1,20 @@ -import type { - ActionDto, - AddressesDto, - TokenDto, - YieldDto, -} from "@stakekit/api-hooks"; import { createStore } from "@xstate/store"; import { Maybe } from "purify-ts"; import { createContext, type PropsWithChildren, useContext } from "react"; import type { + ActionDto, YieldCreateManageActionDto, - YieldPendingActionType, - YieldTokenDto, -} from "../yield-api-client-provider/types"; +} from "../../domain/types/action"; +import type { AddressesDto } from "../../domain/types/addresses"; +import type { YieldPendingActionType } from "../../domain/types/pending-action"; +import type { TokenDto, YieldTokenDto } from "../../domain/types/tokens"; +import type { Yield } from "../../domain/types/yields"; type InitData = { requestDto: YieldCreateManageActionDto; addresses: AddressesDto; pendingActionType: YieldPendingActionType; - integrationData: YieldDto; + integrationData: Yield; interactedToken: TokenDto | YieldTokenDto; gasFeeToken: TokenDto; }; @@ -40,7 +37,7 @@ const store = createStore({ }); const PendingActionStoreContext = createContext( - undefined + undefined, ); export const PendingActionStoreProvider = ({ children }: PropsWithChildren) => { @@ -56,7 +53,7 @@ export const usePendingActionStore = () => { if (!value) { throw new Error( - "usePendingActionStore must be used within a PendingActionProvider" + "usePendingActionStore must be used within a PendingActionProvider", ); } diff --git a/packages/widget/src/providers/query-client/index.tsx b/packages/widget/src/providers/query-client/index.tsx index de5bed17..267901ca 100644 --- a/packages/widget/src/providers/query-client/index.tsx +++ b/packages/widget/src/providers/query-client/index.tsx @@ -33,7 +33,7 @@ export const useSKQueryClient = () => { if (!queryClient) { throw new Error( - "useSKQueryClient must be used within a QueryClientContextProvider" + "useSKQueryClient must be used within a QueryClientContextProvider", ); } diff --git a/packages/widget/src/providers/rainbow-kit.tsx b/packages/widget/src/providers/rainbow-kit.tsx index f637052e..7c8a584b 100644 --- a/packages/widget/src/providers/rainbow-kit.tsx +++ b/packages/widget/src/providers/rainbow-kit.tsx @@ -43,7 +43,7 @@ export const RainbowKitProviderWithTheme = ({ const chainIdsToUse = useMemo( () => new Set(connectorChains.map((c) => c.id)), - [connectorChains] + [connectorChains], ); const disabledChains = useMemo( @@ -52,7 +52,7 @@ export const RainbowKitProviderWithTheme = ({ ...c, info: t("chain_modal.disabled_chain_info"), })), - [ledgerDisabledChains, t] + [ledgerDisabledChains, t], ); const hideDisconnect = useMemo( @@ -60,7 +60,7 @@ export const RainbowKitProviderWithTheme = ({ Maybe.fromNullable(connector) .map((c) => !shouldShowDisconnect(c)) .orDefault(true), - [connector] + [connector], ); const locale = useMemo(() => { diff --git a/packages/widget/src/providers/rainbow/index.tsx b/packages/widget/src/providers/rainbow/index.tsx index 56af6c63..ff65b6d9 100644 --- a/packages/widget/src/providers/rainbow/index.tsx +++ b/packages/widget/src/providers/rainbow/index.tsx @@ -13,7 +13,7 @@ export const RainbowProvider = ({ children }: PropsWithChildren) => { .map((v) => v .filter((a) => a.address !== address) - .map((a) => formatAddress(a.address) as Address) + .map((a) => formatAddress(a.address) as Address), ) .orDefault([]); @@ -25,8 +25,8 @@ export const RainbowProvider = ({ children }: PropsWithChildren) => { Maybe.fromNullable(ledgerAccounts).ifJust((accounts) => List.find( (acc) => formatAddress(acc.address) === address, - accounts - ).ifJust((acc) => onLedgerAccountChange?.(acc)) + accounts, + ).ifJust((acc) => onLedgerAccountChange?.(acc)), ), }} > diff --git a/packages/widget/src/providers/root-element/index.tsx b/packages/widget/src/providers/root-element/index.tsx index 03a71f50..209ed0e4 100644 --- a/packages/widget/src/providers/root-element/index.tsx +++ b/packages/widget/src/providers/root-element/index.tsx @@ -4,7 +4,7 @@ import { rootSelector } from "../../styles/theme/ids"; import { MaybeDocument } from "../../utils/maybe-document"; const RootElementContext = createContext( - undefined + undefined, ); export const RootElementProvider = ({ children }: PropsWithChildren) => { @@ -12,7 +12,7 @@ export const RootElementProvider = ({ children }: PropsWithChildren) => { useLayoutEffect(() => { MaybeDocument.chainNullable( - (doc) => doc.querySelector(rootSelector) as HTMLElement + (doc) => doc.querySelector(rootSelector) as HTMLElement, ).ifJust((el) => setRootElement(el)); }, []); diff --git a/packages/widget/src/providers/safe/config.ts b/packages/widget/src/providers/safe/config.ts index e7b24478..2205e49d 100644 --- a/packages/widget/src/providers/safe/config.ts +++ b/packages/widget/src/providers/safe/config.ts @@ -26,7 +26,7 @@ export const getConfig = (opts: { queryClient: QueryClient }) => staleTime, queryKey, queryFn: () => queryFn(), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get safe config"); diff --git a/packages/widget/src/providers/safe/safe-connector-meta.ts b/packages/widget/src/providers/safe/safe-connector-meta.ts index 6e53756d..6447b87e 100644 --- a/packages/widget/src/providers/safe/safe-connector-meta.ts +++ b/packages/widget/src/providers/safe/safe-connector-meta.ts @@ -25,5 +25,5 @@ export type ExtraProps = ConnectorWithFilteredChains & { type SafeConnector = Connector & ExtraProps; export const isSafeConnector = ( - connector: Connector + connector: Connector, ): connector is SafeConnector => connector.id === configMeta.id; diff --git a/packages/widget/src/providers/safe/safe-connector.ts b/packages/widget/src/providers/safe/safe-connector.ts index 043fbbb5..27ac7b29 100644 --- a/packages/widget/src/providers/safe/safe-connector.ts +++ b/packages/widget/src/providers/safe/safe-connector.ts @@ -51,7 +51,7 @@ function safe(parameters: { shimDisconnect?: boolean } = {}) { $filteredChains.next( Maybe.fromNullable(config.chains.find((c) => c.id === chainId)) .map((c) => [c]) - .orDefault([]) + .orDefault([]), ); if (!disconnect) { @@ -87,7 +87,7 @@ function safe(parameters: { shimDisconnect?: boolean } = {}) { const provider = await getProvider(); if (!provider) throw new ProviderNotFoundError(); return (await provider.request({ method: "eth_accounts" })).map( - getAddress + getAddress, ); }, async getProvider() { @@ -136,13 +136,13 @@ function safe(parameters: { shimDisconnect?: boolean } = {}) { $filteredChains, getTxStatus(txHash) { return EitherAsync(() => sdk.txs.getBySafeTxHash(txHash)).mapLeft( - () => new Error("Could not get transaction status") + () => new Error("Could not get transaction status"), ); }, txStatus: TransactionStatus, sendTransactions(args) { return EitherAsync(() => sdk.txs.send(args)).mapLeft( - () => new Error("Could not send transactions") + () => new Error("Could not send transactions"), ); }, }; diff --git a/packages/widget/src/providers/settings/index.tsx b/packages/widget/src/providers/settings/index.tsx index da80a509..5ac71ad2 100644 --- a/packages/widget/src/providers/settings/index.tsx +++ b/packages/widget/src/providers/settings/index.tsx @@ -7,7 +7,7 @@ import utilaTranslations from "../../translation/English/utila-variant.json"; import type { SettingsContextType } from "./types"; export const SettingsContext = createContext( - undefined + undefined, ); export const SettingsContextProvider = ({ @@ -31,9 +31,9 @@ export const SettingsContextProvider = ({ Object.entries(value).map(([tokenString, innerValue]) => [ tokenString.toLowerCase(), innerValue, - ]) + ]), ), - ]) + ]), ) .map((entries) => Object.fromEntries(entries)) .extract() as typeof rest.preferredTokenYieldsPerNetwork; @@ -54,7 +54,7 @@ export const SettingsContextProvider = ({ "translation", utilaTranslations, true, - true + true, ); } @@ -79,7 +79,7 @@ export const useSettings = () => { if (!context) { throw new Error( - "useSettings must be used within a SettingsContextProvider" + "useSettings must be used within a SettingsContextProvider", ); } diff --git a/packages/widget/src/providers/settings/types.ts b/packages/widget/src/providers/settings/types.ts index b22e09d3..303befb1 100644 --- a/packages/widget/src/providers/settings/types.ts +++ b/packages/widget/src/providers/settings/types.ts @@ -1,10 +1,11 @@ -import type { TokenDto, TransactionFormat } from "@stakekit/api-hooks"; import type { ReactNode } from "react"; import type { SupportedSKChainIds, SupportedSKChains, } from "../../domain/types/chains"; +import type { TransactionFormat } from "../../domain/types/settings"; import type { PreferredTokenYieldsPerNetwork } from "../../domain/types/stake"; +import type { TokenDto } from "../../domain/types/tokens"; import type { SKExternalProviders } from "../../domain/types/wallets"; import type { Languages, localResources } from "../../translation"; import type { RecursivePartial } from "../../types/utils"; diff --git a/packages/widget/src/providers/sk-wallet/index.tsx b/packages/widget/src/providers/sk-wallet/index.tsx index aeb91a11..345b7e06 100644 --- a/packages/widget/src/providers/sk-wallet/index.tsx +++ b/packages/widget/src/providers/sk-wallet/index.tsx @@ -116,10 +116,10 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { miscChainsMap: val.wagmiConfig.miscConfig.miscChainsMap, substrateChainsMap: val.wagmiConfig.substrateConfig.substrateChainsMap, - }) + }), ) .extractNullable(), - [chain, wagmiConfig.data] + [chain, wagmiConfig.data], ); const isConnected = _isConnected && !!address && !!connector && !!network; @@ -162,9 +162,9 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { EitherAsync.liftEither( !isConnected || !network || !connector || !address ? Left(new Error("No wallet connected")) - : Right({ conn: connector, network, address }) + : Right({ conn: connector, network, address }), ), - [connector, isConnected, network, address] + [connector, isConnected, network, address], ); const signTransaction = useCallback( @@ -181,21 +181,23 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { if (isLedgerLiveConnector(conn)) { return EitherAsync.liftEither( Maybe.fromNullable(ledgerCurrentAccountId).toEither( - new SendTransactionError() - ) + new SendTransactionError(), + ), ) .chain((val) => EitherAsync.liftEither( Either.encase(() => JSON.parse(tx)) .chain((parsedTx) => - Either.encase(() => conn.deserializeTransaction(parsedTx)) + Either.encase(() => + conn.deserializeTransaction(parsedTx), + ), ) - .mapLeft(() => new TransactionDecodeError()) + .mapLeft(() => new TransactionDecodeError()), ).map((deserializedTransaction) => ({ accountId: val, deserializedTransaction, - })) + })), ) .chain(({ accountId, deserializedTransaction }) => EitherAsync(() => @@ -204,12 +206,12 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { deserializedTransaction, Maybe.fromNullable(ledgerHwAppId) .map((v) => ({ hwAppId: v })) - .extract() - ) + .extract(), + ), ).mapLeft((e) => { console.log(e); return new SendTransactionError(); - }) + }), ) .map((val) => ({ signedTx: val, broadcasted: true })); } @@ -218,10 +220,10 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { return EitherAsync.liftEither( Either.encase(() => JSON.parse(tx)) .mapLeft(() => "Failed to parse tx") - .chain((val) => substratePayloadCodec.decode(val)) + .chain((val) => substratePayloadCodec.decode(val)), ) .chain((decodedPayload) => - conn.signTransaction({ ...decodedPayload, rawTx: tx }) + conn.signTransaction({ ...decodedPayload, rawTx: tx }), ) .map((signedTx) => ({ signedTx, broadcasted: false })) .chainLeft((e) => { @@ -236,14 +238,14 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { if (isCosmosConnector(conn)) { return EitherAsync.liftEither( Maybe.fromNullable(cosmosCW).toEither( - new Error("cosmosCW missing") - ) + new Error("cosmosCW missing"), + ), ) .chain((cw) => // We need to sign + broadcast as `walletconnect` cosmos client does not support `sendTx` conn .signTransaction({ cw, tx }) - .map((val) => ({ signedTx: val, broadcasted: false })) + .map((val) => ({ signedTx: val, broadcasted: false })), ) .mapLeft(() => new SendTransactionError()); } @@ -258,13 +260,13 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft((e) => { console.log(e); return new TransactionDecodeError(); - }) + }), ) .chain((val) => EitherAsync(() => conn.signTransaction(val)).mapLeft((e) => { console.log(e); return new SendTransactionError(); - }) + }), ) .map((val) => ({ signedTx: JSON.stringify(val), @@ -283,7 +285,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { return Either.encase(() => JSON.parse(tx)) .mapLeft(() => "Failed to parse tx") .chain((val) => - decodeAndPrepareEvmTransaction({ address, input: val }) + decodeAndPrepareEvmTransaction({ address, input: val }), ) .map((v) => ({ type: "evm", tx: v })); } @@ -313,7 +315,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft(() => "Failed to parse tx") .chain((val) => substratePayloadCodec.decode(val)) .map( - (v) => ({ type: "bittensor", tx: v }) as BittensorTx + (v) => ({ type: "bittensor", tx: v }) as BittensorTx, ); } @@ -322,7 +324,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft((e) => { console.log(e); return new TransactionDecodeError(); - }) + }), ) .chain((val) => conn @@ -330,22 +332,22 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft( (e) => new SendTransactionError( - typeof e === "string" ? e : undefined - ) - ) + typeof e === "string" ? e : undefined, + ), + ), ) .map((val) => ({ signedTx: val, broadcasted: true })); } if (isSolanaConnector(conn)) { return EitherAsync.liftEither( - unsignedSolanaTransactionCodec.decode(tx) + unsignedSolanaTransactionCodec.decode(tx), ) .mapLeft(() => new TransactionDecodeError()) .chain((decodedTx) => EitherAsync(() => conn.sendTransaction(decodedTx)) .ifLeft((e) => console.log(e)) - .mapLeft(() => new SendTransactionError()) + .mapLeft(() => new SendTransactionError()), ) .map((res) => ({ signedTx: res, broadcasted: true })); } @@ -371,9 +373,9 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { return EitherAsync.liftEither( Either.encase(() => JSON.parse(tx)) .chain((val) => - decodeAndPrepareEvmTransaction({ address, input: val }) + decodeAndPrepareEvmTransaction({ address, input: val }), ) - .mapLeft(() => new TransactionDecodeError()) + .mapLeft(() => new TransactionDecodeError()), ) .chain((tx) => conn @@ -386,7 +388,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { }, ], }) - .map((res) => res.safeTxHash) + .map((res) => res.safeTxHash), ) .chain((safeTxHash) => withRequestErrorRetry({ @@ -401,11 +403,11 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { res.txStatus === conn.txStatus.FAILED || res.txStatus === conn.txStatus.CANCELLED ? "FAILED" - : "NOT_READY" - ) - ) + : "NOT_READY", + ), + ), ) - : EitherAsync.liftEither(Right(res.txHash)) + : EitherAsync.liftEither(Right(res.txHash)), ) .run() .then((res) => res.unsafeCoerce()), @@ -414,13 +416,13 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .chainNullable((e) => (e as SafeFailedError)._tag === "SafeFailedError" ? (e as SafeFailedError) - : null + : null, ) .filter((e) => e.type !== "FAILED" && !checkIsUnmounted()) .map(() => retryCount < 120) .orDefault(false), retryWaitForMs: () => 7000, - }) + }), ) .mapLeft(() => new SendTransactionError()) .map((val) => ({ signedTx: val as Hash, broadcasted: true })); @@ -435,7 +437,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft((e) => { console.log(e); return new TransactionDecodeError(); - }) + }), ).chain((val) => EitherAsync(() => /** @@ -451,10 +453,10 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { chainId: val.chainId, gas: val.gasLimit, type: val.maxFeePerGas ? "eip1559" : "legacy", - }) + }), ) .mapLeft((e) => new SendTransactionError(e)) - .map((val) => ({ signedTx: val, broadcasted: true })) + .map((val) => ({ signedTx: val, broadcasted: true })), ); }), [ @@ -463,7 +465,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { ledgerCurrentAccountId, sendTransactionAsync, checkIsUnmounted, - ] + ], ); const signMessage = useCallback( @@ -480,7 +482,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { console.log(e); return new Error("sign failed"); }), - [connectorDetails, signMessageAsync] + [connectorDetails, signMessageAsync], ); const onLedgerAccountChange = useCallback( @@ -489,7 +491,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { connector.switchAccount(account); } }, - [connector] + [connector], ); const value = useMemo((): SKWallet => { diff --git a/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts b/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts index 1fd7590d..0434538f 100644 --- a/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts +++ b/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts @@ -1,8 +1,8 @@ import type { ChainWalletBase } from "@cosmos-kit/core"; -import type { AddressWithTokenDtoAdditionalAddresses } from "@stakekit/api-hooks"; import { useQuery } from "@tanstack/react-query"; import { EitherAsync, Left, List, Right } from "purify-ts"; import type { Connector } from "wagmi"; +import type { AddressWithTokenDtoAdditionalAddresses } from "../../domain/types/addresses"; import { getStorageItem } from "../../services/local-storage"; import type { CosmosConnector } from "../cosmos/cosmos-connector-meta"; import { isCosmosConnector } from "../cosmos/cosmos-connector-meta"; @@ -49,7 +49,7 @@ const getAdditionalAddresses = (args: { return getCosmosPubKey(args).map( (pubKey): AddressWithTokenDtoAdditionalAddresses => ({ cosmosPubKey: pubKey, - }) + }), ); } @@ -67,8 +67,8 @@ const getCosmosPubKey = (args: { return EitherAsync(() => args.connector.getAccounts()) .chain((accs) => EitherAsync.liftEither( - List.head(accs).toEither(new Error("no account")) - ) + List.head(accs).toEither(new Error("no account")), + ), ) .chain((acc) => { const skPubKey = prevSkPubKeys[acc]; @@ -82,7 +82,7 @@ const getCosmosPubKey = (args: { }) .chainLeft(() => EitherAsync(() => - args.chainWallet.client.getAccount!(args.chainWallet.chainId) + args.chainWallet.client.getAccount?.(args.chainWallet.chainId), ) .mapLeft((e) => { console.log("missing account error: ", e); @@ -90,5 +90,5 @@ const getCosmosPubKey = (args: { }) .map((account) => { return args.connector.toBase64(account.pubkey); - }) + }), ); diff --git a/packages/widget/src/providers/sk-wallet/use-connector-chains.ts b/packages/widget/src/providers/sk-wallet/use-connector-chains.ts index 48ffc765..d406c62d 100644 --- a/packages/widget/src/providers/sk-wallet/use-connector-chains.ts +++ b/packages/widget/src/providers/sk-wallet/use-connector-chains.ts @@ -27,7 +27,7 @@ export const useConnectorChains = ({ return () => sub.unsubscribe(); }, - [connector, subject] + [connector, subject], ); const getSnapshot = useCallback(() => { diff --git a/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts b/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts index 5ed6df32..051526e5 100644 --- a/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts +++ b/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts @@ -6,7 +6,7 @@ import { isCosmosConnector } from "../cosmos/cosmos-connector-meta"; export const useCosmosCW = (connector?: Connector) => { const [subject] = useState( - () => new BehaviorSubject(null) + () => new BehaviorSubject(null), ); const subscribe = useCallback( @@ -22,7 +22,7 @@ export const useCosmosCW = (connector?: Connector) => { return () => sub.unsubscribe(); }, - [connector, subject] + [connector, subject], ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-init.ts b/packages/widget/src/providers/sk-wallet/use-init.ts index a2a32887..8312e9de 100644 --- a/packages/widget/src/providers/sk-wallet/use-init.ts +++ b/packages/widget/src/providers/sk-wallet/use-init.ts @@ -43,16 +43,16 @@ export const useInit = () => { return EitherAsync.liftEither( List.find( (c) => c.id === "injected" || c.id === safeConfigMeta.id, - config.connectors as Connector[] - ).toEither(new Error("Could not find injected connector")) + config.connectors as Connector[], + ).toEither(new Error("Could not find injected connector")), ) .chain((injConnector) => EitherAsync(() => connect(config, { connector: injConnector, chainId: queryParamsInitChainId, - }) - ) + }), + ), ) .chainLeft(async () => Right(null)); }) @@ -65,7 +65,7 @@ export const useInit = () => { config.state.chainId !== queryParamsInitChainId ) { return EitherAsync(() => - switchChain(config, { chainId: queryParamsInitChainId }) + switchChain(config, { chainId: queryParamsInitChainId }), ).chainLeft(async () => Right(null)); } diff --git a/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts b/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts index 948ee68b..db3fe625 100644 --- a/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts +++ b/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts @@ -20,7 +20,7 @@ export const useLedgerAccounts = (connector?: Connector) => { return () => sub.unsubscribe(); }, - [connector, subject] + [connector, subject], ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts b/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts index 128a4814..03b97208 100644 --- a/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts +++ b/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts @@ -5,7 +5,7 @@ import { isLedgerLiveConnector } from "../ledger/ledger-live-connector-meta"; export const useLedgerCurrentAccountId = (connector?: Connector) => { const [subject] = useState( - () => new BehaviorSubject(undefined) + () => new BehaviorSubject(undefined), ); const subscribe = useCallback( @@ -21,7 +21,7 @@ export const useLedgerCurrentAccountId = (connector?: Connector) => { return () => sub.unsubscribe(); }, - [connector, subject] + [connector, subject], ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts b/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts index f73b8026..d32811d4 100644 --- a/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts +++ b/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts @@ -21,7 +21,7 @@ export const useLedgerDisabledChain = (connector?: Nullable) => { return () => sub.unsubscribe(); }, - [connector, subject] + [connector, subject], ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts b/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts index 84b8561e..d61999d7 100644 --- a/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts +++ b/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts @@ -28,9 +28,9 @@ export const useSyncExternalProvider = ({ () => List.find( (c) => isExternalProviderConnector(c), - connectors as Connector[] + connectors as Connector[], ).filter(isExternalProviderConnector), - [connectors] + [connectors], ); const connectRef = useSavedRef(connect); @@ -46,7 +46,7 @@ export const useSyncExternalProvider = ({ } externalProviderConnector.ifJust((val) => - connectRef.current({ connector: val }) + connectRef.current({ connector: val }), ); }, [ isConnected, @@ -59,7 +59,7 @@ export const useSyncExternalProvider = ({ useUpdateEffect(() => { connectorRef.current .chain((conn) => - Maybe.fromNullable(chainRef.current).map((c) => ({ c, conn })) + Maybe.fromNullable(chainRef.current).map((c) => ({ c, conn })), ) .ifJust((val) => { val.conn.onSupportedChainsChanged({ diff --git a/packages/widget/src/providers/stake-history/index.tsx b/packages/widget/src/providers/stake-history/index.tsx index d8a3dd0a..b7308421 100644 --- a/packages/widget/src/providers/stake-history/index.tsx +++ b/packages/widget/src/providers/stake-history/index.tsx @@ -27,7 +27,7 @@ export const ActionHistoryContextProvider = ({ const value = useMemo( () => [data, setActionHistoryData] as const, - [data, setActionHistoryData] + [data, setActionHistoryData], ); return ( @@ -42,7 +42,7 @@ const useActionHistory = () => { if (context === undefined) { throw new Error( - "useActionHistory must be used within a ActionHistoryContextProvider" + "useActionHistory must be used within a ActionHistoryContextProvider", ); } diff --git a/packages/widget/src/providers/substrate/config.ts b/packages/widget/src/providers/substrate/config.ts index 27f1e4f1..9659b0ed 100644 --- a/packages/widget/src/providers/substrate/config.ts +++ b/packages/widget/src/providers/substrate/config.ts @@ -40,12 +40,12 @@ const queryFn = async ({ const filteredSubstrateChainsMap: Partial = typeSafeObjectFromEntries( typeSafeObjectEntries(substrateChainsMap).filter( - ([_, v]) => networks.has(v.skChainName) - ) + ([_, v]) => networks.has(v.skChainName), + ), ); const substrateChains = Object.values(filteredSubstrateChainsMap).map( - (val) => val.wagmiChain + (val) => val.wagmiChain, ); const lunoKitChains = Object.values(filteredSubstrateChainsMap).map( @@ -61,7 +61,7 @@ const queryFn = async ({ : getNetworkLogo(val.skChainName), genesisHash: val.genesisHash as `0x${string}`, ss58Format: val.ss58Format, - }) + }), ); return Promise.resolve({ @@ -71,8 +71,8 @@ const queryFn = async ({ getSubstrateConnectors( substrateChains, lunoKitChains, - forceWalletConnectOnly - ) + forceWalletConnectOnly, + ), ), }); }, @@ -85,7 +85,7 @@ export const getConfig = (opts: Parameters[0]) => staleTime, queryKey, queryFn: () => queryFn(opts), - }) + }), ).mapLeft((e) => { console.log(e); return new Error("Could not get substrate config"); diff --git a/packages/widget/src/providers/substrate/substrate-connector-meta.ts b/packages/widget/src/providers/substrate/substrate-connector-meta.ts index 7f51a049..58b65fd0 100644 --- a/packages/widget/src/providers/substrate/substrate-connector-meta.ts +++ b/packages/widget/src/providers/substrate/substrate-connector-meta.ts @@ -21,5 +21,5 @@ export type StorageItem = { type SubstrateConnector = Connector & ExtraProps; export const isSubstrateConnector = ( - connector: Connector + connector: Connector, ): connector is SubstrateConnector => connector.type === configMeta.type; diff --git a/packages/widget/src/providers/substrate/substrate-connector.ts b/packages/widget/src/providers/substrate/substrate-connector.ts index 882eb889..96acf9c9 100644 --- a/packages/widget/src/providers/substrate/substrate-connector.ts +++ b/packages/widget/src/providers/substrate/substrate-connector.ts @@ -57,13 +57,13 @@ const createSubstrateConnector = ({ .chain((signer) => EitherAsync.liftEither( Maybe.fromNullable(signer?.signPayload?.bind(signer)).toEither( - new Error("signer missing") - ) + new Error("signer missing"), + ), ) .chain((signPayload) => EitherAsync(() => - signPayload({ ...payload.tx, withSignedTransaction: true }) - ) + signPayload({ ...payload.tx, withSignedTransaction: true }), + ), ) .chain((res) => { if (res.signedTransaction) { @@ -71,8 +71,8 @@ const createSubstrateConnector = ({ Right( typeof res.signedTransaction === "string" ? res.signedTransaction - : u8aToHex(res.signedTransaction) - ) + : u8aToHex(res.signedTransaction), + ), ); } @@ -81,34 +81,34 @@ const createSubstrateConnector = ({ const registry = new TypeRegistry(); registry.setMetadata( - registry.createType("Metadata", payload.metadataRpc) + registry.createType("Metadata", payload.metadataRpc), ); const extrinsic = registry.createType( "Extrinsic", { method: payload.tx.method }, - { version: payload.tx.version } + { version: payload.tx.version }, ); extrinsic.addSignature( payload.tx.address, res.signature, - payload.tx + payload.tx, ); return u8aToHex(extrinsic.toU8a()); - }) + }), ); - }) + }), ) .mapLeft( - (e) => new Error("Failed to sign transaction", { cause: e }) + (e) => new Error("Failed to sign transaction", { cause: e }), ), connect: async (args) => { config.emitter.emit("message", { type: "connecting" }); baseConnector.once("get_uri", (uri: string) => - baseConnector.emit("display_uri", uri) + baseConnector.emit("display_uri", uri), ); const accounts = await baseConnector.connect(name, lunoKitChains); @@ -150,13 +150,13 @@ const createSubstrateConnector = ({ getChainId: async () => $filteredChains.getValue()[0].id, isAuthorized: async () => { const isDisconnected = await config.storage?.getItem( - "substrate.disconnected" + "substrate.disconnected", ); if (isDisconnected) return false; const lastConnectedId = await config.storage?.getItem( - "substrate.lastConnectedId" + "substrate.lastConnectedId", ); return !!(lastConnectedId && lastConnectedId === baseConnector.id); @@ -184,7 +184,7 @@ const createSubstrateConnector = ({ export const getSubstrateConnectors = ( chains: ReadonlyArray, lunoKitChains: LunoKitChain[], - forceWalletConnectOnly: boolean + forceWalletConnectOnly: boolean, ): WalletList[number] => { const subwallet = subwalletConnector(); const talisman = talismanConnector(); diff --git a/packages/widget/src/providers/theme-wrapper.tsx b/packages/widget/src/providers/theme-wrapper.tsx index 2f0ec63b..e6cd891c 100644 --- a/packages/widget/src/providers/theme-wrapper.tsx +++ b/packages/widget/src/providers/theme-wrapper.tsx @@ -45,7 +45,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { return merge( structuredClone(lightTheme), theme.lightMode, - overrides + overrides, ); } @@ -56,7 +56,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { return lightTheme; }) .unsafeCoerce(), - [theme, variant] + [theme, variant], ); const finalDarkTheme = useMemo( @@ -64,7 +64,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { "darkMode" in theme ? merge(structuredClone(darkTheme), theme.darkMode) : null, - [theme] + [theme], ); return ( @@ -80,7 +80,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { finalDarkTheme ? `@media (prefers-color-scheme: dark) { ${rootSelector} {${assignInlineVars( vars, - finalDarkTheme + finalDarkTheme, )}} }` : null, ].join(""), diff --git a/packages/widget/src/providers/tracking/index.tsx b/packages/widget/src/providers/tracking/index.tsx index 5649ac47..5d4d513c 100644 --- a/packages/widget/src/providers/tracking/index.tsx +++ b/packages/widget/src/providers/tracking/index.tsx @@ -74,7 +74,7 @@ type TrackingContextType = { }; export const TrackingContext = createContext( - undefined + undefined, ); export const TrackingContextProvider = ({ @@ -90,10 +90,10 @@ export const TrackingContextProvider = ({ tracking?.trackEvent?.(trackEventMap[event], ...(props ? [props] : [])); variantTracking?.trackEvent?.( trackEventMap[event], - ...(props ? [props] : []) + ...(props ? [props] : []), ); }, - [tracking, variantTracking] + [tracking, variantTracking], ); const trackPageView = useCallback( @@ -101,15 +101,15 @@ export const TrackingContextProvider = ({ tracking?.trackPageView?.(trackPageMap[page], ...(props ? [props] : [])); variantTracking?.trackPageView?.( trackPageMap[page], - ...(props ? [props] : []) + ...(props ? [props] : []), ); }, - [tracking, variantTracking] + [tracking, variantTracking], ); const value = useMemo( () => ({ trackEvent, trackPageView }), - [trackEvent, trackPageView] + [trackEvent, trackPageView], ); return ( diff --git a/packages/widget/src/providers/wagmi/index.ts b/packages/widget/src/providers/wagmi/index.ts index 381ade10..e5968f79 100644 --- a/packages/widget/src/providers/wagmi/index.ts +++ b/packages/widget/src/providers/wagmi/index.ts @@ -133,13 +133,13 @@ const buildWagmiConfig = async (opts: { miscConfig: m, substrateConfig: s, queryParams: qp, - })) - ) - ) - ) - ) - ) - ) + })), + ), + ), + ), + ), + ), + ), ) .chain((val) => getLedgerLiveConfig({ @@ -151,13 +151,13 @@ const buildWagmiConfig = async (opts: { }, queryClient: opts.queryClient, queryParams: val.queryParams, - }).map((l) => ({ ...val, ledgerLiveConnector: l })) + }).map((l) => ({ ...val, ledgerLiveConnector: l })), ) .chain((val) => EitherAsync.liftEither(Maybe.fromFalsy(opts.isSafe).toEither(null)) .chain(() => getSafeConnector({ queryClient: opts.queryClient })) .chainLeft((e) => EitherAsync.liftEither(e ? Left(e) : Right(null))) - .map((s) => ({ ...val, safeConnector: s })) + .map((s) => ({ ...val, safeConnector: s })), ) .map((val) => { const { @@ -272,9 +272,9 @@ const buildWagmiConfig = async (opts: { : wallet; return maybeMapped; - } + }, ), - })) + })), ) .map((walletList) => opts.mapWalletListFn?.(walletList) ?? walletList) .map((walletList) => { @@ -296,7 +296,7 @@ const buildWagmiConfig = async (opts: { : config.chains, }), }; - } + }, ), })); }) @@ -310,7 +310,7 @@ const buildWagmiConfig = async (opts: { val.miscConfig.miscChainsMap[n as keyof MiscChainsMap] ?? val.substrateConfig.substrateChainsMap[ n as keyof SubstrateChainsMap - ] + ], ) .map((c) => c.wagmiChain.id) .extract(); @@ -331,11 +331,11 @@ const buildWagmiConfig = async (opts: { ...prev, ...uniqwith( mipdStore.getProviders(), - (a, b) => a.info.rdns === b.info.rdns + (a, b) => a.info.rdns === b.info.rdns, ).map((p) => ({ rkDetails: { chainGroup: evmChainGroup }, ...wagmiConfig._internal.connectors.setup( - wagmiConfig._internal.connectors.providerDetailToConnector(p) + wagmiConfig._internal.connectors.providerDetailToConnector(p), ), })), ]); @@ -347,9 +347,9 @@ const buildWagmiConfig = async (opts: { (p) => ({ rkDetails: { chainGroup: evmChainGroup }, ...wagmiConfig._internal.connectors.setup( - wagmiConfig._internal.connectors.providerDetailToConnector(p) + wagmiConfig._internal.connectors.providerDetailToConnector(p), ), - }) + }), ), ]); }); @@ -400,7 +400,7 @@ export const useWagmiConfig = () => { i18n, url: yieldsApiUrl ?? config.env.yieldsApiUrl, }), - [apiKey, i18n, yieldsApiUrl] + [apiKey, i18n, yieldsApiUrl], ); const externalProvidersRef = useSavedRef(externalProviders) as diff --git a/packages/widget/src/providers/wagmi/utils.ts b/packages/widget/src/providers/wagmi/utils.ts index e81efadb..51b075f3 100644 --- a/packages/widget/src/providers/wagmi/utils.ts +++ b/packages/widget/src/providers/wagmi/utils.ts @@ -26,7 +26,7 @@ export const createWallet = isWalletConnect?: never; projectId?: never; } - ) + ), ): WalletList[number]["wallets"][number] => () => { const def = { diff --git a/packages/widget/src/providers/yield-api-client-provider/actions.ts b/packages/widget/src/providers/yield-api-client-provider/actions.ts index 41e5ecf0..ee15c651 100644 --- a/packages/widget/src/providers/yield-api-client-provider/actions.ts +++ b/packages/widget/src/providers/yield-api-client-provider/actions.ts @@ -1,43 +1,32 @@ import type { - AddressesDto, - ActionDto as LegacyActionDto, - TokenDto, - YieldDto, -} from "@stakekit/api-hooks"; -import { adaptActionDto } from "./compat"; -import { getResponseData } from "./request-helpers"; -import type { - YieldApiFetchClient, + ActionDto, YieldCreateActionDto, YieldCreateManageActionDto, -} from "./types"; +} from "../../domain/types/action"; +import type { AddressesDto } from "../../domain/types/addresses"; +import type { YieldApiFetchClient } from "../../domain/types/yield-api"; +import type { Yield } from "../../domain/types/yields"; +import { getResponseData } from "./request-helpers"; export const createEnterAction = async ({ addresses, fetchClient, - inputToken, requestDto, yieldDto, }: { addresses: AddressesDto; fetchClient: YieldApiFetchClient; - inputToken: TokenDto; requestDto: YieldCreateActionDto; - yieldDto: YieldDto; -}): Promise => { - const actionDto = await getResponseData( + yieldDto: Yield; +}): Promise => { + void addresses; + void yieldDto; + + return getResponseData( fetchClient.POST("/v1/actions/enter", { body: requestDto, - }) + }), ); - - return adaptActionDto({ - actionDto, - addresses, - gasFeeToken: yieldDto.metadata.gasFeeToken, - inputToken, - yieldDto, - }); }; export const createExitAction = async ({ @@ -49,20 +38,16 @@ export const createExitAction = async ({ addresses: AddressesDto; fetchClient: YieldApiFetchClient; requestDto: YieldCreateActionDto; - yieldDto: YieldDto; -}): Promise => { - const actionDto = await getResponseData( + yieldDto: Yield; +}): Promise => { + void addresses; + void yieldDto; + + return getResponseData( fetchClient.POST("/v1/actions/exit", { body: requestDto, - }) + }), ); - - return adaptActionDto({ - actionDto, - addresses, - gasFeeToken: yieldDto.metadata.gasFeeToken, - yieldDto, - }); }; export const createManageAction = async ({ @@ -74,20 +59,16 @@ export const createManageAction = async ({ addresses: AddressesDto; fetchClient: YieldApiFetchClient; requestDto: YieldCreateManageActionDto; - yieldDto: YieldDto; -}): Promise => { - const actionDto = await getResponseData( + yieldDto: Yield; +}): Promise => { + void addresses; + void yieldDto; + + return getResponseData( fetchClient.POST("/v1/actions/manage", { body: requestDto, - }) + }), ); - - return adaptActionDto({ - actionDto, - addresses, - gasFeeToken: yieldDto.metadata.gasFeeToken, - yieldDto, - }); }; export const listActions = async ({ @@ -110,7 +91,7 @@ export const listActions = async ({ limit, }, }, - }) + }), ); export const getTransaction = async ({ @@ -127,7 +108,7 @@ export const getTransaction = async ({ transactionId, }, }, - }) + }), ); export const submitTransaction = async ({ @@ -149,7 +130,7 @@ export const submitTransaction = async ({ body: { signedTransaction, }, - }) + }), ); export const submitTransactionHash = async ({ @@ -171,5 +152,5 @@ export const submitTransactionHash = async ({ body: { hash, }, - }) + }), ); diff --git a/packages/widget/src/providers/yield-api-client-provider/compat.ts b/packages/widget/src/providers/yield-api-client-provider/compat.ts deleted file mode 100644 index e1f06903..00000000 --- a/packages/widget/src/providers/yield-api-client-provider/compat.ts +++ /dev/null @@ -1,519 +0,0 @@ -import type { - AddressesDto, - ActionDto as LegacyActionDto, - TokenDto as LegacyTokenDto, - TransactionDto as LegacyTransactionDto, - ValidatorDto as LegacyValidatorDto, - YieldDto as LegacyYieldDto, -} from "@stakekit/api-hooks"; -import type { - YieldActionDto, - YieldDto, - YieldTokenDto, - YieldTransactionDto, - YieldValidatorDto, -} from "./types"; - -const NATIVE_TOKEN_PLACEHOLDER = "0x"; - -const toLower = (value: string) => value.toLowerCase(); - -type EncodedGasEstimate = { - amount?: string | null; - gasLimit?: string | null; - token?: YieldTokenDto | LegacyTokenDto | null; -}; - -const mapToken = ( - token: YieldTokenDto | LegacyTokenDto | null | undefined -): LegacyTokenDto | undefined => { - if (!token) return undefined; - - return { ...token } as LegacyTokenDto; -}; - -const uniqTokens = ( - tokens: (YieldTokenDto | LegacyTokenDto | null | undefined)[] -) => { - const seen = new Set(); - - return tokens.flatMap((token) => { - const mapped = mapToken(token); - - if (!mapped) return []; - - const key = `${mapped.network}:${mapped.address?.toLowerCase() ?? ""}:${ - mapped.symbol - }`; - - if (seen.has(key)) { - return []; - } - - seen.add(key); - return [mapped]; - }); -}; - -const secondsToDays = (seconds: number | undefined) => { - if (seconds === undefined) return undefined; - - return { days: Math.round(seconds / 86400) }; -}; - -const getRewardType = ({ - yieldDto, - legacyYieldDto, -}: { - yieldDto: YieldDto; - legacyYieldDto: LegacyYieldDto | null; -}): LegacyYieldDto["rewardType"] => { - const rateType = yieldDto.rewardRate?.rateType?.toLowerCase(); - - if (rateType === "apr" || rateType === "apy") { - return rateType; - } - - return legacyYieldDto?.rewardType ?? "variable"; -}; - -const getArgumentConfig = ( - yieldDto: YieldDto, - legacyYieldDto: LegacyYieldDto | null, - type: "enter" | "exit" -) => { - const fields = yieldDto.mechanics?.arguments?.[type]?.fields ?? []; - const legacyArgs = legacyYieldDto?.args?.[type]?.args ?? {}; - const nextArgs = { ...legacyArgs } as Record; - - for (const field of fields) { - const legacyField = (legacyArgs as Record)[field.name]; - const common = { - required: !!field.required, - ...(field.minimum !== undefined && field.minimum !== null - ? { minimum: Number(field.minimum) } - : {}), - ...(field.maximum !== undefined && field.maximum !== null - ? { maximum: Number(field.maximum) } - : {}), - ...(field.options ? { options: field.options } : {}), - }; - - nextArgs[field.name] = - legacyField && - typeof legacyField === "object" && - !Array.isArray(legacyField) - ? { - ...legacyField, - ...common, - } - : common; - } - - return { - ...(legacyYieldDto?.args?.[type] ?? {}), - args: nextArgs, - }; -}; - -const getRewardTokens = ({ - yieldDto, - legacyYieldDto, -}: { - yieldDto: YieldDto; - legacyYieldDto: LegacyYieldDto | null; -}) => { - if (legacyYieldDto?.metadata.rewardTokens?.length) { - return legacyYieldDto.metadata.rewardTokens; - } - - const seen = new Set(); - const derived = uniqTokens( - yieldDto.rewardRate?.components?.map((component) => component.token) ?? [] - ).filter((token) => { - const key = `${token.network}:${token.address?.toLowerCase() ?? ""}`; - - if (seen.has(key)) { - return false; - } - - seen.add(key); - return true; - }); - - if (derived.length) { - return derived; - } - - return legacyYieldDto?.metadata.rewardTokens; -}; - -const getMetadata = ({ - yieldDto, - legacyYieldDto, -}: { - yieldDto: YieldDto; - legacyYieldDto: LegacyYieldDto | null; -}): LegacyYieldDto["metadata"] => { - const fallbackMetadata = legacyYieldDto?.metadata; - const mechanics = yieldDto.mechanics; - const metadata = yieldDto.metadata; - const yieldTypeMap = { - staking: "staking", - restaking: "restaking", - lending: "lending", - vault: "vault", - liquidity_pool: "vault", - concentrated_liquidity_pool: "vault", - fixed_yield: "vault", - real_world_asset: "vault", - } as const; - const type = - yieldTypeMap[(mechanics?.type ?? "vault") as keyof typeof yieldTypeMap] ?? - "vault"; - - const token = mapToken(yieldDto.token) ?? fallbackMetadata?.token; - const tokens = uniqTokens([ - ...(yieldDto.tokens ?? []), - ...(yieldDto.inputTokens ?? []), - ...(fallbackMetadata?.tokens ?? []), - ]); - const gasFeeToken = - mapToken(mechanics?.gasFeeToken) ?? fallbackMetadata?.gasFeeToken; - - return { - ...(fallbackMetadata ?? {}), - name: metadata?.name ?? fallbackMetadata?.name ?? "", - description: metadata?.description ?? fallbackMetadata?.description ?? "", - documentation: - metadata?.documentation ?? fallbackMetadata?.documentation ?? "", - logoURI: metadata?.logoURI ?? fallbackMetadata?.logoURI ?? "", - type, - token: - token ?? - fallbackMetadata?.token ?? - (mapToken(yieldDto.token) as LegacyTokenDto), - tokens: tokens.length ? tokens : fallbackMetadata?.tokens, - rewardTokens: getRewardTokens({ yieldDto, legacyYieldDto }), - rewardSchedule: - mechanics?.rewardSchedule ?? fallbackMetadata?.rewardSchedule, - rewardClaiming: - mechanics?.rewardClaiming ?? fallbackMetadata?.rewardClaiming, - cooldownPeriod: - secondsToDays(mechanics?.cooldownPeriod?.seconds) ?? - fallbackMetadata?.cooldownPeriod, - warmupPeriod: - secondsToDays(mechanics?.warmupPeriod?.seconds) ?? - fallbackMetadata?.warmupPeriod, - gasFeeToken: - gasFeeToken ?? - fallbackMetadata?.gasFeeToken ?? - (mapToken(yieldDto.token) as LegacyTokenDto), - supportsLedgerWalletApi: - mechanics?.supportsLedgerWalletApi ?? - fallbackMetadata?.supportsLedgerWalletApi, - supportsMultipleValidators: - mechanics?.requiresValidatorSelection ?? - fallbackMetadata?.supportsMultipleValidators, - supportedStandards: - metadata?.supportedStandards ?? fallbackMetadata?.supportedStandards, - fee: { - enabled: !!mechanics?.fee || !!fallbackMetadata?.fee?.enabled, - depositFee: - !!mechanics?.fee?.deposit || !!fallbackMetadata?.fee?.depositFee, - managementFee: - !!mechanics?.fee?.management || !!fallbackMetadata?.fee?.managementFee, - performanceFee: - !!mechanics?.fee?.performance || - !!fallbackMetadata?.fee?.performanceFee, - }, - } as LegacyYieldDto["metadata"]; -}; - -export const adaptValidatorDto = ( - validatorDto: YieldValidatorDto | LegacyValidatorDto -): LegacyValidatorDto => { - const legacyValidator = validatorDto as LegacyValidatorDto; - const rewardRate = - "rewardRate" in validatorDto ? validatorDto.rewardRate : undefined; - const providerId = - "provider" in validatorDto - ? (validatorDto.provider?.id ?? validatorDto.providerId) - : validatorDto.providerId; - const image = - "logoURI" in validatorDto ? validatorDto.logoURI : legacyValidator.image; - const stakedBalance = - "tvl" in validatorDto ? validatorDto.tvl : legacyValidator.stakedBalance; - - return { - address: validatorDto.address, - apr: "apr" in validatorDto ? validatorDto.apr : rewardRate?.total, - commission: validatorDto.commission, - image, - minimumStake: validatorDto.minimumStake, - name: validatorDto.name, - nominatorCount: validatorDto.nominatorCount, - preferred: validatorDto.preferred, - pricePerShare: validatorDto.pricePerShare, - providerId, - remainingPossibleStake: validatorDto.remainingPossibleStake, - remainingSlots: validatorDto.remainingSlots, - stakedBalance, - status: validatorDto.status as LegacyValidatorDto["status"], - subnetId: validatorDto.subnetId, - subnetName: - "subnetName" in validatorDto ? validatorDto.subnetName : undefined, - tokenSymbol: validatorDto.tokenSymbol, - votingPower: validatorDto.votingPower, - website: validatorDto.website, - }; -}; - -export const adaptYieldDto = ({ - yieldDto, - legacyYieldDto, -}: { - yieldDto: YieldDto; - legacyYieldDto: LegacyYieldDto | null; -}): LegacyYieldDto => { - const { validators: _legacyValidators, ...legacyYieldDtoWithoutValidators } = - legacyYieldDto ?? {}; - const rewardRate = - yieldDto.rewardRate?.total ?? legacyYieldDto?.rewardRate ?? 0; - const tokens = uniqTokens([ - ...(yieldDto.tokens ?? []), - ...(yieldDto.inputTokens ?? []), - ...(legacyYieldDto?.tokens ?? []), - ]); - - const token = - mapToken(yieldDto.token) ?? - legacyYieldDto?.token ?? - (tokens[0] as LegacyTokenDto); - - return { - ...legacyYieldDtoWithoutValidators, - id: yieldDto.id, - token, - tokens: tokens.length ? tokens : (legacyYieldDto?.tokens ?? [token]), - metadata: getMetadata({ yieldDto, legacyYieldDto }), - rewardRate, - rewardRateDetails: yieldDto.rewardRate, - rewardType: getRewardType({ yieldDto, legacyYieldDto }), - status: { - ...(legacyYieldDto?.status ?? {}), - ...(yieldDto.status ?? {}), - }, - args: { - ...(legacyYieldDto?.args ?? {}), - enter: getArgumentConfig(yieldDto, legacyYieldDto, "enter"), - ...(yieldDto.mechanics?.arguments?.exit || legacyYieldDto?.args?.exit - ? { - exit: getArgumentConfig(yieldDto, legacyYieldDto, "exit"), - } - : {}), - } as LegacyYieldDto["args"], - feeConfigurations: legacyYieldDto?.feeConfigurations ?? [], - apy: rewardRate, - inputTokens: yieldDto.inputTokens, - outputToken: yieldDto.outputToken, - network: yieldDto.network, - chainId: yieldDto.chainId, - mechanics: yieldDto.mechanics, - statistics: yieldDto.statistics, - risk: yieldDto.risk, - providerId: yieldDto.providerId, - tags: yieldDto.tags, - state: yieldDto.state, - curator: yieldDto.curator, - } as unknown as LegacyYieldDto; -}; - -const getCurrentStepIndex = (transactions: LegacyTransactionDto[]) => { - const idx = transactions.findIndex( - (transaction) => - transaction.status !== "CONFIRMED" && transaction.status !== "SKIPPED" - ); - - if (idx >= 0) { - return idx; - } - - return Math.max(transactions.length - 1, 0); -}; - -export const toActionInputToken = ({ - inputToken, - yieldDto, - inputTokenValue, -}: { - inputToken?: LegacyTokenDto; - yieldDto?: LegacyYieldDto | null; - inputTokenValue?: string | null; -}) => { - if (inputToken) { - return inputToken; - } - - if (!yieldDto) { - return undefined; - } - - if (!inputTokenValue) { - return yieldDto.token ?? yieldDto.tokens?.[0]; - } - - const needle = toLower(inputTokenValue); - - return ( - [ - yieldDto.token, - ...(yieldDto.tokens ?? []), - ...(yieldDto.metadata.tokens ?? []), - ].find((token) => { - const address = token.address ? toLower(token.address) : null; - return ( - address === needle || - token.symbol.toLowerCase() === needle || - (needle === NATIVE_TOKEN_PLACEHOLDER && !token.address) - ); - }) ?? - yieldDto.token ?? - yieldDto.tokens?.[0] - ); -}; - -export const adaptTransactionDto = ({ - transactionDto, - gasFeeToken, - stakeId, -}: { - transactionDto: YieldTransactionDto; - gasFeeToken?: LegacyTokenDto; - stakeId: string; -}): LegacyTransactionDto => ({ - id: transactionDto.id, - accountAddresses: undefined, - annotatedTransaction: (transactionDto.annotatedTransaction ?? - {}) as unknown as LegacyTransactionDto["annotatedTransaction"], - broadcastedAt: transactionDto.broadcastedAt, - createdAt: transactionDto.createdAt, - error: transactionDto.error ?? null, - explorerUrl: transactionDto.explorerUrl ?? null, - gasEstimate: parseGasEstimate(transactionDto.gasEstimate, gasFeeToken), - hash: transactionDto.hash, - isMessage: transactionDto.isMessage ?? false, - ledgerHwAppId: null, - network: transactionDto.network as LegacyTransactionDto["network"], - signedTransaction: transactionDto.signedTransaction, - stakeId, - status: transactionDto.status as LegacyTransactionDto["status"], - stepIndex: transactionDto.stepIndex ?? 0, - structuredTransaction: (transactionDto.structuredTransaction ?? - {}) as unknown as LegacyTransactionDto["structuredTransaction"], - type: transactionDto.type as LegacyTransactionDto["type"], - unsignedTransaction: - typeof transactionDto.unsignedTransaction === "string" - ? transactionDto.unsignedTransaction - : transactionDto.unsignedTransaction - ? JSON.stringify(transactionDto.unsignedTransaction) - : null, -}); - -const parseGasEstimate = ( - gasEstimate: YieldTransactionDto["gasEstimate"], - gasFeeToken?: LegacyTokenDto -): LegacyTransactionDto["gasEstimate"] => { - if (!gasEstimate) { - return null; - } - - try { - const parsed = JSON.parse(gasEstimate) as EncodedGasEstimate | null; - - if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { - if (!gasFeeToken) { - return null; - } - - return { - amount: gasEstimate, - token: gasFeeToken, - }; - } - - const amount = parsed.amount ?? null; - const token = mapToken(parsed.token) ?? gasFeeToken; - - if (!amount || !token) { - return null; - } - - return { - amount, - token, - ...(parsed.gasLimit ? { gasLimit: parsed.gasLimit } : {}), - }; - } catch { - if (!gasFeeToken) { - return null; - } - - return { - amount: gasEstimate, - token: gasFeeToken, - }; - } -}; - -export const adaptActionDto = ({ - actionDto, - addresses, - gasFeeToken, - inputToken, - yieldDto, -}: { - actionDto: YieldActionDto; - addresses?: AddressesDto; - gasFeeToken?: LegacyTokenDto; - inputToken?: LegacyTokenDto; - yieldDto?: LegacyYieldDto | null; -}): LegacyActionDto => { - const adaptedTransactions = actionDto.transactions.map((transactionDto) => - adaptTransactionDto({ - transactionDto, - gasFeeToken: - gasFeeToken ?? yieldDto?.metadata.gasFeeToken ?? yieldDto?.token, - stakeId: actionDto.yieldId, - }) - ); - - const rawArguments = actionDto.rawArguments; - const validatorAddresses = - rawArguments?.validatorAddresses ?? - (rawArguments?.validatorAddress ? [rawArguments.validatorAddress] : null); - - return { - id: actionDto.id, - accountAddresses: undefined, - addresses: addresses ?? { address: actionDto.address }, - amount: actionDto.amount, - completedAt: actionDto.completedAt, - createdAt: actionDto.createdAt, - currentStepIndex: getCurrentStepIndex(adaptedTransactions), - inputToken: toActionInputToken({ - inputToken, - yieldDto, - inputTokenValue: rawArguments?.inputToken, - }), - integrationId: actionDto.yieldId, - projectId: null, - status: actionDto.status, - tokenId: rawArguments?.tokenId ?? null, - transactions: adaptedTransactions, - type: actionDto.type as LegacyActionDto["type"], - USDAmount: actionDto.amountUsd, - validatorAddress: rawArguments?.validatorAddress ?? null, - validatorAddresses, - }; -}; diff --git a/packages/widget/src/providers/yield-api-client-provider/index.tsx b/packages/widget/src/providers/yield-api-client-provider/index.tsx index 93f729b9..d52f1d23 100644 --- a/packages/widget/src/providers/yield-api-client-provider/index.tsx +++ b/packages/widget/src/providers/yield-api-client-provider/index.tsx @@ -7,14 +7,14 @@ import { createContext, useContext, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { waitForDelayedApiRequests } from "../../common/delay-api-requests"; import { config } from "../../config"; +import type { YieldApiFetchClient } from "../../domain/types/yield-api"; import { handleGeoBlockResponse } from "../../hooks/use-geo-block"; import { handleRichErrorResponse } from "../../hooks/use-rich-errors"; import type { paths } from "../../types/yield-api-schema"; import { useSettings } from "../settings"; -import type { YieldApiFetchClient } from "./types"; const QueryContext = createContext | undefined>( - undefined + undefined, ); const FetchContext = createContext(undefined); @@ -104,7 +104,7 @@ export const useYieldApiClient = () => { if (!value) { throw new Error( - "useYieldApiClient must be used within a YieldApiClientProvider" + "useYieldApiClient must be used within a YieldApiClientProvider", ); } @@ -116,7 +116,7 @@ export const useYieldApiFetchClient = () => { if (!value) { throw new Error( - "useYieldApiFetchClient must be used within a YieldApiClientProvider" + "useYieldApiFetchClient must be used within a YieldApiClientProvider", ); } diff --git a/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts index 6fbe7630..6ba78bb0 100644 --- a/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts +++ b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts @@ -1,5 +1,5 @@ -import type { AddressWithTokenDtoAdditionalAddresses } from "@stakekit/api-hooks"; -import type { YieldActionArgumentsDto } from "./types"; +import type { YieldActionArgumentsDto } from "../../domain/types/action"; +import type { AddressWithTokenDtoAdditionalAddresses } from "../../domain/types/addresses"; export const withAdditionalAddresses = ({ additionalAddresses, @@ -22,7 +22,7 @@ export const getResponseData = async < error?: unknown; }, >( - promise: Promise + promise: Promise, ): Promise> => { const response = await promise; diff --git a/packages/widget/src/providers/yield-api-client-provider/types.ts b/packages/widget/src/providers/yield-api-client-provider/types.ts index 3919b904..3f168ac8 100644 --- a/packages/widget/src/providers/yield-api-client-provider/types.ts +++ b/packages/widget/src/providers/yield-api-client-provider/types.ts @@ -1,39 +1,32 @@ -import type { Client } from "openapi-fetch"; -import type { components, paths } from "../../types/yield-api-schema"; - -export type YieldApiFetchClient = Client; - -export type YieldDto = components["schemas"]["YieldDto"]; -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 YieldRewardDto = components["schemas"]["RewardDto"]; -export type YieldRewardRateDto = components["schemas"]["RewardRateDto"]; -export type YieldActionArgumentsDto = - components["schemas"]["ActionArgumentsDto"]; -export type YieldCreateActionDto = components["schemas"]["CreateActionDto"]; -export type YieldCreateManageActionDto = - components["schemas"]["CreateManageActionDto"]; -export type YieldPendingActionType = - | YieldPendingActionDto["type"] - | NonNullable; - -export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; - -export type YieldBalanceDto = components["schemas"]["BalanceDto"]; -export type YieldActionDto = components["schemas"]["ActionDto"]; -export type YieldTransactionDto = components["schemas"]["TransactionDto"]; - -export type YieldBalancesByYieldDto = components["schemas"]["YieldBalancesDto"]; -export type YieldSingleBalancesResponseDto = - components["schemas"]["YieldBalancesDto"]; - -export type YieldBalancesResponseDto = - components["schemas"]["BalancesResponseDto"]; -export type YieldPaginatedResponseDto = - components["schemas"]["PaginatedResponseDto"]; +export type { + YieldActionArgumentsDto, + YieldActionDto, + YieldCreateActionDto, + YieldCreateManageActionDto, + YieldTransactionDto, +} from "../../domain/types/action"; +export type { + YieldPendingActionDto, + YieldPendingActionType, +} from "../../domain/types/pending-action"; +export type { + YieldBalanceDto, + YieldBalancesByYieldDto, + YieldBalancesRequestDto, + YieldBalancesResponseDto, + YieldBalanceType, + YieldPaginatedResponseDto, + YieldSingleBalancesRequestDto, + YieldSingleBalancesResponseDto, +} from "../../domain/types/positions"; +export type { + YieldRewardDto, + YieldRewardRateDto, +} from "../../domain/types/reward-rate"; +export type { YieldTokenDto } from "../../domain/types/tokens"; +export type { YieldValidatorDto } from "../../domain/types/validators"; +export type { YieldApiFetchClient } from "../../domain/types/yield-api"; +export type { + Yield, + YieldApiYieldDto as YieldDto, +} from "../../domain/types/yields"; diff --git a/packages/widget/src/services/local-storage.ts b/packages/widget/src/services/local-storage.ts index 097b1376..25f0a27c 100644 --- a/packages/widget/src/services/local-storage.ts +++ b/packages/widget/src/services/local-storage.ts @@ -20,7 +20,7 @@ type LocalStorageValue = GetType< >; export const getStorageItem = ( - key: K + key: K, ): Either | null> => { const w = MaybeWindow.extractNullable(); @@ -36,23 +36,23 @@ export const getStorageItem = ( codecs[key] .decode(parsedVal) .map((val) => val as LocalStorageValue) - .mapLeft((e) => new Error(e)) + .mapLeft((e) => new Error(e)), ); }; export const setStorageItem = ( key: K, - value: GetType<(typeof codecs)[K]> + value: GetType<(typeof codecs)[K]>, ) => { const w = MaybeWindow.extractNullable(); return Either.encase(() => - w?.localStorage.setItem(key, JSON.stringify(value)) + w?.localStorage.setItem(key, JSON.stringify(value)), ).ifRight(() => notify(key)); }; type Listener = ( - val: GetType<(typeof codecs)[K]> + val: GetType<(typeof codecs)[K]>, ) => void; const listeners: { [Key in keyof LocalStorageKV]: Map } = { @@ -62,7 +62,7 @@ const listeners: { [Key in keyof LocalStorageKV]: Map } = { export const addLocalStorageListener = ( key: K, - listener: Listener + listener: Listener, ) => { listeners[key].set(listener, listener); @@ -73,6 +73,6 @@ const notify = (key: keyof LocalStorageKV) => { getStorageItem(key).ifRight((val) => listeners[key].forEach((listener) => { listener(val as GetType<(typeof codecs)[typeof key]>); - }) + }), ); }; diff --git a/packages/widget/src/styles/theme/atoms.css.ts b/packages/widget/src/styles/theme/atoms.css.ts index 7158d99e..f0486614 100644 --- a/packages/widget/src/styles/theme/atoms.css.ts +++ b/packages/widget/src/styles/theme/atoms.css.ts @@ -21,7 +21,7 @@ const responsiveAtomicProperties = defineProperties({ return acc; }, - {} as { [Key in Breakpoint]: { "@media"?: string } } + {} as { [Key in Breakpoint]: { "@media"?: string } }, ), defaultCondition: "mobile", @@ -51,7 +51,7 @@ const colorAtomicProperties = defineProperties({ export const atoms = createSprinkles( unresponsiveAtomicProperties, responsiveAtomicProperties, - colorAtomicProperties + colorAtomicProperties, ); export type Atoms = Parameters[0]; diff --git a/packages/widget/src/styles/theme/contract.css.ts b/packages/widget/src/styles/theme/contract.css.ts index 1749dde9..08481453 100644 --- a/packages/widget/src/styles/theme/contract.css.ts +++ b/packages/widget/src/styles/theme/contract.css.ts @@ -31,5 +31,5 @@ export const vars = createGlobalThemeContract( `sk-${path .join("-") .replace(/([a-z])([A-Z])/g, "$1-$2") - .toLowerCase()}` + .toLowerCase()}`, ); diff --git a/packages/widget/src/styles/theme/global.css.ts b/packages/widget/src/styles/theme/global.css.ts index 23b2b73a..a9a61f1b 100644 --- a/packages/widget/src/styles/theme/global.css.ts +++ b/packages/widget/src/styles/theme/global.css.ts @@ -58,7 +58,7 @@ globalStyle( margin: "0", }, }, - } + }, ); globalStyle(`${rootSelector} input[type=number]`, { @@ -78,7 +78,7 @@ globalStyle( height: "auto", }, }, - } + }, ); globalStyle("button.simple-display-wallet-button", { @@ -105,7 +105,7 @@ globalStyle( { maxHeight: "80vh", overflow: "scroll", - } + }, ); globalStyle( @@ -113,7 +113,7 @@ globalStyle( { width: 0, height: 0, - } + }, ); globalStyle( @@ -121,7 +121,7 @@ globalStyle( { width: 0, height: 0, - } + }, ); globalStyle( @@ -129,12 +129,12 @@ globalStyle( { width: 0, height: 0, - } + }, ); globalStyle( '[data-rk="stakekit"][aria-labelledby="rk_connect_title"][aria-modal="true"][role="dialog"]', { zIndex: 990, - } + }, ); diff --git a/packages/widget/src/styles/tokens/breakpoints.ts b/packages/widget/src/styles/tokens/breakpoints.ts index 124690d4..1421389b 100644 --- a/packages/widget/src/styles/tokens/breakpoints.ts +++ b/packages/widget/src/styles/tokens/breakpoints.ts @@ -11,6 +11,6 @@ export const minMediaQuery = (breakpoint: Breakpoint) => export const minContainerWidth = ( containerName: string, - breakpoint: Breakpoint | number + breakpoint: Breakpoint | number, ) => `${containerName} (min-width: ${typeof breakpoint === "number" ? breakpoint : breakpoints[breakpoint]}px)`; diff --git a/packages/widget/src/translation/index.ts b/packages/widget/src/translation/index.ts index 74d5bc46..dd2e0073 100644 --- a/packages/widget/src/translation/index.ts +++ b/packages/widget/src/translation/index.ts @@ -39,7 +39,7 @@ i18nInstance.on("languageChanged", (lng) => { }); i18nInstance.services.formatter?.add("lowercase", (value, _, __) => - value.toLowerCase() + value.toLowerCase(), ); export const useLoadErrorTranslations = () => { @@ -57,12 +57,12 @@ export const useLoadErrorTranslations = () => { ( await EitherAsync(() => apiClient.get>( - `https://i18n.stakek.it/locales/${lng}/errors.json` - ) + `https://i18n.stakek.it/locales/${lng}/errors.json`, + ), ).ifRight((res) => i18n.addResourceBundle(i18n.language, "translation", { errors: res.data, - }) + }), ) ).unsafeCoerce(), }); diff --git a/packages/widget/src/types/purify-extend.d.ts b/packages/widget/src/types/purify-extend.d.ts index 4ccd3b05..ff8f41db 100644 --- a/packages/widget/src/types/purify-extend.d.ts +++ b/packages/widget/src/types/purify-extend.d.ts @@ -6,7 +6,7 @@ module "purify-ts" { interface MaybeTypeRef extends OriginalMaybeTypeRef { fromRecord>>( - val: T + val: T, ): Maybe<{ [Key in keyof T]: GetMaybeJust }>; } diff --git a/packages/widget/src/utils/create-state-context.ts b/packages/widget/src/utils/create-state-context.ts index 0b9baa9f..58d68deb 100644 --- a/packages/widget/src/utils/create-state-context.ts +++ b/packages/widget/src/utils/create-state-context.ts @@ -6,7 +6,7 @@ const createStateContext = (defaultInitialValue: T) => { >(undefined); const providerFactory = ( props: { value: [T, React.Dispatch>] }, - children: React.ReactNode + children: React.ReactNode, ) => createElement(context.Provider, props, children); const StateProvider = ({ @@ -17,7 +17,7 @@ const createStateContext = (defaultInitialValue: T) => { initialValue?: T; }) => { const state = useState( - initialValue !== undefined ? initialValue : defaultInitialValue + initialValue !== undefined ? initialValue : defaultInitialValue, ); return providerFactory({ value: state }, children); }; diff --git a/packages/widget/src/utils/date.ts b/packages/widget/src/utils/date.ts index 53a337c3..5430d809 100644 --- a/packages/widget/src/utils/date.ts +++ b/packages/widget/src/utils/date.ts @@ -35,7 +35,7 @@ export const formatDurationUntilDate = (futureDate: Date) => { return formatDuration( { days, hours, minutes, seconds }, - { format: getFormat({ days, hours, minutes }) } + { format: getFormat({ days, hours, minutes }) }, ); }; diff --git a/packages/widget/src/utils/extend-purify.ts b/packages/widget/src/utils/extend-purify.ts index 714ba898..6753589d 100644 --- a/packages/widget/src/utils/extend-purify.ts +++ b/packages/widget/src/utils/extend-purify.ts @@ -2,7 +2,7 @@ import { Just, Maybe, Nothing } from "purify-ts"; import type { GetMaybeJust } from "../types/utils"; Maybe.fromRecord = >>( - val: T + val: T, ): Maybe<{ [Key in keyof T]: GetMaybeJust }> => { const result = {} as { [Key in keyof T]: GetMaybeJust }; diff --git a/packages/widget/src/utils/formatters.ts b/packages/widget/src/utils/formatters.ts index 81b4c5dc..721f9c5e 100644 --- a/packages/widget/src/utils/formatters.ts +++ b/packages/widget/src/utils/formatters.ts @@ -1,9 +1,10 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; 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 type { RewardTypes } from "../domain/types/reward-rate"; +import type { TokenDto, YieldTokenDto } from "../domain/types/tokens"; +import { getYieldGasFeeToken, type Yield } from "../domain/types/yields"; import { APToPercentage, defaultFormattedNumber, formatNumber } from "."; export const formatCountryCode = ({ @@ -16,9 +17,10 @@ export const formatCountryCode = ({ return new Intl.DisplayNames([language], { type: "region" }).of(countryCode); }; -export const getRewardRateFormatted = ( - opts: Pick & { rewardRate: number | undefined } -) => { +export const getRewardRateFormatted = (opts: { + rewardType: RewardTypes; + rewardRate: number | undefined; +}) => { const { rewardRate, rewardType } = opts; if (rewardType === "variable" || !rewardRate) { @@ -28,7 +30,7 @@ export const getRewardRateFormatted = ( return `${APToPercentage(rewardRate)}%`; }; -export const getRewardTypeFormatted = (rewardType: YieldDto["rewardType"]) => { +export const getRewardTypeFormatted = (rewardType: RewardTypes) => { switch (rewardType) { case "apr": return "APR"; @@ -46,7 +48,7 @@ export const getGasFeeInUSD = ({ gas, prices, }: { - yieldDto: Maybe; + yieldDto: Maybe; gas: Maybe; prices: Maybe; }) => @@ -59,19 +61,19 @@ export const getGasFeeInUSD = ({ gasFeeInUSD: getTokenPriceInUSD({ amount: val.gas.toString(), prices: prices.orDefault(new Prices(new Map())), - token: val.yieldDto.metadata.gasFeeToken, + token: getYieldGasFeeToken(val.yieldDto), pricePerShare: null, baseToken: null, }), })) .mapOrDefault( (val) => - `${formatNumber(val.gas, 10)} ${val.yieldDto.metadata.gasFeeToken.symbol} ${ + `${formatNumber(val.gas, 10)} ${getYieldGasFeeToken(val.yieldDto).symbol} ${ val.gasFeeInUSD.isGreaterThan(0) ? ` ($${defaultFormattedNumber(val.gasFeeInUSD)})` : "" }`, - "" + "", ); export const getFeesInUSD = ({ @@ -101,7 +103,7 @@ export const getFeesInUSD = ({ ? ` ($${defaultFormattedNumber(val.feeInUSD)})` : "" }`, - "" + "", ); export const capitalizeFirstLetters = (text: string): string => @@ -110,8 +112,8 @@ export const capitalizeFirstLetters = (text: string): string => t .split(" ") .map( - (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() + (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), ) - .join(" ") + .join(" "), ) .orDefault(""); diff --git a/packages/widget/src/utils/index.ts b/packages/widget/src/utils/index.ts index e3a18578..3521d712 100644 --- a/packages/widget/src/utils/index.ts +++ b/packages/widget/src/utils/index.ts @@ -21,13 +21,13 @@ BigNumber.config({ export const formatNumber = ( number: string | BigNumber | number, - decimals?: number + decimals?: number, ) => Just(BigNumber(number)) .map((v) => typeof decimals === "number" ? v.decimalPlaces(decimals, BigNumber.ROUND_DOWN) - : v + : v, ) .map((v) => v.toFormat()) .unsafeCoerce(); @@ -85,20 +85,20 @@ export const waitForMs = (ms: number) => export const typeSafeObjectFromEntries = < const T extends ReadonlyArray, >( - entries: T + entries: T, ): { [K in T[number] as K[0]]: K[1] } => { return Object.fromEntries(entries) as { [K in T[number] as K[0]]: K[1] }; }; export const typeSafeObjectEntries = >( - obj: T + obj: T, ): { [K in keyof T]: [K, T[K]] }[keyof T][] => { return Object.entries(obj) as { [K in keyof T]: [K, T[K]] }[keyof T][]; }; export function formatAddress( address: string, - opts?: { leadingChars: number; trailingChars: number } + opts?: { leadingChars: number; trailingChars: number }, ): string { const leadingChars = opts?.leadingChars ?? 4; const trailingChars = opts?.trailingChars ?? 4; @@ -106,7 +106,7 @@ export function formatAddress( return address.length < leadingChars + trailingChars ? address : `${address.substring(0, leadingChars)}\u2026${address.substring( - address.length - trailingChars + address.length - trailingChars, )}`; } @@ -129,10 +129,10 @@ export const isMobile = () => { if ( /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test( - navigator.userAgent + navigator.userAgent, ) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( - navigator.userAgent.substring(0, 4) + navigator.userAgent.substring(0, 4), ) ) { return true; @@ -157,7 +157,7 @@ export const bpsToPercentage = (bps: BigNumber | number) => export const groupDateStrings = ( dateStrings: string[], - i18n: i18n + i18n: i18n, ): [string[], number[]] => { const today = new Date(); const yesterday = new Date(today); diff --git a/packages/widget/src/utils/mappers.ts b/packages/widget/src/utils/mappers.ts index dee70454..4ce1c3f5 100644 --- a/packages/widget/src/utils/mappers.ts +++ b/packages/widget/src/utils/mappers.ts @@ -1,5 +1,4 @@ -import type { PriceResponseDto } from "@stakekit/api-hooks"; -import type { Price } from "../domain/types/price"; +import type { Price, PriceResponseDto } from "../domain/types/price"; import { Prices } from "../domain/types/price"; import type { TokenString } from "../domain/types/tokens"; @@ -9,12 +8,12 @@ const priceDtoToPrice = (priceDto: PriceResponseDto[string]): Price => ({ }); export const priceResponseDtoToPrices = ( - priceResponseDto: PriceResponseDto + priceResponseDto: PriceResponseDto, ): Prices => new Prices( Object.keys(priceResponseDto).reduce((acc, key) => { acc.set(key as TokenString, priceDtoToPrice(priceResponseDto[key])); return acc; - }, new Map()) + }, new Map()), ); diff --git a/packages/widget/src/utils/maybe-window.ts b/packages/widget/src/utils/maybe-window.ts index c1aaea38..b8054a4c 100644 --- a/packages/widget/src/utils/maybe-window.ts +++ b/packages/widget/src/utils/maybe-window.ts @@ -1,5 +1,5 @@ import { Maybe } from "purify-ts"; export const MaybeWindow = Maybe.fromNullable( - typeof window === "undefined" ? null : window + typeof window === "undefined" ? null : window, ); diff --git a/packages/widget/src/utils/memoize.ts b/packages/widget/src/utils/memoize.ts index 7559a931..b5ed10b5 100644 --- a/packages/widget/src/utils/memoize.ts +++ b/packages/widget/src/utils/memoize.ts @@ -3,7 +3,7 @@ const cachedResSymbol = Symbol("cachedRes"); type CacheMap = Map>; export const memoize = ( - fn: (...args: Args) => Res + fn: (...args: Args) => Res, ) => { const cache: CacheMap> = new Map(); diff --git a/packages/widget/src/utils/region-iso-3166-codes.ts b/packages/widget/src/utils/region-iso-3166-codes.ts index 592e2742..b5f1b9b6 100644 --- a/packages/widget/src/utils/region-iso-3166-codes.ts +++ b/packages/widget/src/utils/region-iso-3166-codes.ts @@ -1 +1,7305 @@ -export const countries = {"AD-07":{"countryCode":"AD","subdivisionName":"Andorra la Vella","code":"AD-07"},"AD-02":{"countryCode":"AD","subdivisionName":"Canillo","code":"AD-02"},"AD-03":{"countryCode":"AD","subdivisionName":"Encamp","code":"AD-03"},"AD-08":{"countryCode":"AD","subdivisionName":"Escaldes-Engordany","code":"AD-08"},"AD-04":{"countryCode":"AD","subdivisionName":"La Massana","code":"AD-04"},"AD-05":{"countryCode":"AD","subdivisionName":"Ordino","code":"AD-05"},"AD-06":{"countryCode":"AD","subdivisionName":"Sant Julia de Loria","code":"AD-06"},"AE-AJ":{"countryCode":"AE","subdivisionName":"'Ajman","code":"AE-AJ"},"AE-AZ":{"countryCode":"AE","subdivisionName":"Abu Zaby","code":"AE-AZ"},"AE-FU":{"countryCode":"AE","subdivisionName":"Al Fujayrah","code":"AE-FU"},"AE-SH":{"countryCode":"AE","subdivisionName":"Ash Shariqah","code":"AE-SH"},"AE-DU":{"countryCode":"AE","subdivisionName":"Dubayy","code":"AE-DU"},"AE-RK":{"countryCode":"AE","subdivisionName":"Ra's al Khaymah","code":"AE-RK"},"AE-UQ":{"countryCode":"AE","subdivisionName":"Umm al Qaywayn","code":"AE-UQ"},"AF-BDG":{"countryCode":"AF","subdivisionName":"Badghis","code":"AF-BDG"},"AF-BGL":{"countryCode":"AF","subdivisionName":"Baghlan","code":"AF-BGL"},"AF-BAL":{"countryCode":"AF","subdivisionName":"Balkh","code":"AF-BAL"},"AF-BAM":{"countryCode":"AF","subdivisionName":"Bamyan","code":"AF-BAM"},"AF-DAY":{"countryCode":"AF","subdivisionName":"Daykundi","code":"AF-DAY"},"AF-FRA":{"countryCode":"AF","subdivisionName":"Farah","code":"AF-FRA"},"AF-FYB":{"countryCode":"AF","subdivisionName":"Faryab","code":"AF-FYB"},"AF-GHA":{"countryCode":"AF","subdivisionName":"Ghazni","code":"AF-GHA"},"AF-GHO":{"countryCode":"AF","subdivisionName":"Ghor","code":"AF-GHO"},"AF-HEL":{"countryCode":"AF","subdivisionName":"Helmand","code":"AF-HEL"},"AF-HER":{"countryCode":"AF","subdivisionName":"Herat","code":"AF-HER"},"AF-JOW":{"countryCode":"AF","subdivisionName":"Jowzjan","code":"AF-JOW"},"AF-KAB":{"countryCode":"AF","subdivisionName":"Kabul","code":"AF-KAB"},"AF-KAN":{"countryCode":"AF","subdivisionName":"Kandahar","code":"AF-KAN"},"AF-KHO":{"countryCode":"AF","subdivisionName":"Khost","code":"AF-KHO"},"AF-KDZ":{"countryCode":"AF","subdivisionName":"Kunduz","code":"AF-KDZ"},"AF-LAG":{"countryCode":"AF","subdivisionName":"Laghman","code":"AF-LAG"},"AF-LOG":{"countryCode":"AF","subdivisionName":"Logar","code":"AF-LOG"},"AF-NAN":{"countryCode":"AF","subdivisionName":"Nangarhar","code":"AF-NAN"},"AF-NIM":{"countryCode":"AF","subdivisionName":"Nimroz","code":"AF-NIM"},"AF-PKA":{"countryCode":"AF","subdivisionName":"Paktika","code":"AF-PKA"},"AF-PIA":{"countryCode":"AF","subdivisionName":"Paktiya","code":"AF-PIA"},"AF-PAR":{"countryCode":"AF","subdivisionName":"Parwan","code":"AF-PAR"},"AF-SAM":{"countryCode":"AF","subdivisionName":"Samangan","code":"AF-SAM"},"AF-SAR":{"countryCode":"AF","subdivisionName":"Sar-e Pul","code":"AF-SAR"},"AF-TAK":{"countryCode":"AF","subdivisionName":"Takhar","code":"AF-TAK"},"AF-URU":{"countryCode":"AF","subdivisionName":"Uruzgan","code":"AF-URU"},"AF-WAR":{"countryCode":"AF","subdivisionName":"Wardak","code":"AF-WAR"},"AG-10":{"countryCode":"AG","subdivisionName":"Barbuda","code":"AG-10"},"AG-11":{"countryCode":"AG","subdivisionName":"Redonda","code":"AG-11"},"AG-03":{"countryCode":"AG","subdivisionName":"Saint George","code":"AG-03"},"AG-04":{"countryCode":"AG","subdivisionName":"Saint John","code":"AG-04"},"AG-05":{"countryCode":"AG","subdivisionName":"Saint Mary","code":"AG-05"},"AG-07":{"countryCode":"AG","subdivisionName":"Saint Peter","code":"AG-07"},"AG-08":{"countryCode":"AG","subdivisionName":"Saint Philip","code":"AG-08"},"-":{"countryCode":"YT","subdivisionName":"Tsingoni","code":"-"},"AL-01":{"countryCode":"AL","subdivisionName":"Berat","code":"AL-01"},"AL-09":{"countryCode":"AL","subdivisionName":"Diber","code":"AL-09"},"AL-02":{"countryCode":"AL","subdivisionName":"Durres","code":"AL-02"},"AL-03":{"countryCode":"AL","subdivisionName":"Elbasan","code":"AL-03"},"AL-04":{"countryCode":"AL","subdivisionName":"Fier","code":"AL-04"},"AL-05":{"countryCode":"AL","subdivisionName":"Gjirokaster","code":"AL-05"},"AL-06":{"countryCode":"AL","subdivisionName":"Korce","code":"AL-06"},"AL-07":{"countryCode":"AL","subdivisionName":"Kukes","code":"AL-07"},"AL-08":{"countryCode":"AL","subdivisionName":"Lezhe","code":"AL-08"},"AL-10":{"countryCode":"AL","subdivisionName":"Shkoder","code":"AL-10"},"AL-11":{"countryCode":"AL","subdivisionName":"Tirane","code":"AL-11"},"AL-12":{"countryCode":"AL","subdivisionName":"Vlore","code":"AL-12"},"AM-AG":{"countryCode":"AM","subdivisionName":"Aragacotn","code":"AM-AG"},"AM-AR":{"countryCode":"AM","subdivisionName":"Ararat","code":"AM-AR"},"AM-AV":{"countryCode":"AM","subdivisionName":"Armavir","code":"AM-AV"},"AM-ER":{"countryCode":"AM","subdivisionName":"Erevan","code":"AM-ER"},"AM-GR":{"countryCode":"AM","subdivisionName":"Gegark'unik'","code":"AM-GR"},"AM-KT":{"countryCode":"AM","subdivisionName":"Kotayk'","code":"AM-KT"},"AM-LO":{"countryCode":"AM","subdivisionName":"Lori","code":"AM-LO"},"AM-SH":{"countryCode":"AM","subdivisionName":"Sirak","code":"AM-SH"},"AM-SU":{"countryCode":"AM","subdivisionName":"Syunik'","code":"AM-SU"},"AM-TV":{"countryCode":"AM","subdivisionName":"Tavus","code":"AM-TV"},"AM-VD":{"countryCode":"AM","subdivisionName":"Vayoc Jor","code":"AM-VD"},"AO-BGO":{"countryCode":"AO","subdivisionName":"Bengo","code":"AO-BGO"},"AO-BGU":{"countryCode":"AO","subdivisionName":"Benguela","code":"AO-BGU"},"AO-BIE":{"countryCode":"AO","subdivisionName":"Bie","code":"AO-BIE"},"AO-CAB":{"countryCode":"AO","subdivisionName":"Cabinda","code":"AO-CAB"},"AO-CCU":{"countryCode":"AO","subdivisionName":"Cuando Cubango","code":"AO-CCU"},"AO-CNO":{"countryCode":"AO","subdivisionName":"Cuanza-Norte","code":"AO-CNO"},"AO-CUS":{"countryCode":"AO","subdivisionName":"Cuanza-Sul","code":"AO-CUS"},"AO-CNN":{"countryCode":"AO","subdivisionName":"Cunene","code":"AO-CNN"},"AO-HUA":{"countryCode":"AO","subdivisionName":"Huambo","code":"AO-HUA"},"AO-HUI":{"countryCode":"AO","subdivisionName":"Huila","code":"AO-HUI"},"AO-LUA":{"countryCode":"AO","subdivisionName":"Luanda","code":"AO-LUA"},"AO-LNO":{"countryCode":"AO","subdivisionName":"Lunda-Norte","code":"AO-LNO"},"AO-LSU":{"countryCode":"AO","subdivisionName":"Lunda-Sul","code":"AO-LSU"},"AO-MAL":{"countryCode":"AO","subdivisionName":"Malange","code":"AO-MAL"},"AO-MOX":{"countryCode":"AO","subdivisionName":"Moxico","code":"AO-MOX"},"AO-NAM":{"countryCode":"AO","subdivisionName":"Namibe","code":"AO-NAM"},"AO-UIG":{"countryCode":"AO","subdivisionName":"Uige","code":"AO-UIG"},"AO-ZAI":{"countryCode":"AO","subdivisionName":"Zaire","code":"AO-ZAI"},"AR-B":{"countryCode":"AR","subdivisionName":"Buenos Aires","code":"AR-B"},"AR-K":{"countryCode":"AR","subdivisionName":"Catamarca","code":"AR-K"},"AR-H":{"countryCode":"AR","subdivisionName":"Chaco","code":"AR-H"},"AR-U":{"countryCode":"AR","subdivisionName":"Chubut","code":"AR-U"},"AR-C":{"countryCode":"AR","subdivisionName":"Ciudad Autonoma de Buenos Aires","code":"AR-C"},"AR-X":{"countryCode":"AR","subdivisionName":"Cordoba","code":"AR-X"},"AR-W":{"countryCode":"AR","subdivisionName":"Corrientes","code":"AR-W"},"AR-E":{"countryCode":"AR","subdivisionName":"Entre Rios","code":"AR-E"},"AR-P":{"countryCode":"AR","subdivisionName":"Formosa","code":"AR-P"},"AR-Y":{"countryCode":"AR","subdivisionName":"Jujuy","code":"AR-Y"},"AR-L":{"countryCode":"AR","subdivisionName":"La Pampa","code":"AR-L"},"AR-F":{"countryCode":"AR","subdivisionName":"La Rioja","code":"AR-F"},"AR-M":{"countryCode":"AR","subdivisionName":"Mendoza","code":"AR-M"},"AR-N":{"countryCode":"AR","subdivisionName":"Misiones","code":"AR-N"},"AR-Q":{"countryCode":"AR","subdivisionName":"Neuquen","code":"AR-Q"},"AR-R":{"countryCode":"AR","subdivisionName":"Rio Negro","code":"AR-R"},"AR-A":{"countryCode":"AR","subdivisionName":"Salta","code":"AR-A"},"AR-J":{"countryCode":"AR","subdivisionName":"San Juan","code":"AR-J"},"AR-D":{"countryCode":"AR","subdivisionName":"San Luis","code":"AR-D"},"AR-Z":{"countryCode":"AR","subdivisionName":"Santa Cruz","code":"AR-Z"},"AR-S":{"countryCode":"AR","subdivisionName":"Santa Fe","code":"AR-S"},"AR-G":{"countryCode":"AR","subdivisionName":"Santiago del Estero","code":"AR-G"},"AR-V":{"countryCode":"AR","subdivisionName":"Tierra del Fuego","code":"AR-V"},"AR-T":{"countryCode":"AR","subdivisionName":"Tucuman","code":"AR-T"},"AT-1":{"countryCode":"AT","subdivisionName":"Burgenland","code":"AT-1"},"AT-2":{"countryCode":"AT","subdivisionName":"Karnten","code":"AT-2"},"AT-3":{"countryCode":"AT","subdivisionName":"Niederosterreich","code":"AT-3"},"AT-4":{"countryCode":"AT","subdivisionName":"Oberosterreich","code":"AT-4"},"AT-5":{"countryCode":"AT","subdivisionName":"Salzburg","code":"AT-5"},"AT-6":{"countryCode":"AT","subdivisionName":"Steiermark","code":"AT-6"},"AT-7":{"countryCode":"AT","subdivisionName":"Tirol","code":"AT-7"},"AT-8":{"countryCode":"AT","subdivisionName":"Vorarlberg","code":"AT-8"},"AT-9":{"countryCode":"AT","subdivisionName":"Wien","code":"AT-9"},"AU-ACT":{"countryCode":"AU","subdivisionName":"Australian Capital Territory","code":"AU-ACT"},"AU-NSW":{"countryCode":"AU","subdivisionName":"New South Wales","code":"AU-NSW"},"AU-NT":{"countryCode":"AU","subdivisionName":"Northern Territory","code":"AU-NT"},"AU-QLD":{"countryCode":"AU","subdivisionName":"Queensland","code":"AU-QLD"},"AU-SA":{"countryCode":"AU","subdivisionName":"South Australia","code":"AU-SA"},"AU-TAS":{"countryCode":"AU","subdivisionName":"Tasmania","code":"AU-TAS"},"AU-VIC":{"countryCode":"AU","subdivisionName":"Victoria","code":"AU-VIC"},"AU-WA":{"countryCode":"AU","subdivisionName":"Western Australia","code":"AU-WA"},"AZ-ABS":{"countryCode":"AZ","subdivisionName":"Abseron","code":"AZ-ABS"},"AZ-AGC":{"countryCode":"AZ","subdivisionName":"Agcabadi","code":"AZ-AGC"},"AZ-AGS":{"countryCode":"AZ","subdivisionName":"Agdas","code":"AZ-AGS"},"AZ-AGA":{"countryCode":"AZ","subdivisionName":"Agstafa","code":"AZ-AGA"},"AZ-AGU":{"countryCode":"AZ","subdivisionName":"Agsu","code":"AZ-AGU"},"AZ-AST":{"countryCode":"AZ","subdivisionName":"Astara","code":"AZ-AST"},"AZ-BA":{"countryCode":"AZ","subdivisionName":"Baki","code":"AZ-BA"},"AZ-BAL":{"countryCode":"AZ","subdivisionName":"Balakan","code":"AZ-BAL"},"AZ-BAR":{"countryCode":"AZ","subdivisionName":"Barda","code":"AZ-BAR"},"AZ-BEY":{"countryCode":"AZ","subdivisionName":"Beylaqan","code":"AZ-BEY"},"AZ-BIL":{"countryCode":"AZ","subdivisionName":"Bilasuvar","code":"AZ-BIL"},"AZ-CAL":{"countryCode":"AZ","subdivisionName":"Calilabad","code":"AZ-CAL"},"AZ-DAS":{"countryCode":"AZ","subdivisionName":"Daskasan","code":"AZ-DAS"},"AZ-FUZ":{"countryCode":"AZ","subdivisionName":"Fuzuli","code":"AZ-FUZ"},"AZ-GAD":{"countryCode":"AZ","subdivisionName":"Gadabay","code":"AZ-GAD"},"AZ-GA":{"countryCode":"AZ","subdivisionName":"Ganca","code":"AZ-GA"},"AZ-GOY":{"countryCode":"AZ","subdivisionName":"Goycay","code":"AZ-GOY"},"AZ-GYG":{"countryCode":"AZ","subdivisionName":"Goygol","code":"AZ-GYG"},"AZ-IMI":{"countryCode":"AZ","subdivisionName":"Imisli","code":"AZ-IMI"},"AZ-ISM":{"countryCode":"AZ","subdivisionName":"Ismayilli","code":"AZ-ISM"},"AZ-KUR":{"countryCode":"AZ","subdivisionName":"Kurdamir","code":"AZ-KUR"},"AZ-LA":{"countryCode":"AZ","subdivisionName":"Lankaran","code":"AZ-LA"},"AZ-MAS":{"countryCode":"AZ","subdivisionName":"Masalli","code":"AZ-MAS"},"AZ-MI":{"countryCode":"AZ","subdivisionName":"Mingacevir","code":"AZ-MI"},"AZ-NA":{"countryCode":"AZ","subdivisionName":"Naftalan","code":"AZ-NA"},"AZ-NX":{"countryCode":"AZ","subdivisionName":"Naxcivan","code":"AZ-NX"},"AZ-NEF":{"countryCode":"AZ","subdivisionName":"Neftcala","code":"AZ-NEF"},"AZ-OGU":{"countryCode":"AZ","subdivisionName":"Oguz","code":"AZ-OGU"},"AZ-QAB":{"countryCode":"AZ","subdivisionName":"Qabala","code":"AZ-QAB"},"AZ-QAX":{"countryCode":"AZ","subdivisionName":"Qax","code":"AZ-QAX"},"AZ-QAZ":{"countryCode":"AZ","subdivisionName":"Qazax","code":"AZ-QAZ"},"AZ-QBA":{"countryCode":"AZ","subdivisionName":"Quba","code":"AZ-QBA"},"AZ-QUS":{"countryCode":"AZ","subdivisionName":"Qusar","code":"AZ-QUS"},"AZ-SAT":{"countryCode":"AZ","subdivisionName":"Saatli","code":"AZ-SAT"},"AZ-SAB":{"countryCode":"AZ","subdivisionName":"Sabirabad","code":"AZ-SAB"},"AZ-SAK":{"countryCode":"AZ","subdivisionName":"Saki","code":"AZ-SAK"},"AZ-SAL":{"countryCode":"AZ","subdivisionName":"Salyan","code":"AZ-SAL"},"AZ-SMI":{"countryCode":"AZ","subdivisionName":"Samaxi","code":"AZ-SMI"},"AZ-SKR":{"countryCode":"AZ","subdivisionName":"Samkir","code":"AZ-SKR"},"AZ-SMX":{"countryCode":"AZ","subdivisionName":"Samux","code":"AZ-SMX"},"AZ-SR":{"countryCode":"AZ","subdivisionName":"Sirvan","code":"AZ-SR"},"AZ-SIY":{"countryCode":"AZ","subdivisionName":"Siyazan","code":"AZ-SIY"},"AZ-SM":{"countryCode":"AZ","subdivisionName":"Sumqayit","code":"AZ-SM"},"AZ-TAR":{"countryCode":"AZ","subdivisionName":"Tartar","code":"AZ-TAR"},"AZ-TOV":{"countryCode":"AZ","subdivisionName":"Tovuz","code":"AZ-TOV"},"AZ-UCA":{"countryCode":"AZ","subdivisionName":"Ucar","code":"AZ-UCA"},"AZ-XAC":{"countryCode":"AZ","subdivisionName":"Xacmaz","code":"AZ-XAC"},"AZ-XIZ":{"countryCode":"AZ","subdivisionName":"Xizi","code":"AZ-XIZ"},"AZ-YAR":{"countryCode":"AZ","subdivisionName":"Yardimli","code":"AZ-YAR"},"AZ-YEV":{"countryCode":"AZ","subdivisionName":"Yevlax","code":"AZ-YEV"},"AZ-ZAQ":{"countryCode":"AZ","subdivisionName":"Zaqatala","code":"AZ-ZAQ"},"AZ-ZAR":{"countryCode":"AZ","subdivisionName":"Zardab","code":"AZ-ZAR"},"BA-BRC":{"countryCode":"BA","subdivisionName":"Brcko distrikt","code":"BA-BRC"},"BA-BIH":{"countryCode":"BA","subdivisionName":"Federacija Bosne i Hercegovine","code":"BA-BIH"},"BA-SRP":{"countryCode":"BA","subdivisionName":"Republika Srpska","code":"BA-SRP"},"BB-01":{"countryCode":"BB","subdivisionName":"Christ Church","code":"BB-01"},"BB-02":{"countryCode":"BB","subdivisionName":"Saint Andrew","code":"BB-02"},"BB-03":{"countryCode":"BB","subdivisionName":"Saint George","code":"BB-03"},"BB-04":{"countryCode":"BB","subdivisionName":"Saint James","code":"BB-04"},"BB-05":{"countryCode":"BB","subdivisionName":"Saint John","code":"BB-05"},"BB-07":{"countryCode":"BB","subdivisionName":"Saint Lucy","code":"BB-07"},"BB-08":{"countryCode":"BB","subdivisionName":"Saint Michael","code":"BB-08"},"BB-09":{"countryCode":"BB","subdivisionName":"Saint Peter","code":"BB-09"},"BB-10":{"countryCode":"BB","subdivisionName":"Saint Philip","code":"BB-10"},"BB-11":{"countryCode":"BB","subdivisionName":"Saint Thomas","code":"BB-11"},"BD-A":{"countryCode":"BD","subdivisionName":"Barishal","code":"BD-A"},"BD-B":{"countryCode":"BD","subdivisionName":"Chattogram","code":"BD-B"},"BD-C":{"countryCode":"BD","subdivisionName":"Dhaka","code":"BD-C"},"BD-D":{"countryCode":"BD","subdivisionName":"Khulna","code":"BD-D"},"BD-E":{"countryCode":"BD","subdivisionName":"Rajshahi","code":"BD-E"},"BD-F":{"countryCode":"BD","subdivisionName":"Rangpur","code":"BD-F"},"BD-G":{"countryCode":"BD","subdivisionName":"Sylhet","code":"BD-G"},"BE-VAN":{"countryCode":"BE","subdivisionName":"Antwerpen","code":"BE-VAN"},"BE-WBR":{"countryCode":"BE","subdivisionName":"Brabant wallon","code":"BE-WBR"},"BE-BRU":{"countryCode":"BE","subdivisionName":"Brussels Hoofdstedelijk Gewest","code":"BE-BRU"},"BE-WHT":{"countryCode":"BE","subdivisionName":"Hainaut","code":"BE-WHT"},"BE-WLG":{"countryCode":"BE","subdivisionName":"Liege","code":"BE-WLG"},"BE-VLI":{"countryCode":"BE","subdivisionName":"Limburg","code":"BE-VLI"},"BE-WLX":{"countryCode":"BE","subdivisionName":"Luxembourg","code":"BE-WLX"},"BE-WNA":{"countryCode":"BE","subdivisionName":"Namur","code":"BE-WNA"},"BE-VOV":{"countryCode":"BE","subdivisionName":"Oost-Vlaanderen","code":"BE-VOV"},"BE-VBR":{"countryCode":"BE","subdivisionName":"Vlaams-Brabant","code":"BE-VBR"},"BE-VWV":{"countryCode":"BE","subdivisionName":"West-Vlaanderen","code":"BE-VWV"},"BF-BAL":{"countryCode":"BF","subdivisionName":"Bale","code":"BF-BAL"},"BF-BAM":{"countryCode":"BF","subdivisionName":"Bam","code":"BF-BAM"},"BF-BAN":{"countryCode":"BF","subdivisionName":"Banwa","code":"BF-BAN"},"BF-BAZ":{"countryCode":"BF","subdivisionName":"Bazega","code":"BF-BAZ"},"BF-BGR":{"countryCode":"BF","subdivisionName":"Bougouriba","code":"BF-BGR"},"BF-BLG":{"countryCode":"BF","subdivisionName":"Boulgou","code":"BF-BLG"},"BF-BLK":{"countryCode":"BF","subdivisionName":"Boulkiemde","code":"BF-BLK"},"BF-COM":{"countryCode":"BF","subdivisionName":"Comoe","code":"BF-COM"},"BF-GAN":{"countryCode":"BF","subdivisionName":"Ganzourgou","code":"BF-GAN"},"BF-GNA":{"countryCode":"BF","subdivisionName":"Gnagna","code":"BF-GNA"},"BF-GOU":{"countryCode":"BF","subdivisionName":"Gourma","code":"BF-GOU"},"BF-HOU":{"countryCode":"BF","subdivisionName":"Houet","code":"BF-HOU"},"BF-IOB":{"countryCode":"BF","subdivisionName":"Ioba","code":"BF-IOB"},"BF-KAD":{"countryCode":"BF","subdivisionName":"Kadiogo","code":"BF-KAD"},"BF-KEN":{"countryCode":"BF","subdivisionName":"Kenedougou","code":"BF-KEN"},"BF-KMP":{"countryCode":"BF","subdivisionName":"Kompienga","code":"BF-KMP"},"BF-KOS":{"countryCode":"BF","subdivisionName":"Kossi","code":"BF-KOS"},"BF-KOT":{"countryCode":"BF","subdivisionName":"Kouritenga","code":"BF-KOT"},"BF-KOW":{"countryCode":"BF","subdivisionName":"Kourweogo","code":"BF-KOW"},"BF-LER":{"countryCode":"BF","subdivisionName":"Leraba","code":"BF-LER"},"BF-LOR":{"countryCode":"BF","subdivisionName":"Loroum","code":"BF-LOR"},"BF-MOU":{"countryCode":"BF","subdivisionName":"Mouhoun","code":"BF-MOU"},"BF-NAO":{"countryCode":"BF","subdivisionName":"Nahouri","code":"BF-NAO"},"BF-NAM":{"countryCode":"BF","subdivisionName":"Namentenga","code":"BF-NAM"},"BF-NAY":{"countryCode":"BF","subdivisionName":"Nayala","code":"BF-NAY"},"BF-OUB":{"countryCode":"BF","subdivisionName":"Oubritenga","code":"BF-OUB"},"BF-OUD":{"countryCode":"BF","subdivisionName":"Oudalan","code":"BF-OUD"},"BF-PAS":{"countryCode":"BF","subdivisionName":"Passore","code":"BF-PAS"},"BF-SMT":{"countryCode":"BF","subdivisionName":"Sanmatenga","code":"BF-SMT"},"BF-SEN":{"countryCode":"BF","subdivisionName":"Seno","code":"BF-SEN"},"BF-SIS":{"countryCode":"BF","subdivisionName":"Sissili","code":"BF-SIS"},"BF-SOM":{"countryCode":"BF","subdivisionName":"Soum","code":"BF-SOM"},"BF-SOR":{"countryCode":"BF","subdivisionName":"Sourou","code":"BF-SOR"},"BF-TAP":{"countryCode":"BF","subdivisionName":"Tapoa","code":"BF-TAP"},"BF-TUI":{"countryCode":"BF","subdivisionName":"Tuy","code":"BF-TUI"},"BF-YAT":{"countryCode":"BF","subdivisionName":"Yatenga","code":"BF-YAT"},"BF-ZIR":{"countryCode":"BF","subdivisionName":"Ziro","code":"BF-ZIR"},"BF-ZON":{"countryCode":"BF","subdivisionName":"Zondoma","code":"BF-ZON"},"BF-ZOU":{"countryCode":"BF","subdivisionName":"Zoundweogo","code":"BF-ZOU"},"BG-01":{"countryCode":"BG","subdivisionName":"Blagoevgrad","code":"BG-01"},"BG-02":{"countryCode":"BG","subdivisionName":"Burgas","code":"BG-02"},"BG-08":{"countryCode":"BG","subdivisionName":"Dobrich","code":"BG-08"},"BG-07":{"countryCode":"BG","subdivisionName":"Gabrovo","code":"BG-07"},"BG-26":{"countryCode":"BG","subdivisionName":"Haskovo","code":"BG-26"},"BG-09":{"countryCode":"BG","subdivisionName":"Kardzhali","code":"BG-09"},"BG-10":{"countryCode":"BG","subdivisionName":"Kyustendil","code":"BG-10"},"BG-11":{"countryCode":"BG","subdivisionName":"Lovech","code":"BG-11"},"BG-12":{"countryCode":"BG","subdivisionName":"Montana","code":"BG-12"},"BG-13":{"countryCode":"BG","subdivisionName":"Pazardzhik","code":"BG-13"},"BG-14":{"countryCode":"BG","subdivisionName":"Pernik","code":"BG-14"},"BG-15":{"countryCode":"BG","subdivisionName":"Pleven","code":"BG-15"},"BG-16":{"countryCode":"BG","subdivisionName":"Plovdiv","code":"BG-16"},"BG-17":{"countryCode":"BG","subdivisionName":"Razgrad","code":"BG-17"},"BG-18":{"countryCode":"BG","subdivisionName":"Ruse","code":"BG-18"},"BG-27":{"countryCode":"BG","subdivisionName":"Shumen","code":"BG-27"},"BG-19":{"countryCode":"BG","subdivisionName":"Silistra","code":"BG-19"},"BG-20":{"countryCode":"BG","subdivisionName":"Sliven","code":"BG-20"},"BG-21":{"countryCode":"BG","subdivisionName":"Smolyan","code":"BG-21"},"BG-23":{"countryCode":"BG","subdivisionName":"Sofia","code":"BG-23"},"BG-22":{"countryCode":"BG","subdivisionName":"Sofia (stolitsa)","code":"BG-22"},"BG-24":{"countryCode":"BG","subdivisionName":"Stara Zagora","code":"BG-24"},"BG-25":{"countryCode":"BG","subdivisionName":"Targovishte","code":"BG-25"},"BG-03":{"countryCode":"BG","subdivisionName":"Varna","code":"BG-03"},"BG-04":{"countryCode":"BG","subdivisionName":"Veliko Tarnovo","code":"BG-04"},"BG-05":{"countryCode":"BG","subdivisionName":"Vidin","code":"BG-05"},"BG-06":{"countryCode":"BG","subdivisionName":"Vratsa","code":"BG-06"},"BG-28":{"countryCode":"BG","subdivisionName":"Yambol","code":"BG-28"},"BH-13":{"countryCode":"BH","subdivisionName":"Al 'Asimah","code":"BH-13"},"BH-14":{"countryCode":"BH","subdivisionName":"Al Janubiyah","code":"BH-14"},"BH-15":{"countryCode":"BH","subdivisionName":"Al Muharraq","code":"BH-15"},"BH-17":{"countryCode":"BH","subdivisionName":"Ash Shamaliyah","code":"BH-17"},"BI-BM":{"countryCode":"BI","subdivisionName":"Bujumbura Mairie","code":"BI-BM"},"BI-BR":{"countryCode":"BI","subdivisionName":"Bururi","code":"BI-BR"},"BI-CI":{"countryCode":"BI","subdivisionName":"Cibitoke","code":"BI-CI"},"BI-GI":{"countryCode":"BI","subdivisionName":"Gitega","code":"BI-GI"},"BI-KI":{"countryCode":"BI","subdivisionName":"Kirundo","code":"BI-KI"},"BI-MW":{"countryCode":"BI","subdivisionName":"Mwaro","code":"BI-MW"},"BI-NG":{"countryCode":"BI","subdivisionName":"Ngozi","code":"BI-NG"},"BI-RM":{"countryCode":"BI","subdivisionName":"Rumonge","code":"BI-RM"},"BI-RT":{"countryCode":"BI","subdivisionName":"Rutana","code":"BI-RT"},"BI-RY":{"countryCode":"BI","subdivisionName":"Ruyigi","code":"BI-RY"},"BJ-AK":{"countryCode":"BJ","subdivisionName":"Atacora","code":"BJ-AK"},"BJ-AQ":{"countryCode":"BJ","subdivisionName":"Atlantique","code":"BJ-AQ"},"BJ-BO":{"countryCode":"BJ","subdivisionName":"Borgou","code":"BJ-BO"},"BJ-CO":{"countryCode":"BJ","subdivisionName":"Collines","code":"BJ-CO"},"BJ-DO":{"countryCode":"BJ","subdivisionName":"Donga","code":"BJ-DO"},"BJ-LI":{"countryCode":"BJ","subdivisionName":"Littoral","code":"BJ-LI"},"BJ-MO":{"countryCode":"BJ","subdivisionName":"Mono","code":"BJ-MO"},"BJ-OU":{"countryCode":"BJ","subdivisionName":"Oueme","code":"BJ-OU"},"BJ-PL":{"countryCode":"BJ","subdivisionName":"Plateau","code":"BJ-PL"},"BJ-ZO":{"countryCode":"BJ","subdivisionName":"Zou","code":"BJ-ZO"},"BN-BE":{"countryCode":"BN","subdivisionName":"Belait","code":"BN-BE"},"BN-BM":{"countryCode":"BN","subdivisionName":"Brunei-Muara","code":"BN-BM"},"BN-TE":{"countryCode":"BN","subdivisionName":"Temburong","code":"BN-TE"},"BN-TU":{"countryCode":"BN","subdivisionName":"Tutong","code":"BN-TU"},"BO-H":{"countryCode":"BO","subdivisionName":"Chuquisaca","code":"BO-H"},"BO-C":{"countryCode":"BO","subdivisionName":"Cochabamba","code":"BO-C"},"BO-B":{"countryCode":"BO","subdivisionName":"El Beni","code":"BO-B"},"BO-L":{"countryCode":"BO","subdivisionName":"La Paz","code":"BO-L"},"BO-O":{"countryCode":"BO","subdivisionName":"Oruro","code":"BO-O"},"BO-N":{"countryCode":"BO","subdivisionName":"Pando","code":"BO-N"},"BO-P":{"countryCode":"BO","subdivisionName":"Potosi","code":"BO-P"},"BO-S":{"countryCode":"BO","subdivisionName":"Santa Cruz","code":"BO-S"},"BO-T":{"countryCode":"BO","subdivisionName":"Tarija","code":"BO-T"},"BQ-BO":{"countryCode":"BQ","subdivisionName":"Bonaire","code":"BQ-BO"},"BQ-SA":{"countryCode":"BQ","subdivisionName":"Saba","code":"BQ-SA"},"BQ-SE":{"countryCode":"BQ","subdivisionName":"Sint Eustatius","code":"BQ-SE"},"BR-AC":{"countryCode":"BR","subdivisionName":"Acre","code":"BR-AC"},"BR-AL":{"countryCode":"BR","subdivisionName":"Alagoas","code":"BR-AL"},"BR-AP":{"countryCode":"BR","subdivisionName":"Amapa","code":"BR-AP"},"BR-AM":{"countryCode":"BR","subdivisionName":"Amazonas","code":"BR-AM"},"BR-BA":{"countryCode":"BR","subdivisionName":"Bahia","code":"BR-BA"},"BR-CE":{"countryCode":"BR","subdivisionName":"Ceara","code":"BR-CE"},"BR-DF":{"countryCode":"BR","subdivisionName":"Distrito Federal","code":"BR-DF"},"BR-ES":{"countryCode":"BR","subdivisionName":"Espirito Santo","code":"BR-ES"},"BR-GO":{"countryCode":"BR","subdivisionName":"Goias","code":"BR-GO"},"BR-MA":{"countryCode":"BR","subdivisionName":"Maranhao","code":"BR-MA"},"BR-MT":{"countryCode":"BR","subdivisionName":"Mato Grosso","code":"BR-MT"},"BR-MS":{"countryCode":"BR","subdivisionName":"Mato Grosso do Sul","code":"BR-MS"},"BR-MG":{"countryCode":"BR","subdivisionName":"Minas Gerais","code":"BR-MG"},"BR-PA":{"countryCode":"BR","subdivisionName":"Para","code":"BR-PA"},"BR-PB":{"countryCode":"BR","subdivisionName":"Paraiba","code":"BR-PB"},"BR-PR":{"countryCode":"BR","subdivisionName":"Parana","code":"BR-PR"},"BR-PE":{"countryCode":"BR","subdivisionName":"Pernambuco","code":"BR-PE"},"BR-PI":{"countryCode":"BR","subdivisionName":"Piaui","code":"BR-PI"},"BR-RN":{"countryCode":"BR","subdivisionName":"Rio Grande do Norte","code":"BR-RN"},"BR-RS":{"countryCode":"BR","subdivisionName":"Rio Grande do Sul","code":"BR-RS"},"BR-RJ":{"countryCode":"BR","subdivisionName":"Rio de Janeiro","code":"BR-RJ"},"BR-RO":{"countryCode":"BR","subdivisionName":"Rondonia","code":"BR-RO"},"BR-RR":{"countryCode":"BR","subdivisionName":"Roraima","code":"BR-RR"},"BR-SC":{"countryCode":"BR","subdivisionName":"Santa Catarina","code":"BR-SC"},"BR-SP":{"countryCode":"BR","subdivisionName":"Sao Paulo","code":"BR-SP"},"BR-SE":{"countryCode":"BR","subdivisionName":"Sergipe","code":"BR-SE"},"BR-TO":{"countryCode":"BR","subdivisionName":"Tocantins","code":"BR-TO"},"BS-BP":{"countryCode":"BS","subdivisionName":"Black Point","code":"BS-BP"},"BS-CO":{"countryCode":"BS","subdivisionName":"Central Abaco","code":"BS-CO"},"BS-FP":{"countryCode":"BS","subdivisionName":"City of Freeport","code":"BS-FP"},"BS-EG":{"countryCode":"BS","subdivisionName":"East Grand Bahama","code":"BS-EG"},"BS-HI":{"countryCode":"BS","subdivisionName":"Harbour Island","code":"BS-HI"},"BS-LI":{"countryCode":"BS","subdivisionName":"Long Island","code":"BS-LI"},"BS-NP":{"countryCode":"BS","subdivisionName":"New Providence","code":"BS-NP"},"BS-NS":{"countryCode":"BS","subdivisionName":"North Andros","code":"BS-NS"},"BS-NE":{"countryCode":"BS","subdivisionName":"North Eleuthera","code":"BS-NE"},"BS-SS":{"countryCode":"BS","subdivisionName":"San Salvador","code":"BS-SS"},"BS-SE":{"countryCode":"BS","subdivisionName":"South Eleuthera","code":"BS-SE"},"BS-WG":{"countryCode":"BS","subdivisionName":"West Grand Bahama","code":"BS-WG"},"BT-33":{"countryCode":"BT","subdivisionName":"Bumthang","code":"BT-33"},"BT-12":{"countryCode":"BT","subdivisionName":"Chhukha","code":"BT-12"},"BT-22":{"countryCode":"BT","subdivisionName":"Dagana","code":"BT-22"},"BT-GA":{"countryCode":"BT","subdivisionName":"Gasa","code":"BT-GA"},"BT-44":{"countryCode":"BT","subdivisionName":"Lhuentse","code":"BT-44"},"BT-42":{"countryCode":"BT","subdivisionName":"Monggar","code":"BT-42"},"BT-11":{"countryCode":"BT","subdivisionName":"Paro","code":"BT-11"},"BT-43":{"countryCode":"BT","subdivisionName":"Pema Gatshel","code":"BT-43"},"BT-23":{"countryCode":"BT","subdivisionName":"Punakha","code":"BT-23"},"BT-45":{"countryCode":"BT","subdivisionName":"Samdrup Jongkhar","code":"BT-45"},"BT-14":{"countryCode":"BT","subdivisionName":"Samtse","code":"BT-14"},"BT-31":{"countryCode":"BT","subdivisionName":"Sarpang","code":"BT-31"},"BT-15":{"countryCode":"BT","subdivisionName":"Thimphu","code":"BT-15"},"BT-41":{"countryCode":"BT","subdivisionName":"Trashigang","code":"BT-41"},"BT-32":{"countryCode":"BT","subdivisionName":"Trongsa","code":"BT-32"},"BT-21":{"countryCode":"BT","subdivisionName":"Tsirang","code":"BT-21"},"BT-24":{"countryCode":"BT","subdivisionName":"Wangdue Phodrang","code":"BT-24"},"BT-34":{"countryCode":"BT","subdivisionName":"Zhemgang","code":"BT-34"},"BW-CE":{"countryCode":"BW","subdivisionName":"Central","code":"BW-CE"},"BW-CH":{"countryCode":"BW","subdivisionName":"Chobe","code":"BW-CH"},"BW-GH":{"countryCode":"BW","subdivisionName":"Ghanzi","code":"BW-GH"},"BW-KG":{"countryCode":"BW","subdivisionName":"Kgalagadi","code":"BW-KG"},"BW-KL":{"countryCode":"BW","subdivisionName":"Kgatleng","code":"BW-KL"},"BW-KW":{"countryCode":"BW","subdivisionName":"Kweneng","code":"BW-KW"},"BW-NE":{"countryCode":"BW","subdivisionName":"North East","code":"BW-NE"},"BW-NW":{"countryCode":"BW","subdivisionName":"North West","code":"BW-NW"},"BW-SE":{"countryCode":"BW","subdivisionName":"South East","code":"BW-SE"},"BW-SO":{"countryCode":"BW","subdivisionName":"Southern","code":"BW-SO"},"BY-BR":{"countryCode":"BY","subdivisionName":"Brestskaya voblasts'","code":"BY-BR"},"BY-HO":{"countryCode":"BY","subdivisionName":"Homyel'skaya voblasts'","code":"BY-HO"},"BY-HM":{"countryCode":"BY","subdivisionName":"Horad Minsk","code":"BY-HM"},"BY-HR":{"countryCode":"BY","subdivisionName":"Hrodzyenskaya voblasts'","code":"BY-HR"},"BY-MA":{"countryCode":"BY","subdivisionName":"Mahilyowskaya voblasts'","code":"BY-MA"},"BY-MI":{"countryCode":"BY","subdivisionName":"Minskaya voblasts'","code":"BY-MI"},"BY-VI":{"countryCode":"BY","subdivisionName":"Vitsyebskaya voblasts'","code":"BY-VI"},"BZ-BZ":{"countryCode":"BZ","subdivisionName":"Belize","code":"BZ-BZ"},"BZ-CY":{"countryCode":"BZ","subdivisionName":"Cayo","code":"BZ-CY"},"BZ-CZL":{"countryCode":"BZ","subdivisionName":"Corozal","code":"BZ-CZL"},"BZ-OW":{"countryCode":"BZ","subdivisionName":"Orange Walk","code":"BZ-OW"},"BZ-SC":{"countryCode":"BZ","subdivisionName":"Stann Creek","code":"BZ-SC"},"BZ-TOL":{"countryCode":"BZ","subdivisionName":"Toledo","code":"BZ-TOL"},"CA-AB":{"countryCode":"CA","subdivisionName":"Alberta","code":"CA-AB"},"CA-BC":{"countryCode":"CA","subdivisionName":"British Columbia","code":"CA-BC"},"CA-MB":{"countryCode":"CA","subdivisionName":"Manitoba","code":"CA-MB"},"CA-NB":{"countryCode":"CA","subdivisionName":"New Brunswick","code":"CA-NB"},"CA-NL":{"countryCode":"CA","subdivisionName":"Newfoundland and Labrador","code":"CA-NL"},"CA-NT":{"countryCode":"CA","subdivisionName":"Northwest Territories","code":"CA-NT"},"CA-NS":{"countryCode":"CA","subdivisionName":"Nova Scotia","code":"CA-NS"},"CA-NU":{"countryCode":"CA","subdivisionName":"Nunavut","code":"CA-NU"},"CA-ON":{"countryCode":"CA","subdivisionName":"Ontario","code":"CA-ON"},"CA-PE":{"countryCode":"CA","subdivisionName":"Prince Edward Island","code":"CA-PE"},"CA-QC":{"countryCode":"CA","subdivisionName":"Quebec","code":"CA-QC"},"CA-SK":{"countryCode":"CA","subdivisionName":"Saskatchewan","code":"CA-SK"},"CA-YT":{"countryCode":"CA","subdivisionName":"Yukon","code":"CA-YT"},"CD-EQ":{"countryCode":"CD","subdivisionName":"Equateur","code":"CD-EQ"},"CD-HK":{"countryCode":"CD","subdivisionName":"Haut-Katanga","code":"CD-HK"},"CD-HL":{"countryCode":"CD","subdivisionName":"Haut-Lomami","code":"CD-HL"},"CD-IT":{"countryCode":"CD","subdivisionName":"Ituri","code":"CD-IT"},"CD-KS":{"countryCode":"CD","subdivisionName":"Kasai","code":"CD-KS"},"CD-KC":{"countryCode":"CD","subdivisionName":"Kasai Central","code":"CD-KC"},"CD-KE":{"countryCode":"CD","subdivisionName":"Kasai Oriental","code":"CD-KE"},"CD-KN":{"countryCode":"CD","subdivisionName":"Kinshasa","code":"CD-KN"},"CD-LU":{"countryCode":"CD","subdivisionName":"Lualaba","code":"CD-LU"},"CD-MA":{"countryCode":"CD","subdivisionName":"Maniema","code":"CD-MA"},"CD-NK":{"countryCode":"CD","subdivisionName":"Nord-Kivu","code":"CD-NK"},"CD-SA":{"countryCode":"CD","subdivisionName":"Sankuru","code":"CD-SA"},"CD-SK":{"countryCode":"CD","subdivisionName":"Sud-Kivu","code":"CD-SK"},"CD-TA":{"countryCode":"CD","subdivisionName":"Tanganyika","code":"CD-TA"},"CD-TO":{"countryCode":"CD","subdivisionName":"Tshopo","code":"CD-TO"},"CF-BB":{"countryCode":"CF","subdivisionName":"Bamingui-Bangoran","code":"CF-BB"},"CF-BGF":{"countryCode":"CF","subdivisionName":"Bangui","code":"CF-BGF"},"CF-KB":{"countryCode":"CF","subdivisionName":"Gribingui","code":"CF-KB"},"CF-KG":{"countryCode":"CF","subdivisionName":"Kemo-Gribingui","code":"CF-KG"},"CF-HS":{"countryCode":"CF","subdivisionName":"Mambere-Kadei","code":"CF-HS"},"CF-NM":{"countryCode":"CF","subdivisionName":"Nana-Mambere","code":"CF-NM"},"CF-UK":{"countryCode":"CF","subdivisionName":"Ouaka","code":"CF-UK"},"CF-AC":{"countryCode":"CF","subdivisionName":"Ouham","code":"CF-AC"},"CF-OP":{"countryCode":"CF","subdivisionName":"Ouham-Pende","code":"CF-OP"},"CF-VK":{"countryCode":"CF","subdivisionName":"Vakaga","code":"CF-VK"},"CG-11":{"countryCode":"CG","subdivisionName":"Bouenza","code":"CG-11"},"CG-BZV":{"countryCode":"CG","subdivisionName":"Brazzaville","code":"CG-BZV"},"CG-8":{"countryCode":"CG","subdivisionName":"Cuvette","code":"CG-8"},"CG-9":{"countryCode":"CG","subdivisionName":"Niari","code":"CG-9"},"CG-14":{"countryCode":"CG","subdivisionName":"Plateaux","code":"CG-14"},"CG-16":{"countryCode":"CG","subdivisionName":"Pointe-Noire","code":"CG-16"},"CG-13":{"countryCode":"CG","subdivisionName":"Sangha","code":"CG-13"},"CH-AG":{"countryCode":"CH","subdivisionName":"Aargau","code":"CH-AG"},"CH-AR":{"countryCode":"CH","subdivisionName":"Appenzell Ausserrhoden","code":"CH-AR"},"CH-AI":{"countryCode":"CH","subdivisionName":"Appenzell Innerrhoden","code":"CH-AI"},"CH-BL":{"countryCode":"CH","subdivisionName":"Basel-Landschaft","code":"CH-BL"},"CH-BS":{"countryCode":"CH","subdivisionName":"Basel-Stadt","code":"CH-BS"},"CH-BE":{"countryCode":"CH","subdivisionName":"Bern","code":"CH-BE"},"CH-FR":{"countryCode":"CH","subdivisionName":"Fribourg","code":"CH-FR"},"CH-GE":{"countryCode":"CH","subdivisionName":"Geneve","code":"CH-GE"},"CH-GL":{"countryCode":"CH","subdivisionName":"Glarus","code":"CH-GL"},"CH-GR":{"countryCode":"CH","subdivisionName":"Graubunden","code":"CH-GR"},"CH-JU":{"countryCode":"CH","subdivisionName":"Jura","code":"CH-JU"},"CH-LU":{"countryCode":"CH","subdivisionName":"Luzern","code":"CH-LU"},"CH-NE":{"countryCode":"CH","subdivisionName":"Neuchatel","code":"CH-NE"},"CH-NW":{"countryCode":"CH","subdivisionName":"Nidwalden","code":"CH-NW"},"CH-OW":{"countryCode":"CH","subdivisionName":"Obwalden","code":"CH-OW"},"CH-SG":{"countryCode":"CH","subdivisionName":"Sankt Gallen","code":"CH-SG"},"CH-SH":{"countryCode":"CH","subdivisionName":"Schaffhausen","code":"CH-SH"},"CH-SZ":{"countryCode":"CH","subdivisionName":"Schwyz","code":"CH-SZ"},"CH-SO":{"countryCode":"CH","subdivisionName":"Solothurn","code":"CH-SO"},"CH-TG":{"countryCode":"CH","subdivisionName":"Thurgau","code":"CH-TG"},"CH-TI":{"countryCode":"CH","subdivisionName":"Ticino","code":"CH-TI"},"CH-UR":{"countryCode":"CH","subdivisionName":"Uri","code":"CH-UR"},"CH-VS":{"countryCode":"CH","subdivisionName":"Valais","code":"CH-VS"},"CH-VD":{"countryCode":"CH","subdivisionName":"Vaud","code":"CH-VD"},"CH-ZG":{"countryCode":"CH","subdivisionName":"Zug","code":"CH-ZG"},"CH-ZH":{"countryCode":"CH","subdivisionName":"Zurich","code":"CH-ZH"},"CI-AB":{"countryCode":"CI","subdivisionName":"Abidjan","code":"CI-AB"},"CI-BS":{"countryCode":"CI","subdivisionName":"Bas-Sassandra","code":"CI-BS"},"CI-CM":{"countryCode":"CI","subdivisionName":"Comoe","code":"CI-CM"},"CI-DN":{"countryCode":"CI","subdivisionName":"Denguele","code":"CI-DN"},"CI-GD":{"countryCode":"CI","subdivisionName":"Goh-Djiboua","code":"CI-GD"},"CI-LC":{"countryCode":"CI","subdivisionName":"Lacs","code":"CI-LC"},"CI-LG":{"countryCode":"CI","subdivisionName":"Lagunes","code":"CI-LG"},"CI-MG":{"countryCode":"CI","subdivisionName":"Montagnes","code":"CI-MG"},"CI-SM":{"countryCode":"CI","subdivisionName":"Sassandra-Marahoue","code":"CI-SM"},"CI-SV":{"countryCode":"CI","subdivisionName":"Savanes","code":"CI-SV"},"CI-VB":{"countryCode":"CI","subdivisionName":"Vallee du Bandama","code":"CI-VB"},"CI-WR":{"countryCode":"CI","subdivisionName":"Woroba","code":"CI-WR"},"CI-YM":{"countryCode":"CI","subdivisionName":"Yamoussoukro","code":"CI-YM"},"CI-ZZ":{"countryCode":"CI","subdivisionName":"Zanzan","code":"CI-ZZ"},"CL-AI":{"countryCode":"CL","subdivisionName":"Aisen del General Carlos Ibanez del Campo","code":"CL-AI"},"CL-AN":{"countryCode":"CL","subdivisionName":"Antofagasta","code":"CL-AN"},"CL-AP":{"countryCode":"CL","subdivisionName":"Arica y Parinacota","code":"CL-AP"},"CL-AT":{"countryCode":"CL","subdivisionName":"Atacama","code":"CL-AT"},"CL-BI":{"countryCode":"CL","subdivisionName":"Biobio","code":"CL-BI"},"CL-CO":{"countryCode":"CL","subdivisionName":"Coquimbo","code":"CL-CO"},"CL-AR":{"countryCode":"CL","subdivisionName":"La Araucania","code":"CL-AR"},"CL-LI":{"countryCode":"CL","subdivisionName":"Libertador General Bernardo O'Higgins","code":"CL-LI"},"CL-LL":{"countryCode":"CL","subdivisionName":"Los Lagos","code":"CL-LL"},"CL-LR":{"countryCode":"CL","subdivisionName":"Los Rios","code":"CL-LR"},"CL-MA":{"countryCode":"CL","subdivisionName":"Magallanes","code":"CL-MA"},"CL-ML":{"countryCode":"CL","subdivisionName":"Maule","code":"CL-ML"},"CL-NB":{"countryCode":"CL","subdivisionName":"Nuble","code":"CL-NB"},"CL-RM":{"countryCode":"CL","subdivisionName":"Region Metropolitana de Santiago","code":"CL-RM"},"CL-TA":{"countryCode":"CL","subdivisionName":"Tarapaca","code":"CL-TA"},"CL-VS":{"countryCode":"CL","subdivisionName":"Valparaiso","code":"CL-VS"},"CM-AD":{"countryCode":"CM","subdivisionName":"Adamaoua","code":"CM-AD"},"CM-CE":{"countryCode":"CM","subdivisionName":"Centre","code":"CM-CE"},"CM-ES":{"countryCode":"CM","subdivisionName":"Est","code":"CM-ES"},"CM-EN":{"countryCode":"CM","subdivisionName":"Extreme-Nord","code":"CM-EN"},"CM-LT":{"countryCode":"CM","subdivisionName":"Littoral","code":"CM-LT"},"CM-NO":{"countryCode":"CM","subdivisionName":"Nord","code":"CM-NO"},"CM-NW":{"countryCode":"CM","subdivisionName":"Nord-Ouest","code":"CM-NW"},"CM-OU":{"countryCode":"CM","subdivisionName":"Ouest","code":"CM-OU"},"CM-SU":{"countryCode":"CM","subdivisionName":"Sud","code":"CM-SU"},"CM-SW":{"countryCode":"CM","subdivisionName":"Sud-Ouest","code":"CM-SW"},"CN-AH":{"countryCode":"CN","subdivisionName":"Anhui","code":"CN-AH"},"CN-BJ":{"countryCode":"CN","subdivisionName":"Beijing","code":"CN-BJ"},"CN-CQ":{"countryCode":"CN","subdivisionName":"Chongqing","code":"CN-CQ"},"CN-FJ":{"countryCode":"CN","subdivisionName":"Fujian","code":"CN-FJ"},"CN-GS":{"countryCode":"CN","subdivisionName":"Gansu","code":"CN-GS"},"CN-GD":{"countryCode":"CN","subdivisionName":"Guangdong","code":"CN-GD"},"CN-GX":{"countryCode":"CN","subdivisionName":"Guangxi Zhuangzu","code":"CN-GX"},"CN-GZ":{"countryCode":"CN","subdivisionName":"Guizhou","code":"CN-GZ"},"CN-HI":{"countryCode":"CN","subdivisionName":"Hainan","code":"CN-HI"},"CN-HE":{"countryCode":"CN","subdivisionName":"Hebei","code":"CN-HE"},"CN-HL":{"countryCode":"CN","subdivisionName":"Heilongjiang","code":"CN-HL"},"CN-HA":{"countryCode":"CN","subdivisionName":"Henan","code":"CN-HA"},"CN-HB":{"countryCode":"CN","subdivisionName":"Hubei","code":"CN-HB"},"CN-HN":{"countryCode":"CN","subdivisionName":"Hunan","code":"CN-HN"},"CN-JS":{"countryCode":"CN","subdivisionName":"Jiangsu","code":"CN-JS"},"CN-JX":{"countryCode":"CN","subdivisionName":"Jiangxi","code":"CN-JX"},"CN-JL":{"countryCode":"CN","subdivisionName":"Jilin","code":"CN-JL"},"CN-LN":{"countryCode":"CN","subdivisionName":"Liaoning","code":"CN-LN"},"CN-NM":{"countryCode":"CN","subdivisionName":"Nei Mongol","code":"CN-NM"},"CN-NX":{"countryCode":"CN","subdivisionName":"Ningxia Huizu","code":"CN-NX"},"CN-QH":{"countryCode":"CN","subdivisionName":"Qinghai","code":"CN-QH"},"CN-SN":{"countryCode":"CN","subdivisionName":"Shaanxi","code":"CN-SN"},"CN-SD":{"countryCode":"CN","subdivisionName":"Shandong","code":"CN-SD"},"CN-SH":{"countryCode":"CN","subdivisionName":"Shanghai","code":"CN-SH"},"CN-SX":{"countryCode":"CN","subdivisionName":"Shanxi","code":"CN-SX"},"CN-SC":{"countryCode":"CN","subdivisionName":"Sichuan","code":"CN-SC"},"CN-TJ":{"countryCode":"CN","subdivisionName":"Tianjin","code":"CN-TJ"},"CN-XJ":{"countryCode":"CN","subdivisionName":"Xinjiang Uygur","code":"CN-XJ"},"CN-XZ":{"countryCode":"CN","subdivisionName":"Xizang","code":"CN-XZ"},"CN-YN":{"countryCode":"CN","subdivisionName":"Yunnan","code":"CN-YN"},"CN-ZJ":{"countryCode":"CN","subdivisionName":"Zhejiang","code":"CN-ZJ"},"CO-AMA":{"countryCode":"CO","subdivisionName":"Amazonas","code":"CO-AMA"},"CO-ANT":{"countryCode":"CO","subdivisionName":"Antioquia","code":"CO-ANT"},"CO-ARA":{"countryCode":"CO","subdivisionName":"Arauca","code":"CO-ARA"},"CO-ATL":{"countryCode":"CO","subdivisionName":"Atlantico","code":"CO-ATL"},"CO-BOL":{"countryCode":"CO","subdivisionName":"Bolivar","code":"CO-BOL"},"CO-BOY":{"countryCode":"CO","subdivisionName":"Boyaca","code":"CO-BOY"},"CO-CAL":{"countryCode":"CO","subdivisionName":"Caldas","code":"CO-CAL"},"CO-CAQ":{"countryCode":"CO","subdivisionName":"Caqueta","code":"CO-CAQ"},"CO-CAS":{"countryCode":"CO","subdivisionName":"Casanare","code":"CO-CAS"},"CO-CAU":{"countryCode":"CO","subdivisionName":"Cauca","code":"CO-CAU"},"CO-CES":{"countryCode":"CO","subdivisionName":"Cesar","code":"CO-CES"},"CO-CHO":{"countryCode":"CO","subdivisionName":"Choco","code":"CO-CHO"},"CO-COR":{"countryCode":"CO","subdivisionName":"Cordoba","code":"CO-COR"},"CO-CUN":{"countryCode":"CO","subdivisionName":"Cundinamarca","code":"CO-CUN"},"CO-DC":{"countryCode":"CO","subdivisionName":"Distrito Capital de Bogota","code":"CO-DC"},"CO-GUA":{"countryCode":"CO","subdivisionName":"Guainia","code":"CO-GUA"},"CO-GUV":{"countryCode":"CO","subdivisionName":"Guaviare","code":"CO-GUV"},"CO-HUI":{"countryCode":"CO","subdivisionName":"Huila","code":"CO-HUI"},"CO-LAG":{"countryCode":"CO","subdivisionName":"La Guajira","code":"CO-LAG"},"CO-MAG":{"countryCode":"CO","subdivisionName":"Magdalena","code":"CO-MAG"},"CO-MET":{"countryCode":"CO","subdivisionName":"Meta","code":"CO-MET"},"CO-NAR":{"countryCode":"CO","subdivisionName":"Narino","code":"CO-NAR"},"CO-NSA":{"countryCode":"CO","subdivisionName":"Norte de Santander","code":"CO-NSA"},"CO-PUT":{"countryCode":"CO","subdivisionName":"Putumayo","code":"CO-PUT"},"CO-QUI":{"countryCode":"CO","subdivisionName":"Quindio","code":"CO-QUI"},"CO-RIS":{"countryCode":"CO","subdivisionName":"Risaralda","code":"CO-RIS"},"CO-SAP":{"countryCode":"CO","subdivisionName":"San Andres, Providencia y Santa Catalina","code":"CO-SAP"},"CO-SAN":{"countryCode":"CO","subdivisionName":"Santander","code":"CO-SAN"},"CO-SUC":{"countryCode":"CO","subdivisionName":"Sucre","code":"CO-SUC"},"CO-TOL":{"countryCode":"CO","subdivisionName":"Tolima","code":"CO-TOL"},"CO-VAC":{"countryCode":"CO","subdivisionName":"Valle del Cauca","code":"CO-VAC"},"CO-VID":{"countryCode":"CO","subdivisionName":"Vichada","code":"CO-VID"},"CR-A":{"countryCode":"CR","subdivisionName":"Alajuela","code":"CR-A"},"CR-C":{"countryCode":"CR","subdivisionName":"Cartago","code":"CR-C"},"CR-G":{"countryCode":"CR","subdivisionName":"Guanacaste","code":"CR-G"},"CR-H":{"countryCode":"CR","subdivisionName":"Heredia","code":"CR-H"},"CR-L":{"countryCode":"CR","subdivisionName":"Limon","code":"CR-L"},"CR-P":{"countryCode":"CR","subdivisionName":"Puntarenas","code":"CR-P"},"CR-SJ":{"countryCode":"CR","subdivisionName":"San Jose","code":"CR-SJ"},"CU-15":{"countryCode":"CU","subdivisionName":"Artemisa","code":"CU-15"},"CU-09":{"countryCode":"CU","subdivisionName":"Camaguey","code":"CU-09"},"CU-08":{"countryCode":"CU","subdivisionName":"Ciego de Avila","code":"CU-08"},"CU-06":{"countryCode":"CU","subdivisionName":"Cienfuegos","code":"CU-06"},"CU-12":{"countryCode":"CU","subdivisionName":"Granma","code":"CU-12"},"CU-14":{"countryCode":"CU","subdivisionName":"Guantanamo","code":"CU-14"},"CU-11":{"countryCode":"CU","subdivisionName":"Holguin","code":"CU-11"},"CU-99":{"countryCode":"CU","subdivisionName":"Isla de la Juventud","code":"CU-99"},"CU-03":{"countryCode":"CU","subdivisionName":"La Habana","code":"CU-03"},"CU-10":{"countryCode":"CU","subdivisionName":"Las Tunas","code":"CU-10"},"CU-04":{"countryCode":"CU","subdivisionName":"Matanzas","code":"CU-04"},"CU-16":{"countryCode":"CU","subdivisionName":"Mayabeque","code":"CU-16"},"CU-01":{"countryCode":"CU","subdivisionName":"Pinar del Rio","code":"CU-01"},"CU-07":{"countryCode":"CU","subdivisionName":"Sancti Spiritus","code":"CU-07"},"CU-13":{"countryCode":"CU","subdivisionName":"Santiago de Cuba","code":"CU-13"},"CU-05":{"countryCode":"CU","subdivisionName":"Villa Clara","code":"CU-05"},"CV-BV":{"countryCode":"CV","subdivisionName":"Boa Vista","code":"CV-BV"},"CV-BR":{"countryCode":"CV","subdivisionName":"Brava","code":"CV-BR"},"CV-MO":{"countryCode":"CV","subdivisionName":"Mosteiros","code":"CV-MO"},"CV-PN":{"countryCode":"CV","subdivisionName":"Porto Novo","code":"CV-PN"},"CV-PR":{"countryCode":"CV","subdivisionName":"Praia","code":"CV-PR"},"CV-RS":{"countryCode":"CV","subdivisionName":"Ribeira Grande de Santiago","code":"CV-RS"},"CV-SL":{"countryCode":"CV","subdivisionName":"Sal","code":"CV-SL"},"CV-SV":{"countryCode":"CV","subdivisionName":"Sao Vicente","code":"CV-SV"},"CV-TA":{"countryCode":"CV","subdivisionName":"Tarrafal","code":"CV-TA"},"CY-04":{"countryCode":"CY","subdivisionName":"Ammochostos","code":"CY-04"},"CY-06":{"countryCode":"CY","subdivisionName":"Keryneia","code":"CY-06"},"CY-03":{"countryCode":"CY","subdivisionName":"Larnaka","code":"CY-03"},"CY-01":{"countryCode":"CY","subdivisionName":"Lefkosia","code":"CY-01"},"CY-02":{"countryCode":"CY","subdivisionName":"Lemesos","code":"CY-02"},"CY-05":{"countryCode":"CY","subdivisionName":"Pafos","code":"CY-05"},"CZ-31":{"countryCode":"CZ","subdivisionName":"Jihocesky kraj","code":"CZ-31"},"CZ-64":{"countryCode":"CZ","subdivisionName":"Jihomoravsky kraj","code":"CZ-64"},"CZ-41":{"countryCode":"CZ","subdivisionName":"Karlovarsky kraj","code":"CZ-41"},"CZ-63":{"countryCode":"CZ","subdivisionName":"Kraj Vysocina","code":"CZ-63"},"CZ-52":{"countryCode":"CZ","subdivisionName":"Kralovehradecky kraj","code":"CZ-52"},"CZ-51":{"countryCode":"CZ","subdivisionName":"Liberecky kraj","code":"CZ-51"},"CZ-80":{"countryCode":"CZ","subdivisionName":"Moravskoslezsky kraj","code":"CZ-80"},"CZ-71":{"countryCode":"CZ","subdivisionName":"Olomoucky kraj","code":"CZ-71"},"CZ-53":{"countryCode":"CZ","subdivisionName":"Pardubicky kraj","code":"CZ-53"},"CZ-32":{"countryCode":"CZ","subdivisionName":"Plzensky kraj","code":"CZ-32"},"CZ-10":{"countryCode":"CZ","subdivisionName":"Praha, Hlavni mesto","code":"CZ-10"},"CZ-20":{"countryCode":"CZ","subdivisionName":"Stredocesky kraj","code":"CZ-20"},"CZ-42":{"countryCode":"CZ","subdivisionName":"Ustecky kraj","code":"CZ-42"},"CZ-72":{"countryCode":"CZ","subdivisionName":"Zlinsky kraj","code":"CZ-72"},"DE-BW":{"countryCode":"DE","subdivisionName":"Baden-Wurttemberg","code":"DE-BW"},"DE-BY":{"countryCode":"DE","subdivisionName":"Bayern","code":"DE-BY"},"DE-BE":{"countryCode":"DE","subdivisionName":"Berlin","code":"DE-BE"},"DE-BB":{"countryCode":"DE","subdivisionName":"Brandenburg","code":"DE-BB"},"DE-HB":{"countryCode":"DE","subdivisionName":"Bremen","code":"DE-HB"},"DE-HH":{"countryCode":"DE","subdivisionName":"Hamburg","code":"DE-HH"},"DE-HE":{"countryCode":"DE","subdivisionName":"Hessen","code":"DE-HE"},"DE-MV":{"countryCode":"DE","subdivisionName":"Mecklenburg-Vorpommern","code":"DE-MV"},"DE-NI":{"countryCode":"DE","subdivisionName":"Niedersachsen","code":"DE-NI"},"DE-NW":{"countryCode":"DE","subdivisionName":"Nordrhein-Westfalen","code":"DE-NW"},"DE-RP":{"countryCode":"DE","subdivisionName":"Rheinland-Pfalz","code":"DE-RP"},"DE-SL":{"countryCode":"DE","subdivisionName":"Saarland","code":"DE-SL"},"DE-SN":{"countryCode":"DE","subdivisionName":"Sachsen","code":"DE-SN"},"DE-ST":{"countryCode":"DE","subdivisionName":"Sachsen-Anhalt","code":"DE-ST"},"DE-SH":{"countryCode":"DE","subdivisionName":"Schleswig-Holstein","code":"DE-SH"},"DE-TH":{"countryCode":"DE","subdivisionName":"Thuringen","code":"DE-TH"},"DJ-AR":{"countryCode":"DJ","subdivisionName":"Arta","code":"DJ-AR"},"DJ-DI":{"countryCode":"DJ","subdivisionName":"Dikhil","code":"DJ-DI"},"DJ-DJ":{"countryCode":"DJ","subdivisionName":"Djibouti","code":"DJ-DJ"},"DK-84":{"countryCode":"DK","subdivisionName":"Hovedstaden","code":"DK-84"},"DK-82":{"countryCode":"DK","subdivisionName":"Midtjylland","code":"DK-82"},"DK-81":{"countryCode":"DK","subdivisionName":"Nordjylland","code":"DK-81"},"DK-85":{"countryCode":"DK","subdivisionName":"Sjaelland","code":"DK-85"},"DK-83":{"countryCode":"DK","subdivisionName":"Syddanmark","code":"DK-83"},"DM-02":{"countryCode":"DM","subdivisionName":"Saint Andrew","code":"DM-02"},"DM-04":{"countryCode":"DM","subdivisionName":"Saint George","code":"DM-04"},"DM-05":{"countryCode":"DM","subdivisionName":"Saint John","code":"DM-05"},"DM-06":{"countryCode":"DM","subdivisionName":"Saint Joseph","code":"DM-06"},"DM-07":{"countryCode":"DM","subdivisionName":"Saint Luke","code":"DM-07"},"DM-09":{"countryCode":"DM","subdivisionName":"Saint Patrick","code":"DM-09"},"DM-10":{"countryCode":"DM","subdivisionName":"Saint Paul","code":"DM-10"},"DO-02":{"countryCode":"DO","subdivisionName":"Azua","code":"DO-02"},"DO-03":{"countryCode":"DO","subdivisionName":"Baoruco","code":"DO-03"},"DO-04":{"countryCode":"DO","subdivisionName":"Barahona","code":"DO-04"},"DO-05":{"countryCode":"DO","subdivisionName":"Dajabon","code":"DO-05"},"DO-01":{"countryCode":"DO","subdivisionName":"Distrito Nacional (Santo Domingo)","code":"DO-01"},"DO-06":{"countryCode":"DO","subdivisionName":"Duarte","code":"DO-06"},"DO-08":{"countryCode":"DO","subdivisionName":"El Seibo","code":"DO-08"},"DO-07":{"countryCode":"DO","subdivisionName":"Elias Pina","code":"DO-07"},"DO-09":{"countryCode":"DO","subdivisionName":"Espaillat","code":"DO-09"},"DO-30":{"countryCode":"DO","subdivisionName":"Hato Mayor","code":"DO-30"},"DO-19":{"countryCode":"DO","subdivisionName":"Hermanas Mirabal","code":"DO-19"},"DO-10":{"countryCode":"DO","subdivisionName":"Independencia","code":"DO-10"},"DO-11":{"countryCode":"DO","subdivisionName":"La Altagracia","code":"DO-11"},"DO-12":{"countryCode":"DO","subdivisionName":"La Romana","code":"DO-12"},"DO-13":{"countryCode":"DO","subdivisionName":"La Vega","code":"DO-13"},"DO-14":{"countryCode":"DO","subdivisionName":"Maria Trinidad Sanchez","code":"DO-14"},"DO-28":{"countryCode":"DO","subdivisionName":"Monsenor Nouel","code":"DO-28"},"DO-15":{"countryCode":"DO","subdivisionName":"Monte Cristi","code":"DO-15"},"DO-29":{"countryCode":"DO","subdivisionName":"Monte Plata","code":"DO-29"},"DO-16":{"countryCode":"DO","subdivisionName":"Pedernales","code":"DO-16"},"DO-17":{"countryCode":"DO","subdivisionName":"Peravia","code":"DO-17"},"DO-18":{"countryCode":"DO","subdivisionName":"Puerto Plata","code":"DO-18"},"DO-20":{"countryCode":"DO","subdivisionName":"Samana","code":"DO-20"},"DO-21":{"countryCode":"DO","subdivisionName":"San Cristobal","code":"DO-21"},"DO-31":{"countryCode":"DO","subdivisionName":"San Jose de Ocoa","code":"DO-31"},"DO-22":{"countryCode":"DO","subdivisionName":"San Juan","code":"DO-22"},"DO-23":{"countryCode":"DO","subdivisionName":"San Pedro de Macoris","code":"DO-23"},"DO-24":{"countryCode":"DO","subdivisionName":"Sanchez Ramirez","code":"DO-24"},"DO-25":{"countryCode":"DO","subdivisionName":"Santiago","code":"DO-25"},"DO-26":{"countryCode":"DO","subdivisionName":"Santiago Rodriguez","code":"DO-26"},"DO-27":{"countryCode":"DO","subdivisionName":"Valverde","code":"DO-27"},"DZ-01":{"countryCode":"DZ","subdivisionName":"Adrar","code":"DZ-01"},"DZ-44":{"countryCode":"DZ","subdivisionName":"Ain Defla","code":"DZ-44"},"DZ-46":{"countryCode":"DZ","subdivisionName":"Ain Temouchent","code":"DZ-46"},"DZ-16":{"countryCode":"DZ","subdivisionName":"Alger","code":"DZ-16"},"DZ-23":{"countryCode":"DZ","subdivisionName":"Annaba","code":"DZ-23"},"DZ-05":{"countryCode":"DZ","subdivisionName":"Batna","code":"DZ-05"},"DZ-08":{"countryCode":"DZ","subdivisionName":"Bechar","code":"DZ-08"},"DZ-06":{"countryCode":"DZ","subdivisionName":"Bejaia","code":"DZ-06"},"DZ-07":{"countryCode":"DZ","subdivisionName":"Biskra","code":"DZ-07"},"DZ-09":{"countryCode":"DZ","subdivisionName":"Blida","code":"DZ-09"},"DZ-34":{"countryCode":"DZ","subdivisionName":"Bordj Bou Arreridj","code":"DZ-34"},"DZ-10":{"countryCode":"DZ","subdivisionName":"Bouira","code":"DZ-10"},"DZ-35":{"countryCode":"DZ","subdivisionName":"Boumerdes","code":"DZ-35"},"DZ-02":{"countryCode":"DZ","subdivisionName":"Chlef","code":"DZ-02"},"DZ-25":{"countryCode":"DZ","subdivisionName":"Constantine","code":"DZ-25"},"DZ-56":{"countryCode":"DZ","subdivisionName":"Djanet","code":"DZ-56"},"DZ-17":{"countryCode":"DZ","subdivisionName":"Djelfa","code":"DZ-17"},"DZ-32":{"countryCode":"DZ","subdivisionName":"El Bayadh","code":"DZ-32"},"DZ-57":{"countryCode":"DZ","subdivisionName":"El Meghaier","code":"DZ-57"},"DZ-39":{"countryCode":"DZ","subdivisionName":"El Oued","code":"DZ-39"},"DZ-36":{"countryCode":"DZ","subdivisionName":"El Tarf","code":"DZ-36"},"DZ-47":{"countryCode":"DZ","subdivisionName":"Ghardaia","code":"DZ-47"},"DZ-24":{"countryCode":"DZ","subdivisionName":"Guelma","code":"DZ-24"},"DZ-33":{"countryCode":"DZ","subdivisionName":"Illizi","code":"DZ-33"},"DZ-53":{"countryCode":"DZ","subdivisionName":"In Salah","code":"DZ-53"},"DZ-18":{"countryCode":"DZ","subdivisionName":"Jijel","code":"DZ-18"},"DZ-40":{"countryCode":"DZ","subdivisionName":"Khenchela","code":"DZ-40"},"DZ-03":{"countryCode":"DZ","subdivisionName":"Laghouat","code":"DZ-03"},"DZ-28":{"countryCode":"DZ","subdivisionName":"M'sila","code":"DZ-28"},"DZ-29":{"countryCode":"DZ","subdivisionName":"Mascara","code":"DZ-29"},"DZ-26":{"countryCode":"DZ","subdivisionName":"Medea","code":"DZ-26"},"DZ-43":{"countryCode":"DZ","subdivisionName":"Mila","code":"DZ-43"},"DZ-27":{"countryCode":"DZ","subdivisionName":"Mostaganem","code":"DZ-27"},"DZ-45":{"countryCode":"DZ","subdivisionName":"Naama","code":"DZ-45"},"DZ-31":{"countryCode":"DZ","subdivisionName":"Oran","code":"DZ-31"},"DZ-30":{"countryCode":"DZ","subdivisionName":"Ouargla","code":"DZ-30"},"DZ-51":{"countryCode":"DZ","subdivisionName":"Ouled Djellal","code":"DZ-51"},"DZ-04":{"countryCode":"DZ","subdivisionName":"Oum el Bouaghi","code":"DZ-04"},"DZ-48":{"countryCode":"DZ","subdivisionName":"Relizane","code":"DZ-48"},"DZ-20":{"countryCode":"DZ","subdivisionName":"Saida","code":"DZ-20"},"DZ-19":{"countryCode":"DZ","subdivisionName":"Setif","code":"DZ-19"},"DZ-22":{"countryCode":"DZ","subdivisionName":"Sidi Bel Abbes","code":"DZ-22"},"DZ-21":{"countryCode":"DZ","subdivisionName":"Skikda","code":"DZ-21"},"DZ-41":{"countryCode":"DZ","subdivisionName":"Souk Ahras","code":"DZ-41"},"DZ-11":{"countryCode":"DZ","subdivisionName":"Tamanrasset","code":"DZ-11"},"DZ-12":{"countryCode":"DZ","subdivisionName":"Tebessa","code":"DZ-12"},"DZ-14":{"countryCode":"DZ","subdivisionName":"Tiaret","code":"DZ-14"},"DZ-49":{"countryCode":"DZ","subdivisionName":"Timimoun","code":"DZ-49"},"DZ-37":{"countryCode":"DZ","subdivisionName":"Tindouf","code":"DZ-37"},"DZ-42":{"countryCode":"DZ","subdivisionName":"Tipaza","code":"DZ-42"},"DZ-38":{"countryCode":"DZ","subdivisionName":"Tissemsilt","code":"DZ-38"},"DZ-15":{"countryCode":"DZ","subdivisionName":"Tizi Ouzou","code":"DZ-15"},"DZ-13":{"countryCode":"DZ","subdivisionName":"Tlemcen","code":"DZ-13"},"DZ-55":{"countryCode":"DZ","subdivisionName":"Touggourt","code":"DZ-55"},"EC-A":{"countryCode":"EC","subdivisionName":"Azuay","code":"EC-A"},"EC-B":{"countryCode":"EC","subdivisionName":"Bolivar","code":"EC-B"},"EC-F":{"countryCode":"EC","subdivisionName":"Canar","code":"EC-F"},"EC-C":{"countryCode":"EC","subdivisionName":"Carchi","code":"EC-C"},"EC-H":{"countryCode":"EC","subdivisionName":"Chimborazo","code":"EC-H"},"EC-X":{"countryCode":"EC","subdivisionName":"Cotopaxi","code":"EC-X"},"EC-O":{"countryCode":"EC","subdivisionName":"El Oro","code":"EC-O"},"EC-E":{"countryCode":"EC","subdivisionName":"Esmeraldas","code":"EC-E"},"EC-W":{"countryCode":"EC","subdivisionName":"Galapagos","code":"EC-W"},"EC-G":{"countryCode":"EC","subdivisionName":"Guayas","code":"EC-G"},"EC-I":{"countryCode":"EC","subdivisionName":"Imbabura","code":"EC-I"},"EC-L":{"countryCode":"EC","subdivisionName":"Loja","code":"EC-L"},"EC-R":{"countryCode":"EC","subdivisionName":"Los Rios","code":"EC-R"},"EC-M":{"countryCode":"EC","subdivisionName":"Manabi","code":"EC-M"},"EC-S":{"countryCode":"EC","subdivisionName":"Morona Santiago","code":"EC-S"},"EC-N":{"countryCode":"EC","subdivisionName":"Napo","code":"EC-N"},"EC-D":{"countryCode":"EC","subdivisionName":"Orellana","code":"EC-D"},"EC-Y":{"countryCode":"EC","subdivisionName":"Pastaza","code":"EC-Y"},"EC-P":{"countryCode":"EC","subdivisionName":"Pichincha","code":"EC-P"},"EC-SE":{"countryCode":"EC","subdivisionName":"Santa Elena","code":"EC-SE"},"EC-SD":{"countryCode":"EC","subdivisionName":"Santo Domingo de los Tsachilas","code":"EC-SD"},"EC-U":{"countryCode":"EC","subdivisionName":"Sucumbios","code":"EC-U"},"EC-T":{"countryCode":"EC","subdivisionName":"Tungurahua","code":"EC-T"},"EC-Z":{"countryCode":"EC","subdivisionName":"Zamora Chinchipe","code":"EC-Z"},"EE-37":{"countryCode":"EE","subdivisionName":"Harjumaa","code":"EE-37"},"EE-39":{"countryCode":"EE","subdivisionName":"Hiiumaa","code":"EE-39"},"EE-45":{"countryCode":"EE","subdivisionName":"Ida-Virumaa","code":"EE-45"},"EE-52":{"countryCode":"EE","subdivisionName":"Jarvamaa","code":"EE-52"},"EE-50":{"countryCode":"EE","subdivisionName":"Jogevamaa","code":"EE-50"},"EE-60":{"countryCode":"EE","subdivisionName":"Laane-Virumaa","code":"EE-60"},"EE-56":{"countryCode":"EE","subdivisionName":"Laanemaa","code":"EE-56"},"EE-68":{"countryCode":"EE","subdivisionName":"Parnumaa","code":"EE-68"},"EE-64":{"countryCode":"EE","subdivisionName":"Polvamaa","code":"EE-64"},"EE-71":{"countryCode":"EE","subdivisionName":"Raplamaa","code":"EE-71"},"EE-74":{"countryCode":"EE","subdivisionName":"Saaremaa","code":"EE-74"},"EE-79":{"countryCode":"EE","subdivisionName":"Tartumaa","code":"EE-79"},"EE-81":{"countryCode":"EE","subdivisionName":"Valgamaa","code":"EE-81"},"EE-84":{"countryCode":"EE","subdivisionName":"Viljandimaa","code":"EE-84"},"EE-87":{"countryCode":"EE","subdivisionName":"Vorumaa","code":"EE-87"},"EG-DK":{"countryCode":"EG","subdivisionName":"Ad Daqahliyah","code":"EG-DK"},"EG-BA":{"countryCode":"EG","subdivisionName":"Al Bahr al Ahmar","code":"EG-BA"},"EG-BH":{"countryCode":"EG","subdivisionName":"Al Buhayrah","code":"EG-BH"},"EG-FYM":{"countryCode":"EG","subdivisionName":"Al Fayyum","code":"EG-FYM"},"EG-GH":{"countryCode":"EG","subdivisionName":"Al Gharbiyah","code":"EG-GH"},"EG-ALX":{"countryCode":"EG","subdivisionName":"Al Iskandariyah","code":"EG-ALX"},"EG-IS":{"countryCode":"EG","subdivisionName":"Al Isma'iliyah","code":"EG-IS"},"EG-GZ":{"countryCode":"EG","subdivisionName":"Al Jizah","code":"EG-GZ"},"EG-MNF":{"countryCode":"EG","subdivisionName":"Al Minufiyah","code":"EG-MNF"},"EG-MN":{"countryCode":"EG","subdivisionName":"Al Minya","code":"EG-MN"},"EG-C":{"countryCode":"EG","subdivisionName":"Al Qahirah","code":"EG-C"},"EG-KB":{"countryCode":"EG","subdivisionName":"Al Qalyubiyah","code":"EG-KB"},"EG-LX":{"countryCode":"EG","subdivisionName":"Al Uqsur","code":"EG-LX"},"EG-WAD":{"countryCode":"EG","subdivisionName":"Al Wadi al Jadid","code":"EG-WAD"},"EG-SUZ":{"countryCode":"EG","subdivisionName":"As Suways","code":"EG-SUZ"},"EG-SHR":{"countryCode":"EG","subdivisionName":"Ash Sharqiyah","code":"EG-SHR"},"EG-ASN":{"countryCode":"EG","subdivisionName":"Aswan","code":"EG-ASN"},"EG-AST":{"countryCode":"EG","subdivisionName":"Asyut","code":"EG-AST"},"EG-BNS":{"countryCode":"EG","subdivisionName":"Bani Suwayf","code":"EG-BNS"},"EG-PTS":{"countryCode":"EG","subdivisionName":"Bur Sa'id","code":"EG-PTS"},"EG-DT":{"countryCode":"EG","subdivisionName":"Dumyat","code":"EG-DT"},"EG-JS":{"countryCode":"EG","subdivisionName":"Janub Sina'","code":"EG-JS"},"EG-KFS":{"countryCode":"EG","subdivisionName":"Kafr ash Shaykh","code":"EG-KFS"},"EG-MT":{"countryCode":"EG","subdivisionName":"Matruh","code":"EG-MT"},"EG-KN":{"countryCode":"EG","subdivisionName":"Qina","code":"EG-KN"},"EG-SIN":{"countryCode":"EG","subdivisionName":"Shamal Sina'","code":"EG-SIN"},"EG-SHG":{"countryCode":"EG","subdivisionName":"Suhaj","code":"EG-SHG"},"ER-MA":{"countryCode":"ER","subdivisionName":"Al Awsat","code":"ER-MA"},"ER-GB":{"countryCode":"ER","subdivisionName":"Qash-Barkah","code":"ER-GB"},"ES-AN":{"countryCode":"ES","subdivisionName":"Andalucia","code":"ES-AN"},"ES-AR":{"countryCode":"ES","subdivisionName":"Aragon","code":"ES-AR"},"ES-AS":{"countryCode":"ES","subdivisionName":"Asturias, Principado de","code":"ES-AS"},"ES-CN":{"countryCode":"ES","subdivisionName":"Canarias","code":"ES-CN"},"ES-CB":{"countryCode":"ES","subdivisionName":"Cantabria","code":"ES-CB"},"ES-CL":{"countryCode":"ES","subdivisionName":"Castilla y Leon","code":"ES-CL"},"ES-CM":{"countryCode":"ES","subdivisionName":"Castilla-La Mancha","code":"ES-CM"},"ES-CT":{"countryCode":"ES","subdivisionName":"Catalunya","code":"ES-CT"},"ES-CE":{"countryCode":"ES","subdivisionName":"Ceuta","code":"ES-CE"},"ES-EX":{"countryCode":"ES","subdivisionName":"Extremadura","code":"ES-EX"},"ES-GA":{"countryCode":"ES","subdivisionName":"Galicia","code":"ES-GA"},"ES-IB":{"countryCode":"ES","subdivisionName":"Illes Balears","code":"ES-IB"},"ES-RI":{"countryCode":"ES","subdivisionName":"La Rioja","code":"ES-RI"},"ES-MD":{"countryCode":"ES","subdivisionName":"Madrid, Comunidad de","code":"ES-MD"},"ES-ML":{"countryCode":"ES","subdivisionName":"Melilla","code":"ES-ML"},"ES-MC":{"countryCode":"ES","subdivisionName":"Murcia, Region de","code":"ES-MC"},"ES-NC":{"countryCode":"ES","subdivisionName":"Navarra, Comunidad Foral de","code":"ES-NC"},"ES-PV":{"countryCode":"ES","subdivisionName":"Pais Vasco","code":"ES-PV"},"ES-VC":{"countryCode":"ES","subdivisionName":"Valenciana, Comunidad","code":"ES-VC"},"ET-AA":{"countryCode":"ET","subdivisionName":"Addis Ababa","code":"ET-AA"},"ET-AF":{"countryCode":"ET","subdivisionName":"Afar","code":"ET-AF"},"ET-AM":{"countryCode":"ET","subdivisionName":"Amara","code":"ET-AM"},"ET-BE":{"countryCode":"ET","subdivisionName":"Benshangul-Gumaz","code":"ET-BE"},"ET-DD":{"countryCode":"ET","subdivisionName":"Dire Dawa","code":"ET-DD"},"ET-HA":{"countryCode":"ET","subdivisionName":"Harari People","code":"ET-HA"},"ET-OR":{"countryCode":"ET","subdivisionName":"Oromia","code":"ET-OR"},"ET-SO":{"countryCode":"ET","subdivisionName":"Somali","code":"ET-SO"},"ET-SN":{"countryCode":"ET","subdivisionName":"Southern Nations, Nationalities and Peoples","code":"ET-SN"},"ET-TI":{"countryCode":"ET","subdivisionName":"Tigrai","code":"ET-TI"},"FI-02":{"countryCode":"FI","subdivisionName":"Etela-Karjala","code":"FI-02"},"FI-03":{"countryCode":"FI","subdivisionName":"Etela-Pohjanmaa","code":"FI-03"},"FI-04":{"countryCode":"FI","subdivisionName":"Etela-Savo","code":"FI-04"},"FI-05":{"countryCode":"FI","subdivisionName":"Kainuu","code":"FI-05"},"FI-06":{"countryCode":"FI","subdivisionName":"Kanta-Hame","code":"FI-06"},"FI-07":{"countryCode":"FI","subdivisionName":"Keski-Pohjanmaa","code":"FI-07"},"FI-08":{"countryCode":"FI","subdivisionName":"Keski-Suomi","code":"FI-08"},"FI-09":{"countryCode":"FI","subdivisionName":"Kymenlaakso","code":"FI-09"},"FI-10":{"countryCode":"FI","subdivisionName":"Lappi","code":"FI-10"},"FI-16":{"countryCode":"FI","subdivisionName":"Paijat-Hame","code":"FI-16"},"FI-11":{"countryCode":"FI","subdivisionName":"Pirkanmaa","code":"FI-11"},"FI-12":{"countryCode":"FI","subdivisionName":"Pohjanmaa","code":"FI-12"},"FI-13":{"countryCode":"FI","subdivisionName":"Pohjois-Karjala","code":"FI-13"},"FI-14":{"countryCode":"FI","subdivisionName":"Pohjois-Pohjanmaa","code":"FI-14"},"FI-15":{"countryCode":"FI","subdivisionName":"Pohjois-Savo","code":"FI-15"},"FI-17":{"countryCode":"FI","subdivisionName":"Satakunta","code":"FI-17"},"FI-18":{"countryCode":"FI","subdivisionName":"Uusimaa","code":"FI-18"},"FI-19":{"countryCode":"FI","subdivisionName":"Varsinais-Suomi","code":"FI-19"},"FJ-C":{"countryCode":"FJ","subdivisionName":"Central","code":"FJ-C"},"FJ-E":{"countryCode":"FJ","subdivisionName":"Eastern","code":"FJ-E"},"FJ-N":{"countryCode":"FJ","subdivisionName":"Northern","code":"FJ-N"},"FJ-R":{"countryCode":"FJ","subdivisionName":"Rotuma","code":"FJ-R"},"FJ-W":{"countryCode":"FJ","subdivisionName":"Western","code":"FJ-W"},"FM-TRK":{"countryCode":"FM","subdivisionName":"Chuuk","code":"FM-TRK"},"FM-KSA":{"countryCode":"FM","subdivisionName":"Kosrae","code":"FM-KSA"},"FM-PNI":{"countryCode":"FM","subdivisionName":"Pohnpei","code":"FM-PNI"},"FM-YAP":{"countryCode":"FM","subdivisionName":"Yap","code":"FM-YAP"},"FR-ARA":{"countryCode":"FR","subdivisionName":"Auvergne-Rhone-Alpes","code":"FR-ARA"},"FR-BFC":{"countryCode":"FR","subdivisionName":"Bourgogne-Franche-Comte","code":"FR-BFC"},"FR-BRE":{"countryCode":"FR","subdivisionName":"Bretagne","code":"FR-BRE"},"FR-CVL":{"countryCode":"FR","subdivisionName":"Centre-Val de Loire","code":"FR-CVL"},"FR-20R":{"countryCode":"FR","subdivisionName":"Corse","code":"FR-20R"},"FR-GES":{"countryCode":"FR","subdivisionName":"Grand-Est","code":"FR-GES"},"FR-HDF":{"countryCode":"FR","subdivisionName":"Hauts-de-France","code":"FR-HDF"},"FR-IDF":{"countryCode":"FR","subdivisionName":"Ile-de-France","code":"FR-IDF"},"FR-NOR":{"countryCode":"FR","subdivisionName":"Normandie","code":"FR-NOR"},"FR-NAQ":{"countryCode":"FR","subdivisionName":"Nouvelle-Aquitaine","code":"FR-NAQ"},"FR-OCC":{"countryCode":"FR","subdivisionName":"Occitanie","code":"FR-OCC"},"FR-PDL":{"countryCode":"FR","subdivisionName":"Pays-de-la-Loire","code":"FR-PDL"},"FR-PAC":{"countryCode":"FR","subdivisionName":"Provence-Alpes-Cote-d'Azur","code":"FR-PAC"},"GA-1":{"countryCode":"GA","subdivisionName":"Estuaire","code":"GA-1"},"GA-2":{"countryCode":"GA","subdivisionName":"Haut-Ogooue","code":"GA-2"},"GA-3":{"countryCode":"GA","subdivisionName":"Moyen-Ogooue","code":"GA-3"},"GA-4":{"countryCode":"GA","subdivisionName":"Ngounie","code":"GA-4"},"GA-5":{"countryCode":"GA","subdivisionName":"Nyanga","code":"GA-5"},"GA-8":{"countryCode":"GA","subdivisionName":"Ogooue-Maritime","code":"GA-8"},"GA-9":{"countryCode":"GA","subdivisionName":"Woleu-Ntem","code":"GA-9"},"GB-ENG":{"countryCode":"GB","subdivisionName":"England","code":"GB-ENG"},"GB-NIR":{"countryCode":"GB","subdivisionName":"Northern Ireland","code":"GB-NIR"},"GB-SCT":{"countryCode":"GB","subdivisionName":"Scotland","code":"GB-SCT"},"GB-WLS":{"countryCode":"GB","subdivisionName":"Wales","code":"GB-WLS"},"GD-01":{"countryCode":"GD","subdivisionName":"Saint Andrew","code":"GD-01"},"GD-02":{"countryCode":"GD","subdivisionName":"Saint David","code":"GD-02"},"GD-03":{"countryCode":"GD","subdivisionName":"Saint George","code":"GD-03"},"GD-04":{"countryCode":"GD","subdivisionName":"Saint John","code":"GD-04"},"GD-05":{"countryCode":"GD","subdivisionName":"Saint Mark","code":"GD-05"},"GD-06":{"countryCode":"GD","subdivisionName":"Saint Patrick","code":"GD-06"},"GD-10":{"countryCode":"GD","subdivisionName":"Southern Grenadine Islands","code":"GD-10"},"GE-AB":{"countryCode":"GE","subdivisionName":"Abkhazia","code":"GE-AB"},"GE-AJ":{"countryCode":"GE","subdivisionName":"Ajaria","code":"GE-AJ"},"GE-GU":{"countryCode":"GE","subdivisionName":"Guria","code":"GE-GU"},"GE-IM":{"countryCode":"GE","subdivisionName":"Imereti","code":"GE-IM"},"GE-KA":{"countryCode":"GE","subdivisionName":"K'akheti","code":"GE-KA"},"GE-KK":{"countryCode":"GE","subdivisionName":"Kvemo Kartli","code":"GE-KK"},"GE-MM":{"countryCode":"GE","subdivisionName":"Mtskheta-Mtianeti","code":"GE-MM"},"GE-RL":{"countryCode":"GE","subdivisionName":"Rach'a-Lechkhumi-Kvemo Svaneti","code":"GE-RL"},"GE-SZ":{"countryCode":"GE","subdivisionName":"Samegrelo-Zemo Svaneti","code":"GE-SZ"},"GE-SJ":{"countryCode":"GE","subdivisionName":"Samtskhe-Javakheti","code":"GE-SJ"},"GE-SK":{"countryCode":"GE","subdivisionName":"Shida Kartli","code":"GE-SK"},"GE-TB":{"countryCode":"GE","subdivisionName":"Tbilisi","code":"GE-TB"},"GH-AF":{"countryCode":"GH","subdivisionName":"Ahafo","code":"GH-AF"},"GH-AH":{"countryCode":"GH","subdivisionName":"Ashanti","code":"GH-AH"},"GH-BO":{"countryCode":"GH","subdivisionName":"Bono","code":"GH-BO"},"GH-BE":{"countryCode":"GH","subdivisionName":"Bono East","code":"GH-BE"},"GH-CP":{"countryCode":"GH","subdivisionName":"Central","code":"GH-CP"},"GH-EP":{"countryCode":"GH","subdivisionName":"Eastern","code":"GH-EP"},"GH-AA":{"countryCode":"GH","subdivisionName":"Greater Accra","code":"GH-AA"},"GH-NP":{"countryCode":"GH","subdivisionName":"Northern","code":"GH-NP"},"GH-UE":{"countryCode":"GH","subdivisionName":"Upper East","code":"GH-UE"},"GH-UW":{"countryCode":"GH","subdivisionName":"Upper West","code":"GH-UW"},"GH-TV":{"countryCode":"GH","subdivisionName":"Volta","code":"GH-TV"},"GH-WP":{"countryCode":"GH","subdivisionName":"Western","code":"GH-WP"},"GL-AV":{"countryCode":"GL","subdivisionName":"Avannaata Kommunia","code":"GL-AV"},"GL-KU":{"countryCode":"GL","subdivisionName":"Kommune Kujalleq","code":"GL-KU"},"GL-QT":{"countryCode":"GL","subdivisionName":"Kommune Qeqertalik","code":"GL-QT"},"GL-SM":{"countryCode":"GL","subdivisionName":"Kommuneqarfik Sermersooq","code":"GL-SM"},"GL-QE":{"countryCode":"GL","subdivisionName":"Qeqqata Kommunia","code":"GL-QE"},"GM-B":{"countryCode":"GM","subdivisionName":"Banjul","code":"GM-B"},"GM-M":{"countryCode":"GM","subdivisionName":"Central River","code":"GM-M"},"GM-L":{"countryCode":"GM","subdivisionName":"Lower River","code":"GM-L"},"GM-N":{"countryCode":"GM","subdivisionName":"North Bank","code":"GM-N"},"GM-U":{"countryCode":"GM","subdivisionName":"Upper River","code":"GM-U"},"GM-W":{"countryCode":"GM","subdivisionName":"Western","code":"GM-W"},"GN-BF":{"countryCode":"GN","subdivisionName":"Boffa","code":"GN-BF"},"GN-BK":{"countryCode":"GN","subdivisionName":"Boke","code":"GN-BK"},"GN-C":{"countryCode":"GN","subdivisionName":"Conakry","code":"GN-C"},"GN-DB":{"countryCode":"GN","subdivisionName":"Dabola","code":"GN-DB"},"GN-DL":{"countryCode":"GN","subdivisionName":"Dalaba","code":"GN-DL"},"GN-DI":{"countryCode":"GN","subdivisionName":"Dinguiraye","code":"GN-DI"},"GN-FR":{"countryCode":"GN","subdivisionName":"Fria","code":"GN-FR"},"GN-KA":{"countryCode":"GN","subdivisionName":"Kankan","code":"GN-KA"},"GN-KO":{"countryCode":"GN","subdivisionName":"Kouroussa","code":"GN-KO"},"GN-LA":{"countryCode":"GN","subdivisionName":"Labe","code":"GN-LA"},"GN-SI":{"countryCode":"GN","subdivisionName":"Siguiri","code":"GN-SI"},"GQ-AN":{"countryCode":"GQ","subdivisionName":"Annobon","code":"GQ-AN"},"GQ-BN":{"countryCode":"GQ","subdivisionName":"Bioko Norte","code":"GQ-BN"},"GQ-CS":{"countryCode":"GQ","subdivisionName":"Centro Sur","code":"GQ-CS"},"GQ-KN":{"countryCode":"GQ","subdivisionName":"Kie-Ntem","code":"GQ-KN"},"GQ-LI":{"countryCode":"GQ","subdivisionName":"Litoral","code":"GQ-LI"},"GQ-WN":{"countryCode":"GQ","subdivisionName":"Wele-Nzas","code":"GQ-WN"},"GR-A":{"countryCode":"GR","subdivisionName":"Anatoliki Makedonia kai Thraki","code":"GR-A"},"GR-I":{"countryCode":"GR","subdivisionName":"Attiki","code":"GR-I"},"GR-G":{"countryCode":"GR","subdivisionName":"Dytiki Ellada","code":"GR-G"},"GR-C":{"countryCode":"GR","subdivisionName":"Dytiki Makedonia","code":"GR-C"},"GR-F":{"countryCode":"GR","subdivisionName":"Ionia Nisia","code":"GR-F"},"GR-D":{"countryCode":"GR","subdivisionName":"Ipeiros","code":"GR-D"},"GR-B":{"countryCode":"GR","subdivisionName":"Kentriki Makedonia","code":"GR-B"},"GR-M":{"countryCode":"GR","subdivisionName":"Kriti","code":"GR-M"},"GR-L":{"countryCode":"GR","subdivisionName":"Notio Aigaio","code":"GR-L"},"GR-J":{"countryCode":"GR","subdivisionName":"Peloponnisos","code":"GR-J"},"GR-H":{"countryCode":"GR","subdivisionName":"Sterea Ellada","code":"GR-H"},"GR-E":{"countryCode":"GR","subdivisionName":"Thessalia","code":"GR-E"},"GR-K":{"countryCode":"GR","subdivisionName":"Voreio Aigaio","code":"GR-K"},"GT-16":{"countryCode":"GT","subdivisionName":"Alta Verapaz","code":"GT-16"},"GT-15":{"countryCode":"GT","subdivisionName":"Baja Verapaz","code":"GT-15"},"GT-04":{"countryCode":"GT","subdivisionName":"Chimaltenango","code":"GT-04"},"GT-20":{"countryCode":"GT","subdivisionName":"Chiquimula","code":"GT-20"},"GT-02":{"countryCode":"GT","subdivisionName":"El Progreso","code":"GT-02"},"GT-05":{"countryCode":"GT","subdivisionName":"Escuintla","code":"GT-05"},"GT-01":{"countryCode":"GT","subdivisionName":"Guatemala","code":"GT-01"},"GT-13":{"countryCode":"GT","subdivisionName":"Huehuetenango","code":"GT-13"},"GT-18":{"countryCode":"GT","subdivisionName":"Izabal","code":"GT-18"},"GT-21":{"countryCode":"GT","subdivisionName":"Jalapa","code":"GT-21"},"GT-22":{"countryCode":"GT","subdivisionName":"Jutiapa","code":"GT-22"},"GT-17":{"countryCode":"GT","subdivisionName":"Peten","code":"GT-17"},"GT-09":{"countryCode":"GT","subdivisionName":"Quetzaltenango","code":"GT-09"},"GT-14":{"countryCode":"GT","subdivisionName":"Quiche","code":"GT-14"},"GT-11":{"countryCode":"GT","subdivisionName":"Retalhuleu","code":"GT-11"},"GT-03":{"countryCode":"GT","subdivisionName":"Sacatepequez","code":"GT-03"},"GT-12":{"countryCode":"GT","subdivisionName":"San Marcos","code":"GT-12"},"GT-06":{"countryCode":"GT","subdivisionName":"Santa Rosa","code":"GT-06"},"GT-07":{"countryCode":"GT","subdivisionName":"Solola","code":"GT-07"},"GT-10":{"countryCode":"GT","subdivisionName":"Suchitepequez","code":"GT-10"},"GT-08":{"countryCode":"GT","subdivisionName":"Totonicapan","code":"GT-08"},"GT-19":{"countryCode":"GT","subdivisionName":"Zacapa","code":"GT-19"},"GW-BA":{"countryCode":"GW","subdivisionName":"Bafata","code":"GW-BA"},"GW-BS":{"countryCode":"GW","subdivisionName":"Bissau","code":"GW-BS"},"GW-CA":{"countryCode":"GW","subdivisionName":"Cacheu","code":"GW-CA"},"GW-GA":{"countryCode":"GW","subdivisionName":"Gabu","code":"GW-GA"},"GW-OI":{"countryCode":"GW","subdivisionName":"Oio","code":"GW-OI"},"GY-BA":{"countryCode":"GY","subdivisionName":"Barima-Waini","code":"GY-BA"},"GY-CU":{"countryCode":"GY","subdivisionName":"Cuyuni-Mazaruni","code":"GY-CU"},"GY-DE":{"countryCode":"GY","subdivisionName":"Demerara-Mahaica","code":"GY-DE"},"GY-EB":{"countryCode":"GY","subdivisionName":"East Berbice-Corentyne","code":"GY-EB"},"GY-ES":{"countryCode":"GY","subdivisionName":"Essequibo Islands-West Demerara","code":"GY-ES"},"GY-MA":{"countryCode":"GY","subdivisionName":"Mahaica-Berbice","code":"GY-MA"},"GY-PM":{"countryCode":"GY","subdivisionName":"Pomeroon-Supenaam","code":"GY-PM"},"GY-PT":{"countryCode":"GY","subdivisionName":"Potaro-Siparuni","code":"GY-PT"},"GY-UD":{"countryCode":"GY","subdivisionName":"Upper Demerara-Berbice","code":"GY-UD"},"HN-AT":{"countryCode":"HN","subdivisionName":"Atlantida","code":"HN-AT"},"HN-CH":{"countryCode":"HN","subdivisionName":"Choluteca","code":"HN-CH"},"HN-CL":{"countryCode":"HN","subdivisionName":"Colon","code":"HN-CL"},"HN-CM":{"countryCode":"HN","subdivisionName":"Comayagua","code":"HN-CM"},"HN-CP":{"countryCode":"HN","subdivisionName":"Copan","code":"HN-CP"},"HN-CR":{"countryCode":"HN","subdivisionName":"Cortes","code":"HN-CR"},"HN-EP":{"countryCode":"HN","subdivisionName":"El Paraiso","code":"HN-EP"},"HN-FM":{"countryCode":"HN","subdivisionName":"Francisco Morazan","code":"HN-FM"},"HN-GD":{"countryCode":"HN","subdivisionName":"Gracias a Dios","code":"HN-GD"},"HN-IN":{"countryCode":"HN","subdivisionName":"Intibuca","code":"HN-IN"},"HN-IB":{"countryCode":"HN","subdivisionName":"Islas de la Bahia","code":"HN-IB"},"HN-LP":{"countryCode":"HN","subdivisionName":"La Paz","code":"HN-LP"},"HN-LE":{"countryCode":"HN","subdivisionName":"Lempira","code":"HN-LE"},"HN-OC":{"countryCode":"HN","subdivisionName":"Ocotepeque","code":"HN-OC"},"HN-OL":{"countryCode":"HN","subdivisionName":"Olancho","code":"HN-OL"},"HN-SB":{"countryCode":"HN","subdivisionName":"Santa Barbara","code":"HN-SB"},"HN-VA":{"countryCode":"HN","subdivisionName":"Valle","code":"HN-VA"},"HN-YO":{"countryCode":"HN","subdivisionName":"Yoro","code":"HN-YO"},"HR-07":{"countryCode":"HR","subdivisionName":"Bjelovarsko-bilogorska zupanija","code":"HR-07"},"HR-12":{"countryCode":"HR","subdivisionName":"Brodsko-posavska zupanija","code":"HR-12"},"HR-19":{"countryCode":"HR","subdivisionName":"Dubrovacko-neretvanska zupanija","code":"HR-19"},"HR-21":{"countryCode":"HR","subdivisionName":"Grad Zagreb","code":"HR-21"},"HR-18":{"countryCode":"HR","subdivisionName":"Istarska zupanija","code":"HR-18"},"HR-04":{"countryCode":"HR","subdivisionName":"Karlovacka zupanija","code":"HR-04"},"HR-06":{"countryCode":"HR","subdivisionName":"Koprivnicko-krizevacka zupanija","code":"HR-06"},"HR-02":{"countryCode":"HR","subdivisionName":"Krapinsko-zagorska zupanija","code":"HR-02"},"HR-09":{"countryCode":"HR","subdivisionName":"Licko-senjska zupanija","code":"HR-09"},"HR-20":{"countryCode":"HR","subdivisionName":"Medimurska zupanija","code":"HR-20"},"HR-14":{"countryCode":"HR","subdivisionName":"Osjecko-baranjska zupanija","code":"HR-14"},"HR-11":{"countryCode":"HR","subdivisionName":"Pozesko-slavonska zupanija","code":"HR-11"},"HR-08":{"countryCode":"HR","subdivisionName":"Primorsko-goranska zupanija","code":"HR-08"},"HR-15":{"countryCode":"HR","subdivisionName":"Sibensko-kninska zupanija","code":"HR-15"},"HR-03":{"countryCode":"HR","subdivisionName":"Sisacko-moslavacka zupanija","code":"HR-03"},"HR-17":{"countryCode":"HR","subdivisionName":"Splitsko-dalmatinska zupanija","code":"HR-17"},"HR-05":{"countryCode":"HR","subdivisionName":"Varazdinska zupanija","code":"HR-05"},"HR-10":{"countryCode":"HR","subdivisionName":"Viroviticko-podravska zupanija","code":"HR-10"},"HR-16":{"countryCode":"HR","subdivisionName":"Vukovarsko-srijemska zupanija","code":"HR-16"},"HR-13":{"countryCode":"HR","subdivisionName":"Zadarska zupanija","code":"HR-13"},"HR-01":{"countryCode":"HR","subdivisionName":"Zagrebacka zupanija","code":"HR-01"},"HT-AR":{"countryCode":"HT","subdivisionName":"Artibonite","code":"HT-AR"},"HT-CE":{"countryCode":"HT","subdivisionName":"Centre","code":"HT-CE"},"HT-GA":{"countryCode":"HT","subdivisionName":"Grande'Anse","code":"HT-GA"},"HT-NI":{"countryCode":"HT","subdivisionName":"Nippes","code":"HT-NI"},"HT-ND":{"countryCode":"HT","subdivisionName":"Nord","code":"HT-ND"},"HT-NO":{"countryCode":"HT","subdivisionName":"Nord-Ouest","code":"HT-NO"},"HT-OU":{"countryCode":"HT","subdivisionName":"Ouest","code":"HT-OU"},"HT-SD":{"countryCode":"HT","subdivisionName":"Sud","code":"HT-SD"},"HT-SE":{"countryCode":"HT","subdivisionName":"Sud-Est","code":"HT-SE"},"HU-BK":{"countryCode":"HU","subdivisionName":"Bacs-Kiskun","code":"HU-BK"},"HU-BA":{"countryCode":"HU","subdivisionName":"Baranya","code":"HU-BA"},"HU-BE":{"countryCode":"HU","subdivisionName":"Bekes","code":"HU-BE"},"HU-BZ":{"countryCode":"HU","subdivisionName":"Borsod-Abauj-Zemplen","code":"HU-BZ"},"HU-BU":{"countryCode":"HU","subdivisionName":"Budapest","code":"HU-BU"},"HU-CS":{"countryCode":"HU","subdivisionName":"Csongrad-Csanad","code":"HU-CS"},"HU-FE":{"countryCode":"HU","subdivisionName":"Fejer","code":"HU-FE"},"HU-GS":{"countryCode":"HU","subdivisionName":"Gyor-Moson-Sopron","code":"HU-GS"},"HU-HB":{"countryCode":"HU","subdivisionName":"Hajdu-Bihar","code":"HU-HB"},"HU-HE":{"countryCode":"HU","subdivisionName":"Heves","code":"HU-HE"},"HU-JN":{"countryCode":"HU","subdivisionName":"Jasz-Nagykun-Szolnok","code":"HU-JN"},"HU-KE":{"countryCode":"HU","subdivisionName":"Komarom-Esztergom","code":"HU-KE"},"HU-NO":{"countryCode":"HU","subdivisionName":"Nograd","code":"HU-NO"},"HU-PE":{"countryCode":"HU","subdivisionName":"Pest","code":"HU-PE"},"HU-SO":{"countryCode":"HU","subdivisionName":"Somogy","code":"HU-SO"},"HU-SZ":{"countryCode":"HU","subdivisionName":"Szabolcs-Szatmar-Bereg","code":"HU-SZ"},"HU-TO":{"countryCode":"HU","subdivisionName":"Tolna","code":"HU-TO"},"HU-VA":{"countryCode":"HU","subdivisionName":"Vas","code":"HU-VA"},"HU-VE":{"countryCode":"HU","subdivisionName":"Veszprem","code":"HU-VE"},"HU-ZA":{"countryCode":"HU","subdivisionName":"Zala","code":"HU-ZA"},"ID-AC":{"countryCode":"ID","subdivisionName":"Aceh","code":"ID-AC"},"ID-BA":{"countryCode":"ID","subdivisionName":"Bali","code":"ID-BA"},"ID-BT":{"countryCode":"ID","subdivisionName":"Banten","code":"ID-BT"},"ID-BE":{"countryCode":"ID","subdivisionName":"Bengkulu","code":"ID-BE"},"ID-GO":{"countryCode":"ID","subdivisionName":"Gorontalo","code":"ID-GO"},"ID-JK":{"countryCode":"ID","subdivisionName":"Jakarta Raya","code":"ID-JK"},"ID-JA":{"countryCode":"ID","subdivisionName":"Jambi","code":"ID-JA"},"ID-JB":{"countryCode":"ID","subdivisionName":"Jawa Barat","code":"ID-JB"},"ID-JT":{"countryCode":"ID","subdivisionName":"Jawa Tengah","code":"ID-JT"},"ID-JI":{"countryCode":"ID","subdivisionName":"Jawa Timur","code":"ID-JI"},"ID-KB":{"countryCode":"ID","subdivisionName":"Kalimantan Barat","code":"ID-KB"},"ID-KS":{"countryCode":"ID","subdivisionName":"Kalimantan Selatan","code":"ID-KS"},"ID-KT":{"countryCode":"ID","subdivisionName":"Kalimantan Tengah","code":"ID-KT"},"ID-KI":{"countryCode":"ID","subdivisionName":"Kalimantan Timur","code":"ID-KI"},"ID-KU":{"countryCode":"ID","subdivisionName":"Kalimantan Utara","code":"ID-KU"},"ID-BB":{"countryCode":"ID","subdivisionName":"Kepulauan Bangka Belitung","code":"ID-BB"},"ID-KR":{"countryCode":"ID","subdivisionName":"Kepulauan Riau","code":"ID-KR"},"ID-LA":{"countryCode":"ID","subdivisionName":"Lampung","code":"ID-LA"},"ID-ML":{"countryCode":"ID","subdivisionName":"Maluku","code":"ID-ML"},"ID-MU":{"countryCode":"ID","subdivisionName":"Maluku Utara","code":"ID-MU"},"ID-NB":{"countryCode":"ID","subdivisionName":"Nusa Tenggara Barat","code":"ID-NB"},"ID-NT":{"countryCode":"ID","subdivisionName":"Nusa Tenggara Timur","code":"ID-NT"},"ID-PP":{"countryCode":"ID","subdivisionName":"Papua","code":"ID-PP"},"ID-PB":{"countryCode":"ID","subdivisionName":"Papua Barat","code":"ID-PB"},"ID-PE":{"countryCode":"ID","subdivisionName":"Papua Pengunungan","code":"ID-PE"},"ID-PS":{"countryCode":"ID","subdivisionName":"Papua Selatan","code":"ID-PS"},"ID-PT":{"countryCode":"ID","subdivisionName":"Papua Tengah","code":"ID-PT"},"ID-RI":{"countryCode":"ID","subdivisionName":"Riau","code":"ID-RI"},"ID-SR":{"countryCode":"ID","subdivisionName":"Sulawesi Barat","code":"ID-SR"},"ID-SN":{"countryCode":"ID","subdivisionName":"Sulawesi Selatan","code":"ID-SN"},"ID-ST":{"countryCode":"ID","subdivisionName":"Sulawesi Tengah","code":"ID-ST"},"ID-SG":{"countryCode":"ID","subdivisionName":"Sulawesi Tenggara","code":"ID-SG"},"ID-SA":{"countryCode":"ID","subdivisionName":"Sulawesi Utara","code":"ID-SA"},"ID-SB":{"countryCode":"ID","subdivisionName":"Sumatera Barat","code":"ID-SB"},"ID-SS":{"countryCode":"ID","subdivisionName":"Sumatera Selatan","code":"ID-SS"},"ID-SU":{"countryCode":"ID","subdivisionName":"Sumatera Utara","code":"ID-SU"},"ID-YO":{"countryCode":"ID","subdivisionName":"Yogyakarta","code":"ID-YO"},"IE-CW":{"countryCode":"IE","subdivisionName":"Carlow","code":"IE-CW"},"IE-CN":{"countryCode":"IE","subdivisionName":"Cavan","code":"IE-CN"},"IE-CE":{"countryCode":"IE","subdivisionName":"Clare","code":"IE-CE"},"IE-CO":{"countryCode":"IE","subdivisionName":"Cork","code":"IE-CO"},"IE-DL":{"countryCode":"IE","subdivisionName":"Donegal","code":"IE-DL"},"IE-D":{"countryCode":"IE","subdivisionName":"Dublin","code":"IE-D"},"IE-G":{"countryCode":"IE","subdivisionName":"Galway","code":"IE-G"},"IE-KY":{"countryCode":"IE","subdivisionName":"Kerry","code":"IE-KY"},"IE-KE":{"countryCode":"IE","subdivisionName":"Kildare","code":"IE-KE"},"IE-KK":{"countryCode":"IE","subdivisionName":"Kilkenny","code":"IE-KK"},"IE-LS":{"countryCode":"IE","subdivisionName":"Laois","code":"IE-LS"},"IE-LM":{"countryCode":"IE","subdivisionName":"Leitrim","code":"IE-LM"},"IE-LK":{"countryCode":"IE","subdivisionName":"Limerick","code":"IE-LK"},"IE-LD":{"countryCode":"IE","subdivisionName":"Longford","code":"IE-LD"},"IE-LH":{"countryCode":"IE","subdivisionName":"Louth","code":"IE-LH"},"IE-MO":{"countryCode":"IE","subdivisionName":"Mayo","code":"IE-MO"},"IE-MH":{"countryCode":"IE","subdivisionName":"Meath","code":"IE-MH"},"IE-MN":{"countryCode":"IE","subdivisionName":"Monaghan","code":"IE-MN"},"IE-OY":{"countryCode":"IE","subdivisionName":"Offaly","code":"IE-OY"},"IE-RN":{"countryCode":"IE","subdivisionName":"Roscommon","code":"IE-RN"},"IE-SO":{"countryCode":"IE","subdivisionName":"Sligo","code":"IE-SO"},"IE-TA":{"countryCode":"IE","subdivisionName":"Tipperary","code":"IE-TA"},"IE-WD":{"countryCode":"IE","subdivisionName":"Waterford","code":"IE-WD"},"IE-WH":{"countryCode":"IE","subdivisionName":"Westmeath","code":"IE-WH"},"IE-WX":{"countryCode":"IE","subdivisionName":"Wexford","code":"IE-WX"},"IE-WW":{"countryCode":"IE","subdivisionName":"Wicklow","code":"IE-WW"},"IL-D":{"countryCode":"IL","subdivisionName":"HaDarom","code":"IL-D"},"IL-M":{"countryCode":"IL","subdivisionName":"HaMerkaz","code":"IL-M"},"IL-Z":{"countryCode":"IL","subdivisionName":"HaTsafon","code":"IL-Z"},"IL-HA":{"countryCode":"IL","subdivisionName":"Hefa","code":"IL-HA"},"IL-TA":{"countryCode":"IL","subdivisionName":"Tel Aviv","code":"IL-TA"},"IL-JM":{"countryCode":"IL","subdivisionName":"Yerushalayim","code":"IL-JM"},"IN-AN":{"countryCode":"IN","subdivisionName":"Andaman and Nicobar Islands","code":"IN-AN"},"IN-AP":{"countryCode":"IN","subdivisionName":"Andhra Pradesh","code":"IN-AP"},"IN-AR":{"countryCode":"IN","subdivisionName":"Arunachal Pradesh","code":"IN-AR"},"IN-AS":{"countryCode":"IN","subdivisionName":"Assam","code":"IN-AS"},"IN-BR":{"countryCode":"IN","subdivisionName":"Bihar","code":"IN-BR"},"IN-CH":{"countryCode":"IN","subdivisionName":"Chandigarh","code":"IN-CH"},"IN-CT":{"countryCode":"IN","subdivisionName":"Chhattisgarh","code":"IN-CT"},"IN-DN":{"countryCode":"IN","subdivisionName":"Dadra and Nagar Haveli","code":"IN-DN"},"IN-DH":{"countryCode":"IN","subdivisionName":"Dadra and Nagar Haveli and Daman and Diu","code":"IN-DH"},"IN-DL":{"countryCode":"IN","subdivisionName":"Delhi","code":"IN-DL"},"IN-GA":{"countryCode":"IN","subdivisionName":"Goa","code":"IN-GA"},"IN-GJ":{"countryCode":"IN","subdivisionName":"Gujarat","code":"IN-GJ"},"IN-HR":{"countryCode":"IN","subdivisionName":"Haryana","code":"IN-HR"},"IN-HP":{"countryCode":"IN","subdivisionName":"Himachal Pradesh","code":"IN-HP"},"IN-JK":{"countryCode":"IN","subdivisionName":"Jammu and Kashmir","code":"IN-JK"},"IN-JH":{"countryCode":"IN","subdivisionName":"Jharkhand","code":"IN-JH"},"IN-KA":{"countryCode":"IN","subdivisionName":"Karnataka","code":"IN-KA"},"IN-KL":{"countryCode":"IN","subdivisionName":"Kerala","code":"IN-KL"},"IN-LD":{"countryCode":"IN","subdivisionName":"Lakshadweep","code":"IN-LD"},"IN-MP":{"countryCode":"IN","subdivisionName":"Madhya Pradesh","code":"IN-MP"},"IN-MH":{"countryCode":"IN","subdivisionName":"Maharashtra","code":"IN-MH"},"IN-MN":{"countryCode":"IN","subdivisionName":"Manipur","code":"IN-MN"},"IN-ML":{"countryCode":"IN","subdivisionName":"Meghalaya","code":"IN-ML"},"IN-MZ":{"countryCode":"IN","subdivisionName":"Mizoram","code":"IN-MZ"},"IN-NL":{"countryCode":"IN","subdivisionName":"Nagaland","code":"IN-NL"},"IN-OR":{"countryCode":"IN","subdivisionName":"Odisha","code":"IN-OR"},"IN-PY":{"countryCode":"IN","subdivisionName":"Puducherry","code":"IN-PY"},"IN-PB":{"countryCode":"IN","subdivisionName":"Punjab","code":"IN-PB"},"IN-RJ":{"countryCode":"IN","subdivisionName":"Rajasthan","code":"IN-RJ"},"IN-SK":{"countryCode":"IN","subdivisionName":"Sikkim","code":"IN-SK"},"IN-TN":{"countryCode":"IN","subdivisionName":"Tamil Nadu","code":"IN-TN"},"IN-TG":{"countryCode":"IN","subdivisionName":"Telangana","code":"IN-TG"},"IN-TR":{"countryCode":"IN","subdivisionName":"Tripura","code":"IN-TR"},"IN-UP":{"countryCode":"IN","subdivisionName":"Uttar Pradesh","code":"IN-UP"},"IN-UT":{"countryCode":"IN","subdivisionName":"Uttarakhand","code":"IN-UT"},"IN-WB":{"countryCode":"IN","subdivisionName":"West Bengal","code":"IN-WB"},"IQ-AN":{"countryCode":"IQ","subdivisionName":"Al Anbar","code":"IQ-AN"},"IQ-BA":{"countryCode":"IQ","subdivisionName":"Al Basrah","code":"IQ-BA"},"IQ-MU":{"countryCode":"IQ","subdivisionName":"Al Muthanna","code":"IQ-MU"},"IQ-QA":{"countryCode":"IQ","subdivisionName":"Al Qadisiyah","code":"IQ-QA"},"IQ-NA":{"countryCode":"IQ","subdivisionName":"An Najaf","code":"IQ-NA"},"IQ-AR":{"countryCode":"IQ","subdivisionName":"Arbil","code":"IQ-AR"},"IQ-SU":{"countryCode":"IQ","subdivisionName":"As Sulaymaniyah","code":"IQ-SU"},"IQ-BB":{"countryCode":"IQ","subdivisionName":"Babil","code":"IQ-BB"},"IQ-BG":{"countryCode":"IQ","subdivisionName":"Baghdad","code":"IQ-BG"},"IQ-DA":{"countryCode":"IQ","subdivisionName":"Dahuk","code":"IQ-DA"},"IQ-DQ":{"countryCode":"IQ","subdivisionName":"Dhi Qar","code":"IQ-DQ"},"IQ-DI":{"countryCode":"IQ","subdivisionName":"Diyala","code":"IQ-DI"},"IQ-KA":{"countryCode":"IQ","subdivisionName":"Karbala'","code":"IQ-KA"},"IQ-KI":{"countryCode":"IQ","subdivisionName":"Kirkuk","code":"IQ-KI"},"IQ-MA":{"countryCode":"IQ","subdivisionName":"Maysan","code":"IQ-MA"},"IQ-NI":{"countryCode":"IQ","subdivisionName":"Ninawa","code":"IQ-NI"},"IQ-SD":{"countryCode":"IQ","subdivisionName":"Salah ad Din","code":"IQ-SD"},"IQ-WA":{"countryCode":"IQ","subdivisionName":"Wasit","code":"IQ-WA"},"IR-30":{"countryCode":"IR","subdivisionName":"Alborz","code":"IR-30"},"IR-24":{"countryCode":"IR","subdivisionName":"Ardabil","code":"IR-24"},"IR-04":{"countryCode":"IR","subdivisionName":"Azarbayjan-e Gharbi","code":"IR-04"},"IR-03":{"countryCode":"IR","subdivisionName":"Azarbayjan-e Sharqi","code":"IR-03"},"IR-18":{"countryCode":"IR","subdivisionName":"Bushehr","code":"IR-18"},"IR-14":{"countryCode":"IR","subdivisionName":"Chahar Mahal va Bakhtiari","code":"IR-14"},"IR-10":{"countryCode":"IR","subdivisionName":"Esfahan","code":"IR-10"},"IR-07":{"countryCode":"IR","subdivisionName":"Fars","code":"IR-07"},"IR-01":{"countryCode":"IR","subdivisionName":"Gilan","code":"IR-01"},"IR-27":{"countryCode":"IR","subdivisionName":"Golestan","code":"IR-27"},"IR-13":{"countryCode":"IR","subdivisionName":"Hamadan","code":"IR-13"},"IR-22":{"countryCode":"IR","subdivisionName":"Hormozgan","code":"IR-22"},"IR-16":{"countryCode":"IR","subdivisionName":"Ilam","code":"IR-16"},"IR-08":{"countryCode":"IR","subdivisionName":"Kerman","code":"IR-08"},"IR-05":{"countryCode":"IR","subdivisionName":"Kermanshah","code":"IR-05"},"IR-29":{"countryCode":"IR","subdivisionName":"Khorasan-e Jonubi","code":"IR-29"},"IR-09":{"countryCode":"IR","subdivisionName":"Khorasan-e Razavi","code":"IR-09"},"IR-28":{"countryCode":"IR","subdivisionName":"Khorasan-e Shomali","code":"IR-28"},"IR-06":{"countryCode":"IR","subdivisionName":"Khuzestan","code":"IR-06"},"IR-17":{"countryCode":"IR","subdivisionName":"Kohgiluyeh va Bowyer Ahmad","code":"IR-17"},"IR-12":{"countryCode":"IR","subdivisionName":"Kordestan","code":"IR-12"},"IR-15":{"countryCode":"IR","subdivisionName":"Lorestan","code":"IR-15"},"IR-00":{"countryCode":"IR","subdivisionName":"Markazi","code":"IR-00"},"IR-02":{"countryCode":"IR","subdivisionName":"Mazandaran","code":"IR-02"},"IR-26":{"countryCode":"IR","subdivisionName":"Qazvin","code":"IR-26"},"IR-25":{"countryCode":"IR","subdivisionName":"Qom","code":"IR-25"},"IR-20":{"countryCode":"IR","subdivisionName":"Semnan","code":"IR-20"},"IR-11":{"countryCode":"IR","subdivisionName":"Sistan va Baluchestan","code":"IR-11"},"IR-23":{"countryCode":"IR","subdivisionName":"Tehran","code":"IR-23"},"IR-21":{"countryCode":"IR","subdivisionName":"Yazd","code":"IR-21"},"IR-19":{"countryCode":"IR","subdivisionName":"Zanjan","code":"IR-19"},"IS-7":{"countryCode":"IS","subdivisionName":"Austurland","code":"IS-7"},"IS-1":{"countryCode":"IS","subdivisionName":"Hofudborgarsvaedi","code":"IS-1"},"IS-6":{"countryCode":"IS","subdivisionName":"Nordurland eystra","code":"IS-6"},"IS-5":{"countryCode":"IS","subdivisionName":"Nordurland vestra","code":"IS-5"},"IS-8":{"countryCode":"IS","subdivisionName":"Sudurland","code":"IS-8"},"IS-2":{"countryCode":"IS","subdivisionName":"Sudurnes","code":"IS-2"},"IS-4":{"countryCode":"IS","subdivisionName":"Vestfirdir","code":"IS-4"},"IS-3":{"countryCode":"IS","subdivisionName":"Vesturland","code":"IS-3"},"IT-65":{"countryCode":"IT","subdivisionName":"Abruzzo","code":"IT-65"},"IT-77":{"countryCode":"IT","subdivisionName":"Basilicata","code":"IT-77"},"IT-78":{"countryCode":"IT","subdivisionName":"Calabria","code":"IT-78"},"IT-72":{"countryCode":"IT","subdivisionName":"Campania","code":"IT-72"},"IT-45":{"countryCode":"IT","subdivisionName":"Emilia-Romagna","code":"IT-45"},"IT-36":{"countryCode":"IT","subdivisionName":"Friuli-Venezia Giulia","code":"IT-36"},"IT-62":{"countryCode":"IT","subdivisionName":"Lazio","code":"IT-62"},"IT-42":{"countryCode":"IT","subdivisionName":"Liguria","code":"IT-42"},"IT-25":{"countryCode":"IT","subdivisionName":"Lombardia","code":"IT-25"},"IT-57":{"countryCode":"IT","subdivisionName":"Marche","code":"IT-57"},"IT-67":{"countryCode":"IT","subdivisionName":"Molise","code":"IT-67"},"IT-21":{"countryCode":"IT","subdivisionName":"Piemonte","code":"IT-21"},"IT-75":{"countryCode":"IT","subdivisionName":"Puglia","code":"IT-75"},"IT-88":{"countryCode":"IT","subdivisionName":"Sardegna","code":"IT-88"},"IT-82":{"countryCode":"IT","subdivisionName":"Sicilia","code":"IT-82"},"IT-52":{"countryCode":"IT","subdivisionName":"Toscana","code":"IT-52"},"IT-32":{"countryCode":"IT","subdivisionName":"Trentino-Alto Adige","code":"IT-32"},"IT-55":{"countryCode":"IT","subdivisionName":"Umbria","code":"IT-55"},"IT-23":{"countryCode":"IT","subdivisionName":"Valle d'Aosta","code":"IT-23"},"IT-34":{"countryCode":"IT","subdivisionName":"Veneto","code":"IT-34"},"JM-13":{"countryCode":"JM","subdivisionName":"Clarendon","code":"JM-13"},"JM-09":{"countryCode":"JM","subdivisionName":"Hanover","code":"JM-09"},"JM-01":{"countryCode":"JM","subdivisionName":"Kingston","code":"JM-01"},"JM-12":{"countryCode":"JM","subdivisionName":"Manchester","code":"JM-12"},"JM-04":{"countryCode":"JM","subdivisionName":"Portland","code":"JM-04"},"JM-02":{"countryCode":"JM","subdivisionName":"Saint Andrew","code":"JM-02"},"JM-06":{"countryCode":"JM","subdivisionName":"Saint Ann","code":"JM-06"},"JM-14":{"countryCode":"JM","subdivisionName":"Saint Catherine","code":"JM-14"},"JM-11":{"countryCode":"JM","subdivisionName":"Saint Elizabeth","code":"JM-11"},"JM-08":{"countryCode":"JM","subdivisionName":"Saint James","code":"JM-08"},"JM-05":{"countryCode":"JM","subdivisionName":"Saint Mary","code":"JM-05"},"JM-03":{"countryCode":"JM","subdivisionName":"Saint Thomas","code":"JM-03"},"JM-07":{"countryCode":"JM","subdivisionName":"Trelawny","code":"JM-07"},"JM-10":{"countryCode":"JM","subdivisionName":"Westmoreland","code":"JM-10"},"JO-AJ":{"countryCode":"JO","subdivisionName":"'Ajlun","code":"JO-AJ"},"JO-AQ":{"countryCode":"JO","subdivisionName":"Al 'Aqabah","code":"JO-AQ"},"JO-AM":{"countryCode":"JO","subdivisionName":"Al 'Asimah","code":"JO-AM"},"JO-BA":{"countryCode":"JO","subdivisionName":"Al Balqa'","code":"JO-BA"},"JO-KA":{"countryCode":"JO","subdivisionName":"Al Karak","code":"JO-KA"},"JO-MA":{"countryCode":"JO","subdivisionName":"Al Mafraq","code":"JO-MA"},"JO-AT":{"countryCode":"JO","subdivisionName":"At Tafilah","code":"JO-AT"},"JO-AZ":{"countryCode":"JO","subdivisionName":"Az Zarqa'","code":"JO-AZ"},"JO-IR":{"countryCode":"JO","subdivisionName":"Irbid","code":"JO-IR"},"JO-JA":{"countryCode":"JO","subdivisionName":"Jarash","code":"JO-JA"},"JO-MN":{"countryCode":"JO","subdivisionName":"Ma'an","code":"JO-MN"},"JO-MD":{"countryCode":"JO","subdivisionName":"Madaba","code":"JO-MD"},"JP-23":{"countryCode":"JP","subdivisionName":"Aichi","code":"JP-23"},"JP-05":{"countryCode":"JP","subdivisionName":"Akita","code":"JP-05"},"JP-02":{"countryCode":"JP","subdivisionName":"Aomori","code":"JP-02"},"JP-12":{"countryCode":"JP","subdivisionName":"Chiba","code":"JP-12"},"JP-38":{"countryCode":"JP","subdivisionName":"Ehime","code":"JP-38"},"JP-18":{"countryCode":"JP","subdivisionName":"Fukui","code":"JP-18"},"JP-40":{"countryCode":"JP","subdivisionName":"Fukuoka","code":"JP-40"},"JP-07":{"countryCode":"JP","subdivisionName":"Fukushima","code":"JP-07"},"JP-21":{"countryCode":"JP","subdivisionName":"Gifu","code":"JP-21"},"JP-10":{"countryCode":"JP","subdivisionName":"Gunma","code":"JP-10"},"JP-34":{"countryCode":"JP","subdivisionName":"Hiroshima","code":"JP-34"},"JP-01":{"countryCode":"JP","subdivisionName":"Hokkaido","code":"JP-01"},"JP-28":{"countryCode":"JP","subdivisionName":"Hyogo","code":"JP-28"},"JP-08":{"countryCode":"JP","subdivisionName":"Ibaraki","code":"JP-08"},"JP-17":{"countryCode":"JP","subdivisionName":"Ishikawa","code":"JP-17"},"JP-03":{"countryCode":"JP","subdivisionName":"Iwate","code":"JP-03"},"JP-37":{"countryCode":"JP","subdivisionName":"Kagawa","code":"JP-37"},"JP-46":{"countryCode":"JP","subdivisionName":"Kagoshima","code":"JP-46"},"JP-14":{"countryCode":"JP","subdivisionName":"Kanagawa","code":"JP-14"},"JP-39":{"countryCode":"JP","subdivisionName":"Kochi","code":"JP-39"},"JP-43":{"countryCode":"JP","subdivisionName":"Kumamoto","code":"JP-43"},"JP-26":{"countryCode":"JP","subdivisionName":"Kyoto","code":"JP-26"},"JP-24":{"countryCode":"JP","subdivisionName":"Mie","code":"JP-24"},"JP-04":{"countryCode":"JP","subdivisionName":"Miyagi","code":"JP-04"},"JP-45":{"countryCode":"JP","subdivisionName":"Miyazaki","code":"JP-45"},"JP-20":{"countryCode":"JP","subdivisionName":"Nagano","code":"JP-20"},"JP-42":{"countryCode":"JP","subdivisionName":"Nagasaki","code":"JP-42"},"JP-29":{"countryCode":"JP","subdivisionName":"Nara","code":"JP-29"},"JP-15":{"countryCode":"JP","subdivisionName":"Niigata","code":"JP-15"},"JP-44":{"countryCode":"JP","subdivisionName":"Oita","code":"JP-44"},"JP-33":{"countryCode":"JP","subdivisionName":"Okayama","code":"JP-33"},"JP-47":{"countryCode":"JP","subdivisionName":"Okinawa","code":"JP-47"},"JP-27":{"countryCode":"JP","subdivisionName":"Osaka","code":"JP-27"},"JP-41":{"countryCode":"JP","subdivisionName":"Saga","code":"JP-41"},"JP-11":{"countryCode":"JP","subdivisionName":"Saitama","code":"JP-11"},"JP-25":{"countryCode":"JP","subdivisionName":"Shiga","code":"JP-25"},"JP-32":{"countryCode":"JP","subdivisionName":"Shimane","code":"JP-32"},"JP-22":{"countryCode":"JP","subdivisionName":"Shizuoka","code":"JP-22"},"JP-09":{"countryCode":"JP","subdivisionName":"Tochigi","code":"JP-09"},"JP-36":{"countryCode":"JP","subdivisionName":"Tokushima","code":"JP-36"},"JP-13":{"countryCode":"JP","subdivisionName":"Tokyo","code":"JP-13"},"JP-31":{"countryCode":"JP","subdivisionName":"Tottori","code":"JP-31"},"JP-16":{"countryCode":"JP","subdivisionName":"Toyama","code":"JP-16"},"JP-30":{"countryCode":"JP","subdivisionName":"Wakayama","code":"JP-30"},"JP-06":{"countryCode":"JP","subdivisionName":"Yamagata","code":"JP-06"},"JP-35":{"countryCode":"JP","subdivisionName":"Yamaguchi","code":"JP-35"},"JP-19":{"countryCode":"JP","subdivisionName":"Yamanashi","code":"JP-19"},"KE-01":{"countryCode":"KE","subdivisionName":"Baringo","code":"KE-01"},"KE-02":{"countryCode":"KE","subdivisionName":"Bomet","code":"KE-02"},"KE-03":{"countryCode":"KE","subdivisionName":"Bungoma","code":"KE-03"},"KE-04":{"countryCode":"KE","subdivisionName":"Busia","code":"KE-04"},"KE-05":{"countryCode":"KE","subdivisionName":"Elgeyo/Marakwet","code":"KE-05"},"KE-06":{"countryCode":"KE","subdivisionName":"Embu","code":"KE-06"},"KE-07":{"countryCode":"KE","subdivisionName":"Garissa","code":"KE-07"},"KE-08":{"countryCode":"KE","subdivisionName":"Homa Bay","code":"KE-08"},"KE-09":{"countryCode":"KE","subdivisionName":"Isiolo","code":"KE-09"},"KE-10":{"countryCode":"KE","subdivisionName":"Kajiado","code":"KE-10"},"KE-11":{"countryCode":"KE","subdivisionName":"Kakamega","code":"KE-11"},"KE-12":{"countryCode":"KE","subdivisionName":"Kericho","code":"KE-12"},"KE-13":{"countryCode":"KE","subdivisionName":"Kiambu","code":"KE-13"},"KE-14":{"countryCode":"KE","subdivisionName":"Kilifi","code":"KE-14"},"KE-15":{"countryCode":"KE","subdivisionName":"Kirinyaga","code":"KE-15"},"KE-16":{"countryCode":"KE","subdivisionName":"Kisii","code":"KE-16"},"KE-17":{"countryCode":"KE","subdivisionName":"Kisumu","code":"KE-17"},"KE-18":{"countryCode":"KE","subdivisionName":"Kitui","code":"KE-18"},"KE-19":{"countryCode":"KE","subdivisionName":"Kwale","code":"KE-19"},"KE-20":{"countryCode":"KE","subdivisionName":"Laikipia","code":"KE-20"},"KE-21":{"countryCode":"KE","subdivisionName":"Lamu","code":"KE-21"},"KE-22":{"countryCode":"KE","subdivisionName":"Machakos","code":"KE-22"},"KE-23":{"countryCode":"KE","subdivisionName":"Makueni","code":"KE-23"},"KE-24":{"countryCode":"KE","subdivisionName":"Mandera","code":"KE-24"},"KE-25":{"countryCode":"KE","subdivisionName":"Marsabit","code":"KE-25"},"KE-26":{"countryCode":"KE","subdivisionName":"Meru","code":"KE-26"},"KE-27":{"countryCode":"KE","subdivisionName":"Migori","code":"KE-27"},"KE-28":{"countryCode":"KE","subdivisionName":"Mombasa","code":"KE-28"},"KE-29":{"countryCode":"KE","subdivisionName":"Murang'a","code":"KE-29"},"KE-30":{"countryCode":"KE","subdivisionName":"Nairobi City","code":"KE-30"},"KE-31":{"countryCode":"KE","subdivisionName":"Nakuru","code":"KE-31"},"KE-32":{"countryCode":"KE","subdivisionName":"Nandi","code":"KE-32"},"KE-33":{"countryCode":"KE","subdivisionName":"Narok","code":"KE-33"},"KE-34":{"countryCode":"KE","subdivisionName":"Nyamira","code":"KE-34"},"KE-35":{"countryCode":"KE","subdivisionName":"Nyandarua","code":"KE-35"},"KE-36":{"countryCode":"KE","subdivisionName":"Nyeri","code":"KE-36"},"KE-37":{"countryCode":"KE","subdivisionName":"Samburu","code":"KE-37"},"KE-38":{"countryCode":"KE","subdivisionName":"Siaya","code":"KE-38"},"KE-39":{"countryCode":"KE","subdivisionName":"Taita/Taveta","code":"KE-39"},"KE-40":{"countryCode":"KE","subdivisionName":"Tana River","code":"KE-40"},"KE-41":{"countryCode":"KE","subdivisionName":"Tharaka-Nithi","code":"KE-41"},"KE-42":{"countryCode":"KE","subdivisionName":"Trans Nzoia","code":"KE-42"},"KE-43":{"countryCode":"KE","subdivisionName":"Turkana","code":"KE-43"},"KE-44":{"countryCode":"KE","subdivisionName":"Uasin Gishu","code":"KE-44"},"KE-45":{"countryCode":"KE","subdivisionName":"Vihiga","code":"KE-45"},"KE-46":{"countryCode":"KE","subdivisionName":"Wajir","code":"KE-46"},"KE-47":{"countryCode":"KE","subdivisionName":"West Pokot","code":"KE-47"},"KG-B":{"countryCode":"KG","subdivisionName":"Batken","code":"KG-B"},"KG-GB":{"countryCode":"KG","subdivisionName":"Bishkek Shaary","code":"KG-GB"},"KG-C":{"countryCode":"KG","subdivisionName":"Chuy","code":"KG-C"},"KG-J":{"countryCode":"KG","subdivisionName":"Jalal-Abad","code":"KG-J"},"KG-N":{"countryCode":"KG","subdivisionName":"Naryn","code":"KG-N"},"KG-GO":{"countryCode":"KG","subdivisionName":"Osh Shaary","code":"KG-GO"},"KG-T":{"countryCode":"KG","subdivisionName":"Talas","code":"KG-T"},"KG-Y":{"countryCode":"KG","subdivisionName":"Ysyk-Kol","code":"KG-Y"},"KH-2":{"countryCode":"KH","subdivisionName":"Baat Dambang","code":"KH-2"},"KH-1":{"countryCode":"KH","subdivisionName":"Banteay Mean Choay","code":"KH-1"},"KH-23":{"countryCode":"KH","subdivisionName":"Kaeb","code":"KH-23"},"KH-3":{"countryCode":"KH","subdivisionName":"Kampong Chaam","code":"KH-3"},"KH-4":{"countryCode":"KH","subdivisionName":"Kampong Chhnang","code":"KH-4"},"KH-5":{"countryCode":"KH","subdivisionName":"Kampong Spueu","code":"KH-5"},"KH-6":{"countryCode":"KH","subdivisionName":"Kampong Thum","code":"KH-6"},"KH-7":{"countryCode":"KH","subdivisionName":"Kampot","code":"KH-7"},"KH-8":{"countryCode":"KH","subdivisionName":"Kandaal","code":"KH-8"},"KH-10":{"countryCode":"KH","subdivisionName":"Kracheh","code":"KH-10"},"KH-11":{"countryCode":"KH","subdivisionName":"Mondol Kiri","code":"KH-11"},"KH-24":{"countryCode":"KH","subdivisionName":"Pailin","code":"KH-24"},"KH-12":{"countryCode":"KH","subdivisionName":"Phnom Penh","code":"KH-12"},"KH-15":{"countryCode":"KH","subdivisionName":"Pousaat","code":"KH-15"},"KH-18":{"countryCode":"KH","subdivisionName":"Preah Sihanouk","code":"KH-18"},"KH-13":{"countryCode":"KH","subdivisionName":"Preah Vihear","code":"KH-13"},"KH-14":{"countryCode":"KH","subdivisionName":"Prey Veaeng","code":"KH-14"},"KH-17":{"countryCode":"KH","subdivisionName":"Siem Reab","code":"KH-17"},"KH-19":{"countryCode":"KH","subdivisionName":"Stueng Traeng","code":"KH-19"},"KH-20":{"countryCode":"KH","subdivisionName":"Svaay Rieng","code":"KH-20"},"KH-21":{"countryCode":"KH","subdivisionName":"Taakaev","code":"KH-21"},"KI-G":{"countryCode":"KI","subdivisionName":"Gilbert Islands","code":"KI-G"},"KI-L":{"countryCode":"KI","subdivisionName":"Line Islands","code":"KI-L"},"KM-G":{"countryCode":"KM","subdivisionName":"Grande Comore","code":"KM-G"},"KM-M":{"countryCode":"KM","subdivisionName":"Moheli","code":"KM-M"},"KN-01":{"countryCode":"KN","subdivisionName":"Christ Church Nichola Town","code":"KN-01"},"KN-02":{"countryCode":"KN","subdivisionName":"Saint Anne Sandy Point","code":"KN-02"},"KN-03":{"countryCode":"KN","subdivisionName":"Saint George Basseterre","code":"KN-03"},"KN-05":{"countryCode":"KN","subdivisionName":"Saint James Windward","code":"KN-05"},"KN-06":{"countryCode":"KN","subdivisionName":"Saint John Capisterre","code":"KN-06"},"KN-07":{"countryCode":"KN","subdivisionName":"Saint John Figtree","code":"KN-07"},"KN-08":{"countryCode":"KN","subdivisionName":"Saint Mary Cayon","code":"KN-08"},"KN-09":{"countryCode":"KN","subdivisionName":"Saint Paul Capisterre","code":"KN-09"},"KN-10":{"countryCode":"KN","subdivisionName":"Saint Paul Charlestown","code":"KN-10"},"KN-11":{"countryCode":"KN","subdivisionName":"Saint Peter Basseterre","code":"KN-11"},"KN-12":{"countryCode":"KN","subdivisionName":"Saint Thomas Lowland","code":"KN-12"},"KN-13":{"countryCode":"KN","subdivisionName":"Saint Thomas Middle Island","code":"KN-13"},"KN-15":{"countryCode":"KN","subdivisionName":"Trinity Palmetto Point","code":"KN-15"},"KP-01":{"countryCode":"KP","subdivisionName":"P'yongyang","code":"KP-01"},"KR-26":{"countryCode":"KR","subdivisionName":"Busan-gwangyeoksi","code":"KR-26"},"KR-43":{"countryCode":"KR","subdivisionName":"Chungcheongbuk-do","code":"KR-43"},"KR-44":{"countryCode":"KR","subdivisionName":"Chungcheongnam-do","code":"KR-44"},"KR-27":{"countryCode":"KR","subdivisionName":"Daegu-gwangyeoksi","code":"KR-27"},"KR-30":{"countryCode":"KR","subdivisionName":"Daejeon-gwangyeoksi","code":"KR-30"},"KR-42":{"countryCode":"KR","subdivisionName":"Gangwon-do","code":"KR-42"},"KR-29":{"countryCode":"KR","subdivisionName":"Gwangju-gwangyeoksi","code":"KR-29"},"KR-41":{"countryCode":"KR","subdivisionName":"Gyeonggi-do","code":"KR-41"},"KR-47":{"countryCode":"KR","subdivisionName":"Gyeongsangbuk-do","code":"KR-47"},"KR-48":{"countryCode":"KR","subdivisionName":"Gyeongsangnam-do","code":"KR-48"},"KR-28":{"countryCode":"KR","subdivisionName":"Incheon-gwangyeoksi","code":"KR-28"},"KR-49":{"countryCode":"KR","subdivisionName":"Jeju-teukbyeoljachido","code":"KR-49"},"KR-45":{"countryCode":"KR","subdivisionName":"Jeollabuk-do","code":"KR-45"},"KR-46":{"countryCode":"KR","subdivisionName":"Jeollanam-do","code":"KR-46"},"KR-11":{"countryCode":"KR","subdivisionName":"Seoul-teukbyeolsi","code":"KR-11"},"KR-31":{"countryCode":"KR","subdivisionName":"Ulsan-gwangyeoksi","code":"KR-31"},"KW-KU":{"countryCode":"KW","subdivisionName":"Al 'Asimah","code":"KW-KU"},"KW-AH":{"countryCode":"KW","subdivisionName":"Al Ahmadi","code":"KW-AH"},"KW-FA":{"countryCode":"KW","subdivisionName":"Al Farwaniyah","code":"KW-FA"},"KW-JA":{"countryCode":"KW","subdivisionName":"Al Jahra'","code":"KW-JA"},"KW-HA":{"countryCode":"KW","subdivisionName":"Hawalli","code":"KW-HA"},"KW-MU":{"countryCode":"KW","subdivisionName":"Mubarak al Kabir","code":"KW-MU"},"KZ-10":{"countryCode":"KZ","subdivisionName":"Abay oblysy","code":"KZ-10"},"KZ-75":{"countryCode":"KZ","subdivisionName":"Almaty","code":"KZ-75"},"KZ-19":{"countryCode":"KZ","subdivisionName":"Almaty oblysy","code":"KZ-19"},"KZ-11":{"countryCode":"KZ","subdivisionName":"Aqmola oblysy","code":"KZ-11"},"KZ-15":{"countryCode":"KZ","subdivisionName":"Aqtobe oblysy","code":"KZ-15"},"KZ-71":{"countryCode":"KZ","subdivisionName":"Astana","code":"KZ-71"},"KZ-23":{"countryCode":"KZ","subdivisionName":"Atyrau oblysy","code":"KZ-23"},"KZ-27":{"countryCode":"KZ","subdivisionName":"Batys Qazaqstan oblysy","code":"KZ-27"},"KZ-47":{"countryCode":"KZ","subdivisionName":"Mangghystau oblysy","code":"KZ-47"},"KZ-55":{"countryCode":"KZ","subdivisionName":"Pavlodar oblysy","code":"KZ-55"},"KZ-35":{"countryCode":"KZ","subdivisionName":"Qaraghandy oblysy","code":"KZ-35"},"KZ-39":{"countryCode":"KZ","subdivisionName":"Qostanay oblysy","code":"KZ-39"},"KZ-43":{"countryCode":"KZ","subdivisionName":"Qyzylorda oblysy","code":"KZ-43"},"KZ-63":{"countryCode":"KZ","subdivisionName":"Shyghys Qazaqstan oblysy","code":"KZ-63"},"KZ-79":{"countryCode":"KZ","subdivisionName":"Shymkent","code":"KZ-79"},"KZ-59":{"countryCode":"KZ","subdivisionName":"Soltustik Qazaqstan oblysy","code":"KZ-59"},"KZ-61":{"countryCode":"KZ","subdivisionName":"Turkistan oblysy","code":"KZ-61"},"KZ-62":{"countryCode":"KZ","subdivisionName":"Ulytau oblysy","code":"KZ-62"},"KZ-31":{"countryCode":"KZ","subdivisionName":"Zhambyl oblysy","code":"KZ-31"},"KZ-33":{"countryCode":"KZ","subdivisionName":"Zhetisu oblysy","code":"KZ-33"},"LA-AT":{"countryCode":"LA","subdivisionName":"Attapu","code":"LA-AT"},"LA-BK":{"countryCode":"LA","subdivisionName":"Bokeo","code":"LA-BK"},"LA-BL":{"countryCode":"LA","subdivisionName":"Bolikhamxai","code":"LA-BL"},"LA-CH":{"countryCode":"LA","subdivisionName":"Champasak","code":"LA-CH"},"LA-KH":{"countryCode":"LA","subdivisionName":"Khammouan","code":"LA-KH"},"LA-LP":{"countryCode":"LA","subdivisionName":"Louangphabang","code":"LA-LP"},"LA-OU":{"countryCode":"LA","subdivisionName":"Oudomxai","code":"LA-OU"},"LA-PH":{"countryCode":"LA","subdivisionName":"Phongsali","code":"LA-PH"},"LA-SV":{"countryCode":"LA","subdivisionName":"Savannakhet","code":"LA-SV"},"LA-VI":{"countryCode":"LA","subdivisionName":"Viangchan","code":"LA-VI"},"LA-XA":{"countryCode":"LA","subdivisionName":"Xaignabouli","code":"LA-XA"},"LA-XE":{"countryCode":"LA","subdivisionName":"Xekong","code":"LA-XE"},"LA-XI":{"countryCode":"LA","subdivisionName":"Xiangkhouang","code":"LA-XI"},"LB-AK":{"countryCode":"LB","subdivisionName":"Aakkar","code":"LB-AK"},"LB-BH":{"countryCode":"LB","subdivisionName":"Baalbek-Hermel","code":"LB-BH"},"LB-BI":{"countryCode":"LB","subdivisionName":"Beqaa","code":"LB-BI"},"LB-BA":{"countryCode":"LB","subdivisionName":"Beyrouth","code":"LB-BA"},"LB-AS":{"countryCode":"LB","subdivisionName":"Liban-Nord","code":"LB-AS"},"LB-JA":{"countryCode":"LB","subdivisionName":"Liban-Sud","code":"LB-JA"},"LB-JL":{"countryCode":"LB","subdivisionName":"Mont-Liban","code":"LB-JL"},"LB-NA":{"countryCode":"LB","subdivisionName":"Nabatiye","code":"LB-NA"},"LC-01":{"countryCode":"LC","subdivisionName":"Anse la Raye","code":"LC-01"},"LC-02":{"countryCode":"LC","subdivisionName":"Castries","code":"LC-02"},"LC-03":{"countryCode":"LC","subdivisionName":"Choiseul","code":"LC-03"},"LC-05":{"countryCode":"LC","subdivisionName":"Dennery","code":"LC-05"},"LC-06":{"countryCode":"LC","subdivisionName":"Gros Islet","code":"LC-06"},"LC-07":{"countryCode":"LC","subdivisionName":"Laborie","code":"LC-07"},"LC-08":{"countryCode":"LC","subdivisionName":"Micoud","code":"LC-08"},"LC-10":{"countryCode":"LC","subdivisionName":"Soufriere","code":"LC-10"},"LC-11":{"countryCode":"LC","subdivisionName":"Vieux Fort","code":"LC-11"},"LI-01":{"countryCode":"LI","subdivisionName":"Balzers","code":"LI-01"},"LI-02":{"countryCode":"LI","subdivisionName":"Eschen","code":"LI-02"},"LI-03":{"countryCode":"LI","subdivisionName":"Gamprin","code":"LI-03"},"LI-04":{"countryCode":"LI","subdivisionName":"Mauren","code":"LI-04"},"LI-06":{"countryCode":"LI","subdivisionName":"Ruggell","code":"LI-06"},"LI-07":{"countryCode":"LI","subdivisionName":"Schaan","code":"LI-07"},"LI-09":{"countryCode":"LI","subdivisionName":"Triesen","code":"LI-09"},"LI-10":{"countryCode":"LI","subdivisionName":"Triesenberg","code":"LI-10"},"LI-11":{"countryCode":"LI","subdivisionName":"Vaduz","code":"LI-11"},"LK-2":{"countryCode":"LK","subdivisionName":"Central Province","code":"LK-2"},"LK-5":{"countryCode":"LK","subdivisionName":"Eastern Province","code":"LK-5"},"LK-7":{"countryCode":"LK","subdivisionName":"North Central Province","code":"LK-7"},"LK-6":{"countryCode":"LK","subdivisionName":"North Western Province","code":"LK-6"},"LK-4":{"countryCode":"LK","subdivisionName":"Northern Province","code":"LK-4"},"LK-9":{"countryCode":"LK","subdivisionName":"Sabaragamuwa Province","code":"LK-9"},"LK-3":{"countryCode":"LK","subdivisionName":"Southern Province","code":"LK-3"},"LK-8":{"countryCode":"LK","subdivisionName":"Uva Province","code":"LK-8"},"LK-1":{"countryCode":"LK","subdivisionName":"Western Province","code":"LK-1"},"LR-BM":{"countryCode":"LR","subdivisionName":"Bomi","code":"LR-BM"},"LR-BG":{"countryCode":"LR","subdivisionName":"Bong","code":"LR-BG"},"LR-CM":{"countryCode":"LR","subdivisionName":"Grand Cape Mount","code":"LR-CM"},"LR-GG":{"countryCode":"LR","subdivisionName":"Grand Gedeh","code":"LR-GG"},"LR-MG":{"countryCode":"LR","subdivisionName":"Margibi","code":"LR-MG"},"LR-MO":{"countryCode":"LR","subdivisionName":"Montserrado","code":"LR-MO"},"LR-RI":{"countryCode":"LR","subdivisionName":"River Cess","code":"LR-RI"},"LR-SI":{"countryCode":"LR","subdivisionName":"Sinoe","code":"LR-SI"},"LS-D":{"countryCode":"LS","subdivisionName":"Berea","code":"LS-D"},"LS-B":{"countryCode":"LS","subdivisionName":"Botha-Bothe","code":"LS-B"},"LS-C":{"countryCode":"LS","subdivisionName":"Leribe","code":"LS-C"},"LS-E":{"countryCode":"LS","subdivisionName":"Mafeteng","code":"LS-E"},"LS-A":{"countryCode":"LS","subdivisionName":"Maseru","code":"LS-A"},"LS-F":{"countryCode":"LS","subdivisionName":"Mohale's Hoek","code":"LS-F"},"LS-J":{"countryCode":"LS","subdivisionName":"Mokhotlong","code":"LS-J"},"LS-H":{"countryCode":"LS","subdivisionName":"Qacha's Nek","code":"LS-H"},"LS-G":{"countryCode":"LS","subdivisionName":"Quthing","code":"LS-G"},"LS-K":{"countryCode":"LS","subdivisionName":"Thaba-Tseka","code":"LS-K"},"LT-AL":{"countryCode":"LT","subdivisionName":"Alytaus apskritis","code":"LT-AL"},"LT-KU":{"countryCode":"LT","subdivisionName":"Kauno apskritis","code":"LT-KU"},"LT-KL":{"countryCode":"LT","subdivisionName":"Klaipedos apskritis","code":"LT-KL"},"LT-MR":{"countryCode":"LT","subdivisionName":"Marijampoles apskritis","code":"LT-MR"},"LT-PN":{"countryCode":"LT","subdivisionName":"Panevezio apskritis","code":"LT-PN"},"LT-SA":{"countryCode":"LT","subdivisionName":"Siauliu apskritis","code":"LT-SA"},"LT-TA":{"countryCode":"LT","subdivisionName":"Taurages apskritis","code":"LT-TA"},"LT-TE":{"countryCode":"LT","subdivisionName":"Telsiu apskritis","code":"LT-TE"},"LT-UT":{"countryCode":"LT","subdivisionName":"Utenos apskritis","code":"LT-UT"},"LT-VL":{"countryCode":"LT","subdivisionName":"Vilniaus apskritis","code":"LT-VL"},"LU-CA":{"countryCode":"LU","subdivisionName":"Capellen","code":"LU-CA"},"LU-CL":{"countryCode":"LU","subdivisionName":"Clervaux","code":"LU-CL"},"LU-DI":{"countryCode":"LU","subdivisionName":"Diekirch","code":"LU-DI"},"LU-EC":{"countryCode":"LU","subdivisionName":"Echternach","code":"LU-EC"},"LU-ES":{"countryCode":"LU","subdivisionName":"Esch-sur-Alzette","code":"LU-ES"},"LU-GR":{"countryCode":"LU","subdivisionName":"Grevenmacher","code":"LU-GR"},"LU-LU":{"countryCode":"LU","subdivisionName":"Luxembourg","code":"LU-LU"},"LU-ME":{"countryCode":"LU","subdivisionName":"Mersch","code":"LU-ME"},"LU-RD":{"countryCode":"LU","subdivisionName":"Redange","code":"LU-RD"},"LU-RM":{"countryCode":"LU","subdivisionName":"Remich","code":"LU-RM"},"LU-VD":{"countryCode":"LU","subdivisionName":"Vianden","code":"LU-VD"},"LU-WI":{"countryCode":"LU","subdivisionName":"Wiltz","code":"LU-WI"},"LV-011":{"countryCode":"LV","subdivisionName":"Adazu novads","code":"LV-011"},"LV-002":{"countryCode":"LV","subdivisionName":"Aizkraukles novads","code":"LV-002"},"LV-007":{"countryCode":"LV","subdivisionName":"Aluksnes novads","code":"LV-007"},"LV-111":{"countryCode":"LV","subdivisionName":"Augsdaugavas novads","code":"LV-111"},"LV-015":{"countryCode":"LV","subdivisionName":"Balvu novads","code":"LV-015"},"LV-016":{"countryCode":"LV","subdivisionName":"Bauskas novads","code":"LV-016"},"LV-022":{"countryCode":"LV","subdivisionName":"Cesu novads","code":"LV-022"},"LV-DGV":{"countryCode":"LV","subdivisionName":"Daugavpils","code":"LV-DGV"},"LV-112":{"countryCode":"LV","subdivisionName":"Dienvidkurzemes novads","code":"LV-112"},"LV-026":{"countryCode":"LV","subdivisionName":"Dobeles novads","code":"LV-026"},"LV-033":{"countryCode":"LV","subdivisionName":"Gulbenes novads","code":"LV-033"},"LV-042":{"countryCode":"LV","subdivisionName":"Jekabpils novads","code":"LV-042"},"LV-JEL":{"countryCode":"LV","subdivisionName":"Jelgava","code":"LV-JEL"},"LV-041":{"countryCode":"LV","subdivisionName":"Jelgavas novads","code":"LV-041"},"LV-JUR":{"countryCode":"LV","subdivisionName":"Jurmala","code":"LV-JUR"},"LV-052":{"countryCode":"LV","subdivisionName":"Kekavas novads","code":"LV-052"},"LV-047":{"countryCode":"LV","subdivisionName":"Kraslavas novads","code":"LV-047"},"LV-050":{"countryCode":"LV","subdivisionName":"Kuldigas novads","code":"LV-050"},"LV-LPX":{"countryCode":"LV","subdivisionName":"Liepaja","code":"LV-LPX"},"LV-054":{"countryCode":"LV","subdivisionName":"Limbazu novads","code":"LV-054"},"LV-056":{"countryCode":"LV","subdivisionName":"Livanu novads","code":"LV-056"},"LV-058":{"countryCode":"LV","subdivisionName":"Ludzas novads","code":"LV-058"},"LV-059":{"countryCode":"LV","subdivisionName":"Madonas novads","code":"LV-059"},"LV-062":{"countryCode":"LV","subdivisionName":"Marupes novads","code":"LV-062"},"LV-067":{"countryCode":"LV","subdivisionName":"Ogres novads","code":"LV-067"},"LV-068":{"countryCode":"LV","subdivisionName":"Olaines novads","code":"LV-068"},"LV-073":{"countryCode":"LV","subdivisionName":"Preilu novads","code":"LV-073"},"LV-077":{"countryCode":"LV","subdivisionName":"Rezeknes novads","code":"LV-077"},"LV-RIX":{"countryCode":"LV","subdivisionName":"Riga","code":"LV-RIX"},"LV-080":{"countryCode":"LV","subdivisionName":"Ropazu novads","code":"LV-080"},"LV-087":{"countryCode":"LV","subdivisionName":"Salaspils novads","code":"LV-087"},"LV-088":{"countryCode":"LV","subdivisionName":"Saldus novads","code":"LV-088"},"LV-089":{"countryCode":"LV","subdivisionName":"Saulkrastu novads","code":"LV-089"},"LV-091":{"countryCode":"LV","subdivisionName":"Siguldas novads","code":"LV-091"},"LV-094":{"countryCode":"LV","subdivisionName":"Smiltenes novads","code":"LV-094"},"LV-097":{"countryCode":"LV","subdivisionName":"Talsu novads","code":"LV-097"},"LV-099":{"countryCode":"LV","subdivisionName":"Tukuma novads","code":"LV-099"},"LV-101":{"countryCode":"LV","subdivisionName":"Valkas novads","code":"LV-101"},"LV-113":{"countryCode":"LV","subdivisionName":"Valmieras novads","code":"LV-113"},"LV-102":{"countryCode":"LV","subdivisionName":"Varaklanu novads","code":"LV-102"},"LV-106":{"countryCode":"LV","subdivisionName":"Ventspils novads","code":"LV-106"},"LY-BU":{"countryCode":"LY","subdivisionName":"Al Butnan","code":"LY-BU"},"LY-JA":{"countryCode":"LY","subdivisionName":"Al Jabal al Akhdar","code":"LY-JA"},"LY-JG":{"countryCode":"LY","subdivisionName":"Al Jabal al Gharbi","code":"LY-JG"},"LY-JI":{"countryCode":"LY","subdivisionName":"Al Jafarah","code":"LY-JI"},"LY-JU":{"countryCode":"LY","subdivisionName":"Al Jufrah","code":"LY-JU"},"LY-KF":{"countryCode":"LY","subdivisionName":"Al Kufrah","code":"LY-KF"},"LY-MJ":{"countryCode":"LY","subdivisionName":"Al Marj","code":"LY-MJ"},"LY-MB":{"countryCode":"LY","subdivisionName":"Al Marqab","code":"LY-MB"},"LY-WA":{"countryCode":"LY","subdivisionName":"Al Wahat","code":"LY-WA"},"LY-NQ":{"countryCode":"LY","subdivisionName":"An Nuqat al Khams","code":"LY-NQ"},"LY-ZA":{"countryCode":"LY","subdivisionName":"Az Zawiyah","code":"LY-ZA"},"LY-BA":{"countryCode":"LY","subdivisionName":"Banghazi","code":"LY-BA"},"LY-DR":{"countryCode":"LY","subdivisionName":"Darnah","code":"LY-DR"},"LY-MI":{"countryCode":"LY","subdivisionName":"Misratah","code":"LY-MI"},"LY-MQ":{"countryCode":"LY","subdivisionName":"Murzuq","code":"LY-MQ"},"LY-NL":{"countryCode":"LY","subdivisionName":"Nalut","code":"LY-NL"},"LY-SB":{"countryCode":"LY","subdivisionName":"Sabha","code":"LY-SB"},"LY-SR":{"countryCode":"LY","subdivisionName":"Surt","code":"LY-SR"},"LY-TB":{"countryCode":"LY","subdivisionName":"Tarabulus","code":"LY-TB"},"LY-WS":{"countryCode":"LY","subdivisionName":"Wadi ash Shati'","code":"LY-WS"},"MA-05":{"countryCode":"MA","subdivisionName":"Beni-Mellal-Khenifra","code":"MA-05"},"MA-06":{"countryCode":"MA","subdivisionName":"Casablanca-Settat","code":"MA-06"},"MA-08":{"countryCode":"MA","subdivisionName":"Draa-Tafilalet","code":"MA-08"},"MA-03":{"countryCode":"MA","subdivisionName":"Fes- Meknes","code":"MA-03"},"MA-10":{"countryCode":"MA","subdivisionName":"Guelmim-Oued Noun (EH-partial)","code":"MA-10"},"MA-02":{"countryCode":"MA","subdivisionName":"L'Oriental","code":"MA-02"},"MA-11":{"countryCode":"MA","subdivisionName":"Laayoune-Sakia El Hamra (EH-partial)","code":"MA-11"},"MA-07":{"countryCode":"MA","subdivisionName":"Marrakech-Safi","code":"MA-07"},"MA-04":{"countryCode":"MA","subdivisionName":"Rabat-Sale-Kenitra","code":"MA-04"},"MA-09":{"countryCode":"MA","subdivisionName":"Souss-Massa","code":"MA-09"},"MA-01":{"countryCode":"MA","subdivisionName":"Tanger-Tetouan-Al Hoceima","code":"MA-01"},"MC-FO":{"countryCode":"MC","subdivisionName":"Fontvieille","code":"MC-FO"},"MC-CO":{"countryCode":"MC","subdivisionName":"La Condamine","code":"MC-CO"},"MC-MO":{"countryCode":"MC","subdivisionName":"Monaco-Ville","code":"MC-MO"},"MC-MG":{"countryCode":"MC","subdivisionName":"Moneghetti","code":"MC-MG"},"MC-MC":{"countryCode":"MC","subdivisionName":"Monte-Carlo","code":"MC-MC"},"MC-SR":{"countryCode":"MC","subdivisionName":"Saint-Roman","code":"MC-SR"},"MD-AN":{"countryCode":"MD","subdivisionName":"Anenii Noi","code":"MD-AN"},"MD-BA":{"countryCode":"MD","subdivisionName":"Balti","code":"MD-BA"},"MD-BS":{"countryCode":"MD","subdivisionName":"Basarabeasca","code":"MD-BS"},"MD-BD":{"countryCode":"MD","subdivisionName":"Bender","code":"MD-BD"},"MD-BR":{"countryCode":"MD","subdivisionName":"Briceni","code":"MD-BR"},"MD-CA":{"countryCode":"MD","subdivisionName":"Cahul","code":"MD-CA"},"MD-CL":{"countryCode":"MD","subdivisionName":"Calarasi","code":"MD-CL"},"MD-CT":{"countryCode":"MD","subdivisionName":"Cantemir","code":"MD-CT"},"MD-CS":{"countryCode":"MD","subdivisionName":"Causeni","code":"MD-CS"},"MD-CU":{"countryCode":"MD","subdivisionName":"Chisinau","code":"MD-CU"},"MD-CM":{"countryCode":"MD","subdivisionName":"Cimislia","code":"MD-CM"},"MD-CR":{"countryCode":"MD","subdivisionName":"Criuleni","code":"MD-CR"},"MD-DO":{"countryCode":"MD","subdivisionName":"Donduseni","code":"MD-DO"},"MD-DR":{"countryCode":"MD","subdivisionName":"Drochia","code":"MD-DR"},"MD-DU":{"countryCode":"MD","subdivisionName":"Dubasari","code":"MD-DU"},"MD-ED":{"countryCode":"MD","subdivisionName":"Edinet","code":"MD-ED"},"MD-FA":{"countryCode":"MD","subdivisionName":"Falesti","code":"MD-FA"},"MD-FL":{"countryCode":"MD","subdivisionName":"Floresti","code":"MD-FL"},"MD-GA":{"countryCode":"MD","subdivisionName":"Gagauzia, Unitatea teritoriala autonoma","code":"MD-GA"},"MD-GL":{"countryCode":"MD","subdivisionName":"Glodeni","code":"MD-GL"},"MD-HI":{"countryCode":"MD","subdivisionName":"Hincesti","code":"MD-HI"},"MD-IA":{"countryCode":"MD","subdivisionName":"Ialoveni","code":"MD-IA"},"MD-LE":{"countryCode":"MD","subdivisionName":"Leova","code":"MD-LE"},"MD-NI":{"countryCode":"MD","subdivisionName":"Nisporeni","code":"MD-NI"},"MD-OC":{"countryCode":"MD","subdivisionName":"Ocnita","code":"MD-OC"},"MD-OR":{"countryCode":"MD","subdivisionName":"Orhei","code":"MD-OR"},"MD-RE":{"countryCode":"MD","subdivisionName":"Rezina","code":"MD-RE"},"MD-RI":{"countryCode":"MD","subdivisionName":"Riscani","code":"MD-RI"},"MD-SI":{"countryCode":"MD","subdivisionName":"Singerei","code":"MD-SI"},"MD-SD":{"countryCode":"MD","subdivisionName":"Soldanesti","code":"MD-SD"},"MD-SO":{"countryCode":"MD","subdivisionName":"Soroca","code":"MD-SO"},"MD-SV":{"countryCode":"MD","subdivisionName":"Stefan Voda","code":"MD-SV"},"MD-SN":{"countryCode":"MD","subdivisionName":"Stinga Nistrului, unitatea teritoriala din","code":"MD-SN"},"MD-ST":{"countryCode":"MD","subdivisionName":"Straseni","code":"MD-ST"},"MD-TA":{"countryCode":"MD","subdivisionName":"Taraclia","code":"MD-TA"},"MD-TE":{"countryCode":"MD","subdivisionName":"Telenesti","code":"MD-TE"},"MD-UN":{"countryCode":"MD","subdivisionName":"Ungheni","code":"MD-UN"},"ME-01":{"countryCode":"ME","subdivisionName":"Andrijevica","code":"ME-01"},"ME-02":{"countryCode":"ME","subdivisionName":"Bar","code":"ME-02"},"ME-03":{"countryCode":"ME","subdivisionName":"Berane","code":"ME-03"},"ME-04":{"countryCode":"ME","subdivisionName":"Bijelo Polje","code":"ME-04"},"ME-05":{"countryCode":"ME","subdivisionName":"Budva","code":"ME-05"},"ME-06":{"countryCode":"ME","subdivisionName":"Cetinje","code":"ME-06"},"ME-07":{"countryCode":"ME","subdivisionName":"Danilovgrad","code":"ME-07"},"ME-08":{"countryCode":"ME","subdivisionName":"Herceg-Novi","code":"ME-08"},"ME-09":{"countryCode":"ME","subdivisionName":"Kolasin","code":"ME-09"},"ME-10":{"countryCode":"ME","subdivisionName":"Kotor","code":"ME-10"},"ME-12":{"countryCode":"ME","subdivisionName":"Niksic","code":"ME-12"},"ME-13":{"countryCode":"ME","subdivisionName":"Plav","code":"ME-13"},"ME-14":{"countryCode":"ME","subdivisionName":"Pljevlja","code":"ME-14"},"ME-15":{"countryCode":"ME","subdivisionName":"Pluzine","code":"ME-15"},"ME-16":{"countryCode":"ME","subdivisionName":"Podgorica","code":"ME-16"},"ME-17":{"countryCode":"ME","subdivisionName":"Rozaje","code":"ME-17"},"ME-19":{"countryCode":"ME","subdivisionName":"Tivat","code":"ME-19"},"ME-24":{"countryCode":"ME","subdivisionName":"Tuzi","code":"ME-24"},"ME-20":{"countryCode":"ME","subdivisionName":"Ulcinj","code":"ME-20"},"ME-21":{"countryCode":"ME","subdivisionName":"Zabljak","code":"ME-21"},"MG-T":{"countryCode":"MG","subdivisionName":"Antananarivo","code":"MG-T"},"MG-D":{"countryCode":"MG","subdivisionName":"Antsiranana","code":"MG-D"},"MG-F":{"countryCode":"MG","subdivisionName":"Fianarantsoa","code":"MG-F"},"MG-M":{"countryCode":"MG","subdivisionName":"Mahajanga","code":"MG-M"},"MG-A":{"countryCode":"MG","subdivisionName":"Toamasina","code":"MG-A"},"MG-U":{"countryCode":"MG","subdivisionName":"Toliara","code":"MG-U"},"MH-KWA":{"countryCode":"MH","subdivisionName":"Kwajalein","code":"MH-KWA"},"MH-MAJ":{"countryCode":"MH","subdivisionName":"Majuro","code":"MH-MAJ"},"MK-201":{"countryCode":"MK","subdivisionName":"Berovo","code":"MK-201"},"MK-501":{"countryCode":"MK","subdivisionName":"Bitola","code":"MK-501"},"MK-401":{"countryCode":"MK","subdivisionName":"Bogdanci","code":"MK-401"},"MK-601":{"countryCode":"MK","subdivisionName":"Bogovinje","code":"MK-601"},"MK-402":{"countryCode":"MK","subdivisionName":"Bosilovo","code":"MK-402"},"MK-602":{"countryCode":"MK","subdivisionName":"Brvenica","code":"MK-602"},"MK-803":{"countryCode":"MK","subdivisionName":"Butel","code":"MK-803"},"MK-109":{"countryCode":"MK","subdivisionName":"Caska","code":"MK-109"},"MK-814":{"countryCode":"MK","subdivisionName":"Centar","code":"MK-814"},"MK-210":{"countryCode":"MK","subdivisionName":"Cesinovo-Oblesevo","code":"MK-210"},"MK-816":{"countryCode":"MK","subdivisionName":"Cucer Sandevo","code":"MK-816"},"MK-303":{"countryCode":"MK","subdivisionName":"Debar","code":"MK-303"},"MK-502":{"countryCode":"MK","subdivisionName":"Demir Hisar","code":"MK-502"},"MK-103":{"countryCode":"MK","subdivisionName":"Demir Kapija","code":"MK-103"},"MK-406":{"countryCode":"MK","subdivisionName":"Dojran","code":"MK-406"},"MK-503":{"countryCode":"MK","subdivisionName":"Dolneni","code":"MK-503"},"MK-804":{"countryCode":"MK","subdivisionName":"Gazi Baba","code":"MK-804"},"MK-405":{"countryCode":"MK","subdivisionName":"Gevgelija","code":"MK-405"},"MK-604":{"countryCode":"MK","subdivisionName":"Gostivar","code":"MK-604"},"MK-102":{"countryCode":"MK","subdivisionName":"Gradsko","code":"MK-102"},"MK-807":{"countryCode":"MK","subdivisionName":"Ilinden","code":"MK-807"},"MK-606":{"countryCode":"MK","subdivisionName":"Jegunovce","code":"MK-606"},"MK-205":{"countryCode":"MK","subdivisionName":"Karbinci","code":"MK-205"},"MK-104":{"countryCode":"MK","subdivisionName":"Kavadarci","code":"MK-104"},"MK-307":{"countryCode":"MK","subdivisionName":"Kicevo","code":"MK-307"},"MK-809":{"countryCode":"MK","subdivisionName":"Kisela Voda","code":"MK-809"},"MK-206":{"countryCode":"MK","subdivisionName":"Kocani","code":"MK-206"},"MK-701":{"countryCode":"MK","subdivisionName":"Kratovo","code":"MK-701"},"MK-702":{"countryCode":"MK","subdivisionName":"Kriva Palanka","code":"MK-702"},"MK-504":{"countryCode":"MK","subdivisionName":"Krivogastani","code":"MK-504"},"MK-505":{"countryCode":"MK","subdivisionName":"Krusevo","code":"MK-505"},"MK-703":{"countryCode":"MK","subdivisionName":"Kumanovo","code":"MK-703"},"MK-704":{"countryCode":"MK","subdivisionName":"Lipkovo","code":"MK-704"},"MK-105":{"countryCode":"MK","subdivisionName":"Lozovo","code":"MK-105"},"MK-207":{"countryCode":"MK","subdivisionName":"Makedonska Kamenica","code":"MK-207"},"MK-308":{"countryCode":"MK","subdivisionName":"Makedonski Brod","code":"MK-308"},"MK-607":{"countryCode":"MK","subdivisionName":"Mavrovo i Rostusa","code":"MK-607"},"MK-506":{"countryCode":"MK","subdivisionName":"Mogila","code":"MK-506"},"MK-106":{"countryCode":"MK","subdivisionName":"Negotino","code":"MK-106"},"MK-507":{"countryCode":"MK","subdivisionName":"Novaci","code":"MK-507"},"MK-408":{"countryCode":"MK","subdivisionName":"Novo Selo","code":"MK-408"},"MK-310":{"countryCode":"MK","subdivisionName":"Ohrid","code":"MK-310"},"MK-208":{"countryCode":"MK","subdivisionName":"Pehcevo","code":"MK-208"},"MK-810":{"countryCode":"MK","subdivisionName":"Petrovec","code":"MK-810"},"MK-311":{"countryCode":"MK","subdivisionName":"Plasnica","code":"MK-311"},"MK-508":{"countryCode":"MK","subdivisionName":"Prilep","code":"MK-508"},"MK-209":{"countryCode":"MK","subdivisionName":"Probistip","code":"MK-209"},"MK-409":{"countryCode":"MK","subdivisionName":"Radovis","code":"MK-409"},"MK-705":{"countryCode":"MK","subdivisionName":"Rankovce","code":"MK-705"},"MK-509":{"countryCode":"MK","subdivisionName":"Resen","code":"MK-509"},"MK-107":{"countryCode":"MK","subdivisionName":"Rosoman","code":"MK-107"},"MK-811":{"countryCode":"MK","subdivisionName":"Saraj","code":"MK-811"},"MK-812":{"countryCode":"MK","subdivisionName":"Sopiste","code":"MK-812"},"MK-211":{"countryCode":"MK","subdivisionName":"Stip","code":"MK-211"},"MK-312":{"countryCode":"MK","subdivisionName":"Struga","code":"MK-312"},"MK-410":{"countryCode":"MK","subdivisionName":"Strumica","code":"MK-410"},"MK-813":{"countryCode":"MK","subdivisionName":"Studenicani","code":"MK-813"},"MK-108":{"countryCode":"MK","subdivisionName":"Sveti Nikole","code":"MK-108"},"MK-608":{"countryCode":"MK","subdivisionName":"Tearce","code":"MK-608"},"MK-609":{"countryCode":"MK","subdivisionName":"Tetovo","code":"MK-609"},"MK-403":{"countryCode":"MK","subdivisionName":"Valandovo","code":"MK-403"},"MK-404":{"countryCode":"MK","subdivisionName":"Vasilevo","code":"MK-404"},"MK-101":{"countryCode":"MK","subdivisionName":"Veles","code":"MK-101"},"MK-301":{"countryCode":"MK","subdivisionName":"Vevcani","code":"MK-301"},"MK-202":{"countryCode":"MK","subdivisionName":"Vinica","code":"MK-202"},"MK-806":{"countryCode":"MK","subdivisionName":"Zelenikovo","code":"MK-806"},"MK-605":{"countryCode":"MK","subdivisionName":"Zelino","code":"MK-605"},"ML-BKO":{"countryCode":"ML","subdivisionName":"Bamako","code":"ML-BKO"},"ML-7":{"countryCode":"ML","subdivisionName":"Gao","code":"ML-7"},"ML-1":{"countryCode":"ML","subdivisionName":"Kayes","code":"ML-1"},"ML-8":{"countryCode":"ML","subdivisionName":"Kidal","code":"ML-8"},"ML-2":{"countryCode":"ML","subdivisionName":"Koulikoro","code":"ML-2"},"ML-5":{"countryCode":"ML","subdivisionName":"Mopti","code":"ML-5"},"ML-4":{"countryCode":"ML","subdivisionName":"Segou","code":"ML-4"},"ML-3":{"countryCode":"ML","subdivisionName":"Sikasso","code":"ML-3"},"ML-6":{"countryCode":"ML","subdivisionName":"Tombouctou","code":"ML-6"},"MM-07":{"countryCode":"MM","subdivisionName":"Ayeyarwady","code":"MM-07"},"MM-02":{"countryCode":"MM","subdivisionName":"Bago","code":"MM-02"},"MM-14":{"countryCode":"MM","subdivisionName":"Chin","code":"MM-14"},"MM-11":{"countryCode":"MM","subdivisionName":"Kachin","code":"MM-11"},"MM-12":{"countryCode":"MM","subdivisionName":"Kayah","code":"MM-12"},"MM-13":{"countryCode":"MM","subdivisionName":"Kayin","code":"MM-13"},"MM-03":{"countryCode":"MM","subdivisionName":"Magway","code":"MM-03"},"MM-04":{"countryCode":"MM","subdivisionName":"Mandalay","code":"MM-04"},"MM-15":{"countryCode":"MM","subdivisionName":"Mon","code":"MM-15"},"MM-18":{"countryCode":"MM","subdivisionName":"Nay Pyi Taw","code":"MM-18"},"MM-16":{"countryCode":"MM","subdivisionName":"Rakhine","code":"MM-16"},"MM-01":{"countryCode":"MM","subdivisionName":"Sagaing","code":"MM-01"},"MM-17":{"countryCode":"MM","subdivisionName":"Shan","code":"MM-17"},"MM-05":{"countryCode":"MM","subdivisionName":"Tanintharyi","code":"MM-05"},"MM-06":{"countryCode":"MM","subdivisionName":"Yangon","code":"MM-06"},"MN-069":{"countryCode":"MN","subdivisionName":"Bayanhongor","code":"MN-069"},"MN-037":{"countryCode":"MN","subdivisionName":"Darhan uul","code":"MN-037"},"MN-061":{"countryCode":"MN","subdivisionName":"Dornod","code":"MN-061"},"MN-065":{"countryCode":"MN","subdivisionName":"Govi-Altay","code":"MN-065"},"MN-039":{"countryCode":"MN","subdivisionName":"Hentiy","code":"MN-039"},"MN-043":{"countryCode":"MN","subdivisionName":"Hovd","code":"MN-043"},"MN-053":{"countryCode":"MN","subdivisionName":"Omnogovi","code":"MN-053"},"MN-055":{"countryCode":"MN","subdivisionName":"Ovorhangay","code":"MN-055"},"MN-049":{"countryCode":"MN","subdivisionName":"Selenge","code":"MN-049"},"MN-047":{"countryCode":"MN","subdivisionName":"Tov","code":"MN-047"},"MN-1":{"countryCode":"MN","subdivisionName":"Ulaanbaatar","code":"MN-1"},"MR-07":{"countryCode":"MR","subdivisionName":"Adrar","code":"MR-07"},"MR-03":{"countryCode":"MR","subdivisionName":"Assaba","code":"MR-03"},"MR-05":{"countryCode":"MR","subdivisionName":"Brakna","code":"MR-05"},"MR-08":{"countryCode":"MR","subdivisionName":"Dakhlet Nouadhibou","code":"MR-08"},"MR-04":{"countryCode":"MR","subdivisionName":"Gorgol","code":"MR-04"},"MR-02":{"countryCode":"MR","subdivisionName":"Hodh el Gharbi","code":"MR-02"},"MR-12":{"countryCode":"MR","subdivisionName":"Inchiri","code":"MR-12"},"MR-13":{"countryCode":"MR","subdivisionName":"Nouakchott Ouest","code":"MR-13"},"MR-09":{"countryCode":"MR","subdivisionName":"Tagant","code":"MR-09"},"MR-11":{"countryCode":"MR","subdivisionName":"Tiris Zemmour","code":"MR-11"},"MR-06":{"countryCode":"MR","subdivisionName":"Trarza","code":"MR-06"},"MT-01":{"countryCode":"MT","subdivisionName":"Attard","code":"MT-01"},"MT-02":{"countryCode":"MT","subdivisionName":"Balzan","code":"MT-02"},"MT-03":{"countryCode":"MT","subdivisionName":"Birgu","code":"MT-03"},"MT-04":{"countryCode":"MT","subdivisionName":"Birkirkara","code":"MT-04"},"MT-05":{"countryCode":"MT","subdivisionName":"Birzebbuga","code":"MT-05"},"MT-06":{"countryCode":"MT","subdivisionName":"Bormla","code":"MT-06"},"MT-07":{"countryCode":"MT","subdivisionName":"Dingli","code":"MT-07"},"MT-08":{"countryCode":"MT","subdivisionName":"Fgura","code":"MT-08"},"MT-09":{"countryCode":"MT","subdivisionName":"Floriana","code":"MT-09"},"MT-10":{"countryCode":"MT","subdivisionName":"Fontana","code":"MT-10"},"MT-13":{"countryCode":"MT","subdivisionName":"Ghajnsielem","code":"MT-13"},"MT-14":{"countryCode":"MT","subdivisionName":"Gharb","code":"MT-14"},"MT-15":{"countryCode":"MT","subdivisionName":"Gharghur","code":"MT-15"},"MT-16":{"countryCode":"MT","subdivisionName":"Ghasri","code":"MT-16"},"MT-17":{"countryCode":"MT","subdivisionName":"Ghaxaq","code":"MT-17"},"MT-11":{"countryCode":"MT","subdivisionName":"Gudja","code":"MT-11"},"MT-12":{"countryCode":"MT","subdivisionName":"Gzira","code":"MT-12"},"MT-18":{"countryCode":"MT","subdivisionName":"Hamrun","code":"MT-18"},"MT-19":{"countryCode":"MT","subdivisionName":"Iklin","code":"MT-19"},"MT-20":{"countryCode":"MT","subdivisionName":"Isla","code":"MT-20"},"MT-21":{"countryCode":"MT","subdivisionName":"Kalkara","code":"MT-21"},"MT-23":{"countryCode":"MT","subdivisionName":"Kirkop","code":"MT-23"},"MT-24":{"countryCode":"MT","subdivisionName":"Lija","code":"MT-24"},"MT-25":{"countryCode":"MT","subdivisionName":"Luqa","code":"MT-25"},"MT-26":{"countryCode":"MT","subdivisionName":"Marsa","code":"MT-26"},"MT-27":{"countryCode":"MT","subdivisionName":"Marsaskala","code":"MT-27"},"MT-28":{"countryCode":"MT","subdivisionName":"Marsaxlokk","code":"MT-28"},"MT-29":{"countryCode":"MT","subdivisionName":"Mdina","code":"MT-29"},"MT-30":{"countryCode":"MT","subdivisionName":"Mellieha","code":"MT-30"},"MT-31":{"countryCode":"MT","subdivisionName":"Mgarr","code":"MT-31"},"MT-32":{"countryCode":"MT","subdivisionName":"Mosta","code":"MT-32"},"MT-33":{"countryCode":"MT","subdivisionName":"Mqabba","code":"MT-33"},"MT-34":{"countryCode":"MT","subdivisionName":"Msida","code":"MT-34"},"MT-35":{"countryCode":"MT","subdivisionName":"Mtarfa","code":"MT-35"},"MT-36":{"countryCode":"MT","subdivisionName":"Munxar","code":"MT-36"},"MT-37":{"countryCode":"MT","subdivisionName":"Nadur","code":"MT-37"},"MT-38":{"countryCode":"MT","subdivisionName":"Naxxar","code":"MT-38"},"MT-39":{"countryCode":"MT","subdivisionName":"Paola","code":"MT-39"},"MT-40":{"countryCode":"MT","subdivisionName":"Pembroke","code":"MT-40"},"MT-41":{"countryCode":"MT","subdivisionName":"Pieta","code":"MT-41"},"MT-42":{"countryCode":"MT","subdivisionName":"Qala","code":"MT-42"},"MT-43":{"countryCode":"MT","subdivisionName":"Qormi","code":"MT-43"},"MT-44":{"countryCode":"MT","subdivisionName":"Qrendi","code":"MT-44"},"MT-45":{"countryCode":"MT","subdivisionName":"Rabat Gozo","code":"MT-45"},"MT-46":{"countryCode":"MT","subdivisionName":"Rabat Malta","code":"MT-46"},"MT-47":{"countryCode":"MT","subdivisionName":"Safi","code":"MT-47"},"MT-49":{"countryCode":"MT","subdivisionName":"Saint John","code":"MT-49"},"MT-48":{"countryCode":"MT","subdivisionName":"Saint Julian's","code":"MT-48"},"MT-50":{"countryCode":"MT","subdivisionName":"Saint Lawrence","code":"MT-50"},"MT-53":{"countryCode":"MT","subdivisionName":"Saint Lucia's","code":"MT-53"},"MT-51":{"countryCode":"MT","subdivisionName":"Saint Paul's Bay","code":"MT-51"},"MT-52":{"countryCode":"MT","subdivisionName":"Sannat","code":"MT-52"},"MT-54":{"countryCode":"MT","subdivisionName":"Santa Venera","code":"MT-54"},"MT-55":{"countryCode":"MT","subdivisionName":"Siggiewi","code":"MT-55"},"MT-56":{"countryCode":"MT","subdivisionName":"Sliema","code":"MT-56"},"MT-57":{"countryCode":"MT","subdivisionName":"Swieqi","code":"MT-57"},"MT-58":{"countryCode":"MT","subdivisionName":"Ta' Xbiex","code":"MT-58"},"MT-59":{"countryCode":"MT","subdivisionName":"Tarxien","code":"MT-59"},"MT-60":{"countryCode":"MT","subdivisionName":"Valletta","code":"MT-60"},"MT-61":{"countryCode":"MT","subdivisionName":"Xaghra","code":"MT-61"},"MT-62":{"countryCode":"MT","subdivisionName":"Xewkija","code":"MT-62"},"MT-63":{"countryCode":"MT","subdivisionName":"Xghajra","code":"MT-63"},"MT-64":{"countryCode":"MT","subdivisionName":"Zabbar","code":"MT-64"},"MT-65":{"countryCode":"MT","subdivisionName":"Zebbug Gozo","code":"MT-65"},"MT-67":{"countryCode":"MT","subdivisionName":"Zejtun","code":"MT-67"},"MT-68":{"countryCode":"MT","subdivisionName":"Zurrieq","code":"MT-68"},"MU-BL":{"countryCode":"MU","subdivisionName":"Black River","code":"MU-BL"},"MU-FL":{"countryCode":"MU","subdivisionName":"Flacq","code":"MU-FL"},"MU-GP":{"countryCode":"MU","subdivisionName":"Grand Port","code":"MU-GP"},"MU-MO":{"countryCode":"MU","subdivisionName":"Moka","code":"MU-MO"},"MU-PA":{"countryCode":"MU","subdivisionName":"Pamplemousses","code":"MU-PA"},"MU-PW":{"countryCode":"MU","subdivisionName":"Plaines Wilhems","code":"MU-PW"},"MU-PL":{"countryCode":"MU","subdivisionName":"Port Louis","code":"MU-PL"},"MU-RR":{"countryCode":"MU","subdivisionName":"Riviere du Rempart","code":"MU-RR"},"MU-RO":{"countryCode":"MU","subdivisionName":"Rodrigues Islands","code":"MU-RO"},"MU-SA":{"countryCode":"MU","subdivisionName":"Savanne","code":"MU-SA"},"MV-01":{"countryCode":"MV","subdivisionName":"Addu City","code":"MV-01"},"MV-03":{"countryCode":"MV","subdivisionName":"Faadhippolhu","code":"MV-03"},"MV-04":{"countryCode":"MV","subdivisionName":"Felidhu Atoll","code":"MV-04"},"MV-05":{"countryCode":"MV","subdivisionName":"Hahdhunmathi","code":"MV-05"},"MV-MLE":{"countryCode":"MV","subdivisionName":"Male","code":"MV-MLE"},"MV-12":{"countryCode":"MV","subdivisionName":"Mulaku Atoll","code":"MV-12"},"MV-02":{"countryCode":"MV","subdivisionName":"North Ari Atoll","code":"MV-02"},"MV-27":{"countryCode":"MV","subdivisionName":"North Huvadhu Atoll","code":"MV-27"},"MV-13":{"countryCode":"MV","subdivisionName":"North Maalhosmadulu","code":"MV-13"},"MV-07":{"countryCode":"MV","subdivisionName":"North Thiladhunmathi","code":"MV-07"},"MV-00":{"countryCode":"MV","subdivisionName":"South Ari Atoll","code":"MV-00"},"MV-28":{"countryCode":"MV","subdivisionName":"South Huvadhu Atoll","code":"MV-28"},"MV-20":{"countryCode":"MV","subdivisionName":"South Maalhosmadulu","code":"MV-20"},"MV-25":{"countryCode":"MV","subdivisionName":"South Miladhunmadulu","code":"MV-25"},"MV-23":{"countryCode":"MV","subdivisionName":"South Thiladhunmathi","code":"MV-23"},"MW-BA":{"countryCode":"MW","subdivisionName":"Balaka","code":"MW-BA"},"MW-BL":{"countryCode":"MW","subdivisionName":"Blantyre","code":"MW-BL"},"MW-CK":{"countryCode":"MW","subdivisionName":"Chikwawa","code":"MW-CK"},"MW-CR":{"countryCode":"MW","subdivisionName":"Chiradzulu","code":"MW-CR"},"MW-DE":{"countryCode":"MW","subdivisionName":"Dedza","code":"MW-DE"},"MW-DO":{"countryCode":"MW","subdivisionName":"Dowa","code":"MW-DO"},"MW-KR":{"countryCode":"MW","subdivisionName":"Karonga","code":"MW-KR"},"MW-LI":{"countryCode":"MW","subdivisionName":"Lilongwe","code":"MW-LI"},"MW-MH":{"countryCode":"MW","subdivisionName":"Machinga","code":"MW-MH"},"MW-MG":{"countryCode":"MW","subdivisionName":"Mangochi","code":"MW-MG"},"MW-MW":{"countryCode":"MW","subdivisionName":"Mwanza","code":"MW-MW"},"MW-MZ":{"countryCode":"MW","subdivisionName":"Mzimba","code":"MW-MZ"},"MW-NE":{"countryCode":"MW","subdivisionName":"Neno","code":"MW-NE"},"MW-NK":{"countryCode":"MW","subdivisionName":"Nkhotakota","code":"MW-NK"},"MW-NI":{"countryCode":"MW","subdivisionName":"Ntchisi","code":"MW-NI"},"MW-SA":{"countryCode":"MW","subdivisionName":"Salima","code":"MW-SA"},"MW-TH":{"countryCode":"MW","subdivisionName":"Thyolo","code":"MW-TH"},"MW-ZO":{"countryCode":"MW","subdivisionName":"Zomba","code":"MW-ZO"},"MX-AGU":{"countryCode":"MX","subdivisionName":"Aguascalientes","code":"MX-AGU"},"MX-BCN":{"countryCode":"MX","subdivisionName":"Baja California","code":"MX-BCN"},"MX-BCS":{"countryCode":"MX","subdivisionName":"Baja California Sur","code":"MX-BCS"},"MX-CAM":{"countryCode":"MX","subdivisionName":"Campeche","code":"MX-CAM"},"MX-CHP":{"countryCode":"MX","subdivisionName":"Chiapas","code":"MX-CHP"},"MX-CHH":{"countryCode":"MX","subdivisionName":"Chihuahua","code":"MX-CHH"},"MX-CMX":{"countryCode":"MX","subdivisionName":"Ciudad de Mexico","code":"MX-CMX"},"MX-COA":{"countryCode":"MX","subdivisionName":"Coahuila de Zaragoza","code":"MX-COA"},"MX-COL":{"countryCode":"MX","subdivisionName":"Colima","code":"MX-COL"},"MX-DUR":{"countryCode":"MX","subdivisionName":"Durango","code":"MX-DUR"},"MX-GUA":{"countryCode":"MX","subdivisionName":"Guanajuato","code":"MX-GUA"},"MX-GRO":{"countryCode":"MX","subdivisionName":"Guerrero","code":"MX-GRO"},"MX-HID":{"countryCode":"MX","subdivisionName":"Hidalgo","code":"MX-HID"},"MX-JAL":{"countryCode":"MX","subdivisionName":"Jalisco","code":"MX-JAL"},"MX-MEX":{"countryCode":"MX","subdivisionName":"Mexico","code":"MX-MEX"},"MX-MIC":{"countryCode":"MX","subdivisionName":"Michoacan de Ocampo","code":"MX-MIC"},"MX-MOR":{"countryCode":"MX","subdivisionName":"Morelos","code":"MX-MOR"},"MX-NAY":{"countryCode":"MX","subdivisionName":"Nayarit","code":"MX-NAY"},"MX-NLE":{"countryCode":"MX","subdivisionName":"Nuevo Leon","code":"MX-NLE"},"MX-OAX":{"countryCode":"MX","subdivisionName":"Oaxaca","code":"MX-OAX"},"MX-PUE":{"countryCode":"MX","subdivisionName":"Puebla","code":"MX-PUE"},"MX-QUE":{"countryCode":"MX","subdivisionName":"Queretaro","code":"MX-QUE"},"MX-ROO":{"countryCode":"MX","subdivisionName":"Quintana Roo","code":"MX-ROO"},"MX-SLP":{"countryCode":"MX","subdivisionName":"San Luis Potosi","code":"MX-SLP"},"MX-SIN":{"countryCode":"MX","subdivisionName":"Sinaloa","code":"MX-SIN"},"MX-SON":{"countryCode":"MX","subdivisionName":"Sonora","code":"MX-SON"},"MX-TAB":{"countryCode":"MX","subdivisionName":"Tabasco","code":"MX-TAB"},"MX-TAM":{"countryCode":"MX","subdivisionName":"Tamaulipas","code":"MX-TAM"},"MX-TLA":{"countryCode":"MX","subdivisionName":"Tlaxcala","code":"MX-TLA"},"MX-VER":{"countryCode":"MX","subdivisionName":"Veracruz de Ignacio de la Llave","code":"MX-VER"},"MX-YUC":{"countryCode":"MX","subdivisionName":"Yucatan","code":"MX-YUC"},"MX-ZAC":{"countryCode":"MX","subdivisionName":"Zacatecas","code":"MX-ZAC"},"MY-01":{"countryCode":"MY","subdivisionName":"Johor","code":"MY-01"},"MY-02":{"countryCode":"MY","subdivisionName":"Kedah","code":"MY-02"},"MY-03":{"countryCode":"MY","subdivisionName":"Kelantan","code":"MY-03"},"MY-04":{"countryCode":"MY","subdivisionName":"Melaka","code":"MY-04"},"MY-05":{"countryCode":"MY","subdivisionName":"Negeri Sembilan","code":"MY-05"},"MY-06":{"countryCode":"MY","subdivisionName":"Pahang","code":"MY-06"},"MY-08":{"countryCode":"MY","subdivisionName":"Perak","code":"MY-08"},"MY-09":{"countryCode":"MY","subdivisionName":"Perlis","code":"MY-09"},"MY-07":{"countryCode":"MY","subdivisionName":"Pulau Pinang","code":"MY-07"},"MY-12":{"countryCode":"MY","subdivisionName":"Sabah","code":"MY-12"},"MY-13":{"countryCode":"MY","subdivisionName":"Sarawak","code":"MY-13"},"MY-10":{"countryCode":"MY","subdivisionName":"Selangor","code":"MY-10"},"MY-11":{"countryCode":"MY","subdivisionName":"Terengganu","code":"MY-11"},"MY-14":{"countryCode":"MY","subdivisionName":"Wilayah Persekutuan Kuala Lumpur","code":"MY-14"},"MY-15":{"countryCode":"MY","subdivisionName":"Wilayah Persekutuan Labuan","code":"MY-15"},"MY-16":{"countryCode":"MY","subdivisionName":"Wilayah Persekutuan Putrajaya","code":"MY-16"},"MZ-P":{"countryCode":"MZ","subdivisionName":"Cabo Delgado","code":"MZ-P"},"MZ-G":{"countryCode":"MZ","subdivisionName":"Gaza","code":"MZ-G"},"MZ-I":{"countryCode":"MZ","subdivisionName":"Inhambane","code":"MZ-I"},"MZ-B":{"countryCode":"MZ","subdivisionName":"Manica","code":"MZ-B"},"MZ-L":{"countryCode":"MZ","subdivisionName":"Maputo","code":"MZ-L"},"MZ-N":{"countryCode":"MZ","subdivisionName":"Nampula","code":"MZ-N"},"MZ-A":{"countryCode":"MZ","subdivisionName":"Niassa","code":"MZ-A"},"MZ-S":{"countryCode":"MZ","subdivisionName":"Sofala","code":"MZ-S"},"MZ-T":{"countryCode":"MZ","subdivisionName":"Tete","code":"MZ-T"},"MZ-Q":{"countryCode":"MZ","subdivisionName":"Zambezia","code":"MZ-Q"},"NA-ER":{"countryCode":"NA","subdivisionName":"Erongo","code":"NA-ER"},"NA-HA":{"countryCode":"NA","subdivisionName":"Hardap","code":"NA-HA"},"NA-KA":{"countryCode":"NA","subdivisionName":"Karas","code":"NA-KA"},"NA-KE":{"countryCode":"NA","subdivisionName":"Kavango East","code":"NA-KE"},"NA-KW":{"countryCode":"NA","subdivisionName":"Kavango West","code":"NA-KW"},"NA-KH":{"countryCode":"NA","subdivisionName":"Khomas","code":"NA-KH"},"NA-KU":{"countryCode":"NA","subdivisionName":"Kunene","code":"NA-KU"},"NA-OW":{"countryCode":"NA","subdivisionName":"Ohangwena","code":"NA-OW"},"NA-OH":{"countryCode":"NA","subdivisionName":"Omaheke","code":"NA-OH"},"NA-OS":{"countryCode":"NA","subdivisionName":"Omusati","code":"NA-OS"},"NA-ON":{"countryCode":"NA","subdivisionName":"Oshana","code":"NA-ON"},"NA-OT":{"countryCode":"NA","subdivisionName":"Oshikoto","code":"NA-OT"},"NA-OD":{"countryCode":"NA","subdivisionName":"Otjozondjupa","code":"NA-OD"},"NA-CA":{"countryCode":"NA","subdivisionName":"Zambezi","code":"NA-CA"},"NE-1":{"countryCode":"NE","subdivisionName":"Agadez","code":"NE-1"},"NE-2":{"countryCode":"NE","subdivisionName":"Diffa","code":"NE-2"},"NE-3":{"countryCode":"NE","subdivisionName":"Dosso","code":"NE-3"},"NE-4":{"countryCode":"NE","subdivisionName":"Maradi","code":"NE-4"},"NE-8":{"countryCode":"NE","subdivisionName":"Niamey","code":"NE-8"},"NE-5":{"countryCode":"NE","subdivisionName":"Tahoua","code":"NE-5"},"NE-6":{"countryCode":"NE","subdivisionName":"Tillaberi","code":"NE-6"},"NE-7":{"countryCode":"NE","subdivisionName":"Zinder","code":"NE-7"},"NG-AB":{"countryCode":"NG","subdivisionName":"Abia","code":"NG-AB"},"NG-FC":{"countryCode":"NG","subdivisionName":"Abuja Federal Capital Territory","code":"NG-FC"},"NG-AD":{"countryCode":"NG","subdivisionName":"Adamawa","code":"NG-AD"},"NG-AK":{"countryCode":"NG","subdivisionName":"Akwa Ibom","code":"NG-AK"},"NG-AN":{"countryCode":"NG","subdivisionName":"Anambra","code":"NG-AN"},"NG-BA":{"countryCode":"NG","subdivisionName":"Bauchi","code":"NG-BA"},"NG-BY":{"countryCode":"NG","subdivisionName":"Bayelsa","code":"NG-BY"},"NG-BE":{"countryCode":"NG","subdivisionName":"Benue","code":"NG-BE"},"NG-BO":{"countryCode":"NG","subdivisionName":"Borno","code":"NG-BO"},"NG-CR":{"countryCode":"NG","subdivisionName":"Cross River","code":"NG-CR"},"NG-DE":{"countryCode":"NG","subdivisionName":"Delta","code":"NG-DE"},"NG-EB":{"countryCode":"NG","subdivisionName":"Ebonyi","code":"NG-EB"},"NG-ED":{"countryCode":"NG","subdivisionName":"Edo","code":"NG-ED"},"NG-EK":{"countryCode":"NG","subdivisionName":"Ekiti","code":"NG-EK"},"NG-EN":{"countryCode":"NG","subdivisionName":"Enugu","code":"NG-EN"},"NG-GO":{"countryCode":"NG","subdivisionName":"Gombe","code":"NG-GO"},"NG-IM":{"countryCode":"NG","subdivisionName":"Imo","code":"NG-IM"},"NG-JI":{"countryCode":"NG","subdivisionName":"Jigawa","code":"NG-JI"},"NG-KD":{"countryCode":"NG","subdivisionName":"Kaduna","code":"NG-KD"},"NG-KN":{"countryCode":"NG","subdivisionName":"Kano","code":"NG-KN"},"NG-KT":{"countryCode":"NG","subdivisionName":"Katsina","code":"NG-KT"},"NG-KE":{"countryCode":"NG","subdivisionName":"Kebbi","code":"NG-KE"},"NG-KO":{"countryCode":"NG","subdivisionName":"Kogi","code":"NG-KO"},"NG-KW":{"countryCode":"NG","subdivisionName":"Kwara","code":"NG-KW"},"NG-LA":{"countryCode":"NG","subdivisionName":"Lagos","code":"NG-LA"},"NG-NA":{"countryCode":"NG","subdivisionName":"Nasarawa","code":"NG-NA"},"NG-NI":{"countryCode":"NG","subdivisionName":"Niger","code":"NG-NI"},"NG-OG":{"countryCode":"NG","subdivisionName":"Ogun","code":"NG-OG"},"NG-ON":{"countryCode":"NG","subdivisionName":"Ondo","code":"NG-ON"},"NG-OS":{"countryCode":"NG","subdivisionName":"Osun","code":"NG-OS"},"NG-OY":{"countryCode":"NG","subdivisionName":"Oyo","code":"NG-OY"},"NG-PL":{"countryCode":"NG","subdivisionName":"Plateau","code":"NG-PL"},"NG-RI":{"countryCode":"NG","subdivisionName":"Rivers","code":"NG-RI"},"NG-SO":{"countryCode":"NG","subdivisionName":"Sokoto","code":"NG-SO"},"NG-TA":{"countryCode":"NG","subdivisionName":"Taraba","code":"NG-TA"},"NG-YO":{"countryCode":"NG","subdivisionName":"Yobe","code":"NG-YO"},"NG-ZA":{"countryCode":"NG","subdivisionName":"Zamfara","code":"NG-ZA"},"NI-BO":{"countryCode":"NI","subdivisionName":"Boaco","code":"NI-BO"},"NI-CA":{"countryCode":"NI","subdivisionName":"Carazo","code":"NI-CA"},"NI-CI":{"countryCode":"NI","subdivisionName":"Chinandega","code":"NI-CI"},"NI-CO":{"countryCode":"NI","subdivisionName":"Chontales","code":"NI-CO"},"NI-AN":{"countryCode":"NI","subdivisionName":"Costa Caribe Norte","code":"NI-AN"},"NI-AS":{"countryCode":"NI","subdivisionName":"Costa Caribe Sur","code":"NI-AS"},"NI-ES":{"countryCode":"NI","subdivisionName":"Esteli","code":"NI-ES"},"NI-GR":{"countryCode":"NI","subdivisionName":"Granada","code":"NI-GR"},"NI-JI":{"countryCode":"NI","subdivisionName":"Jinotega","code":"NI-JI"},"NI-LE":{"countryCode":"NI","subdivisionName":"Leon","code":"NI-LE"},"NI-MD":{"countryCode":"NI","subdivisionName":"Madriz","code":"NI-MD"},"NI-MN":{"countryCode":"NI","subdivisionName":"Managua","code":"NI-MN"},"NI-MS":{"countryCode":"NI","subdivisionName":"Masaya","code":"NI-MS"},"NI-MT":{"countryCode":"NI","subdivisionName":"Matagalpa","code":"NI-MT"},"NI-NS":{"countryCode":"NI","subdivisionName":"Nueva Segovia","code":"NI-NS"},"NI-SJ":{"countryCode":"NI","subdivisionName":"Rio San Juan","code":"NI-SJ"},"NI-RI":{"countryCode":"NI","subdivisionName":"Rivas","code":"NI-RI"},"NL-DR":{"countryCode":"NL","subdivisionName":"Drenthe","code":"NL-DR"},"NL-FL":{"countryCode":"NL","subdivisionName":"Flevoland","code":"NL-FL"},"NL-FR":{"countryCode":"NL","subdivisionName":"Fryslan","code":"NL-FR"},"NL-GE":{"countryCode":"NL","subdivisionName":"Gelderland","code":"NL-GE"},"NL-GR":{"countryCode":"NL","subdivisionName":"Groningen","code":"NL-GR"},"NL-LI":{"countryCode":"NL","subdivisionName":"Limburg","code":"NL-LI"},"NL-NB":{"countryCode":"NL","subdivisionName":"Noord-Brabant","code":"NL-NB"},"NL-NH":{"countryCode":"NL","subdivisionName":"Noord-Holland","code":"NL-NH"},"NL-OV":{"countryCode":"NL","subdivisionName":"Overijssel","code":"NL-OV"},"NL-UT":{"countryCode":"NL","subdivisionName":"Utrecht","code":"NL-UT"},"NL-ZE":{"countryCode":"NL","subdivisionName":"Zeeland","code":"NL-ZE"},"NL-ZH":{"countryCode":"NL","subdivisionName":"Zuid-Holland","code":"NL-ZH"},"NO-42":{"countryCode":"NO","subdivisionName":"Agder","code":"NO-42"},"NO-34":{"countryCode":"NO","subdivisionName":"Innlandet","code":"NO-34"},"NO-15":{"countryCode":"NO","subdivisionName":"More og Romsdal","code":"NO-15"},"NO-18":{"countryCode":"NO","subdivisionName":"Nordland","code":"NO-18"},"NO-03":{"countryCode":"NO","subdivisionName":"Oslo","code":"NO-03"},"NO-11":{"countryCode":"NO","subdivisionName":"Rogaland","code":"NO-11"},"NO-54":{"countryCode":"NO","subdivisionName":"Troms og Finnmark","code":"NO-54"},"NO-50":{"countryCode":"NO","subdivisionName":"Trondelag","code":"NO-50"},"NO-38":{"countryCode":"NO","subdivisionName":"Vestfold og Telemark","code":"NO-38"},"NO-46":{"countryCode":"NO","subdivisionName":"Vestland","code":"NO-46"},"NO-30":{"countryCode":"NO","subdivisionName":"Viken","code":"NO-30"},"NP-BA":{"countryCode":"NP","subdivisionName":"Bagmati","code":"NP-BA"},"NP-BH":{"countryCode":"NP","subdivisionName":"Bheri","code":"NP-BH"},"NP-DH":{"countryCode":"NP","subdivisionName":"Dhawalagiri","code":"NP-DH"},"NP-GA":{"countryCode":"NP","subdivisionName":"Gandaki","code":"NP-GA"},"NP-JA":{"countryCode":"NP","subdivisionName":"Janakpur","code":"NP-JA"},"NP-KA":{"countryCode":"NP","subdivisionName":"Karnali","code":"NP-KA"},"NP-KO":{"countryCode":"NP","subdivisionName":"Kosi","code":"NP-KO"},"NP-LU":{"countryCode":"NP","subdivisionName":"Lumbini","code":"NP-LU"},"NP-MA":{"countryCode":"NP","subdivisionName":"Mahakali","code":"NP-MA"},"NP-ME":{"countryCode":"NP","subdivisionName":"Mechi","code":"NP-ME"},"NP-NA":{"countryCode":"NP","subdivisionName":"Narayani","code":"NP-NA"},"NP-RA":{"countryCode":"NP","subdivisionName":"Rapti","code":"NP-RA"},"NP-SA":{"countryCode":"NP","subdivisionName":"Sagarmatha","code":"NP-SA"},"NP-SE":{"countryCode":"NP","subdivisionName":"Seti","code":"NP-SE"},"NR-01":{"countryCode":"NR","subdivisionName":"Aiwo","code":"NR-01"},"NR-03":{"countryCode":"NR","subdivisionName":"Anetan","code":"NR-03"},"NR-14":{"countryCode":"NR","subdivisionName":"Yaren","code":"NR-14"},"NZ-AUK":{"countryCode":"NZ","subdivisionName":"Auckland","code":"NZ-AUK"},"NZ-BOP":{"countryCode":"NZ","subdivisionName":"Bay of Plenty","code":"NZ-BOP"},"NZ-CAN":{"countryCode":"NZ","subdivisionName":"Canterbury","code":"NZ-CAN"},"NZ-CIT":{"countryCode":"NZ","subdivisionName":"Chatham Islands Territory","code":"NZ-CIT"},"NZ-GIS":{"countryCode":"NZ","subdivisionName":"Gisborne","code":"NZ-GIS"},"NZ-HKB":{"countryCode":"NZ","subdivisionName":"Hawke's Bay","code":"NZ-HKB"},"NZ-MWT":{"countryCode":"NZ","subdivisionName":"Manawatu-Wanganui","code":"NZ-MWT"},"NZ-MBH":{"countryCode":"NZ","subdivisionName":"Marlborough","code":"NZ-MBH"},"NZ-NSN":{"countryCode":"NZ","subdivisionName":"Nelson","code":"NZ-NSN"},"NZ-NTL":{"countryCode":"NZ","subdivisionName":"Northland","code":"NZ-NTL"},"NZ-OTA":{"countryCode":"NZ","subdivisionName":"Otago","code":"NZ-OTA"},"NZ-STL":{"countryCode":"NZ","subdivisionName":"Southland","code":"NZ-STL"},"NZ-TKI":{"countryCode":"NZ","subdivisionName":"Taranaki","code":"NZ-TKI"},"NZ-TAS":{"countryCode":"NZ","subdivisionName":"Tasman","code":"NZ-TAS"},"NZ-WKO":{"countryCode":"NZ","subdivisionName":"Waikato","code":"NZ-WKO"},"NZ-WGN":{"countryCode":"NZ","subdivisionName":"Wellington","code":"NZ-WGN"},"NZ-WTC":{"countryCode":"NZ","subdivisionName":"West Coast","code":"NZ-WTC"},"OM-DA":{"countryCode":"OM","subdivisionName":"Ad Dakhiliyah","code":"OM-DA"},"OM-BU":{"countryCode":"OM","subdivisionName":"Al Buraymi","code":"OM-BU"},"OM-WU":{"countryCode":"OM","subdivisionName":"Al Wusta","code":"OM-WU"},"OM-ZA":{"countryCode":"OM","subdivisionName":"Az Zahirah","code":"OM-ZA"},"OM-BJ":{"countryCode":"OM","subdivisionName":"Janub al Batinah","code":"OM-BJ"},"OM-SJ":{"countryCode":"OM","subdivisionName":"Janub ash Sharqiyah","code":"OM-SJ"},"OM-MA":{"countryCode":"OM","subdivisionName":"Masqat","code":"OM-MA"},"OM-MU":{"countryCode":"OM","subdivisionName":"Musandam","code":"OM-MU"},"OM-BS":{"countryCode":"OM","subdivisionName":"Shamal al Batinah","code":"OM-BS"},"OM-SS":{"countryCode":"OM","subdivisionName":"Shamal ash Sharqiyah","code":"OM-SS"},"OM-ZU":{"countryCode":"OM","subdivisionName":"Zufar","code":"OM-ZU"},"PA-1":{"countryCode":"PA","subdivisionName":"Bocas del Toro","code":"PA-1"},"PA-4":{"countryCode":"PA","subdivisionName":"Chiriqui","code":"PA-4"},"PA-2":{"countryCode":"PA","subdivisionName":"Cocle","code":"PA-2"},"PA-3":{"countryCode":"PA","subdivisionName":"Colon","code":"PA-3"},"PA-5":{"countryCode":"PA","subdivisionName":"Darien","code":"PA-5"},"PA-6":{"countryCode":"PA","subdivisionName":"Herrera","code":"PA-6"},"PA-7":{"countryCode":"PA","subdivisionName":"Los Santos","code":"PA-7"},"PA-NB":{"countryCode":"PA","subdivisionName":"Ngobe-Bugle","code":"PA-NB"},"PA-8":{"countryCode":"PA","subdivisionName":"Panama","code":"PA-8"},"PA-9":{"countryCode":"PA","subdivisionName":"Veraguas","code":"PA-9"},"PE-AMA":{"countryCode":"PE","subdivisionName":"Amazonas","code":"PE-AMA"},"PE-ANC":{"countryCode":"PE","subdivisionName":"Ancash","code":"PE-ANC"},"PE-APU":{"countryCode":"PE","subdivisionName":"Apurimac","code":"PE-APU"},"PE-ARE":{"countryCode":"PE","subdivisionName":"Arequipa","code":"PE-ARE"},"PE-AYA":{"countryCode":"PE","subdivisionName":"Ayacucho","code":"PE-AYA"},"PE-CAJ":{"countryCode":"PE","subdivisionName":"Cajamarca","code":"PE-CAJ"},"PE-CUS":{"countryCode":"PE","subdivisionName":"Cusco","code":"PE-CUS"},"PE-CAL":{"countryCode":"PE","subdivisionName":"El Callao","code":"PE-CAL"},"PE-HUV":{"countryCode":"PE","subdivisionName":"Huancavelica","code":"PE-HUV"},"PE-HUC":{"countryCode":"PE","subdivisionName":"Huanuco","code":"PE-HUC"},"PE-ICA":{"countryCode":"PE","subdivisionName":"Ica","code":"PE-ICA"},"PE-JUN":{"countryCode":"PE","subdivisionName":"Junin","code":"PE-JUN"},"PE-LAL":{"countryCode":"PE","subdivisionName":"La Libertad","code":"PE-LAL"},"PE-LAM":{"countryCode":"PE","subdivisionName":"Lambayeque","code":"PE-LAM"},"PE-LIM":{"countryCode":"PE","subdivisionName":"Lima","code":"PE-LIM"},"PE-LOR":{"countryCode":"PE","subdivisionName":"Loreto","code":"PE-LOR"},"PE-MDD":{"countryCode":"PE","subdivisionName":"Madre de Dios","code":"PE-MDD"},"PE-MOQ":{"countryCode":"PE","subdivisionName":"Moquegua","code":"PE-MOQ"},"PE-PAS":{"countryCode":"PE","subdivisionName":"Pasco","code":"PE-PAS"},"PE-PIU":{"countryCode":"PE","subdivisionName":"Piura","code":"PE-PIU"},"PE-PUN":{"countryCode":"PE","subdivisionName":"Puno","code":"PE-PUN"},"PE-SAM":{"countryCode":"PE","subdivisionName":"San Martin","code":"PE-SAM"},"PE-TAC":{"countryCode":"PE","subdivisionName":"Tacna","code":"PE-TAC"},"PE-TUM":{"countryCode":"PE","subdivisionName":"Tumbes","code":"PE-TUM"},"PE-UCA":{"countryCode":"PE","subdivisionName":"Ucayali","code":"PE-UCA"},"PG-NSB":{"countryCode":"PG","subdivisionName":"Bougainville","code":"PG-NSB"},"PG-CPM":{"countryCode":"PG","subdivisionName":"Central","code":"PG-CPM"},"PG-CPK":{"countryCode":"PG","subdivisionName":"Chimbu","code":"PG-CPK"},"PG-EBR":{"countryCode":"PG","subdivisionName":"East New Britain","code":"PG-EBR"},"PG-ESW":{"countryCode":"PG","subdivisionName":"East Sepik","code":"PG-ESW"},"PG-EHG":{"countryCode":"PG","subdivisionName":"Eastern Highlands","code":"PG-EHG"},"PG-MPM":{"countryCode":"PG","subdivisionName":"Madang","code":"PG-MPM"},"PG-MRL":{"countryCode":"PG","subdivisionName":"Manus","code":"PG-MRL"},"PG-MBA":{"countryCode":"PG","subdivisionName":"Milne Bay","code":"PG-MBA"},"PG-MPL":{"countryCode":"PG","subdivisionName":"Morobe","code":"PG-MPL"},"PG-NCD":{"countryCode":"PG","subdivisionName":"National Capital District (Port Moresby)","code":"PG-NCD"},"PG-NIK":{"countryCode":"PG","subdivisionName":"New Ireland","code":"PG-NIK"},"PG-SHM":{"countryCode":"PG","subdivisionName":"Southern Highlands","code":"PG-SHM"},"PG-WBK":{"countryCode":"PG","subdivisionName":"West New Britain","code":"PG-WBK"},"PG-SAN":{"countryCode":"PG","subdivisionName":"West Sepik","code":"PG-SAN"},"PG-WPD":{"countryCode":"PG","subdivisionName":"Western","code":"PG-WPD"},"PG-WHM":{"countryCode":"PG","subdivisionName":"Western Highlands","code":"PG-WHM"},"PH-ABR":{"countryCode":"PH","subdivisionName":"Abra","code":"PH-ABR"},"PH-AGN":{"countryCode":"PH","subdivisionName":"Agusan del Norte","code":"PH-AGN"},"PH-AGS":{"countryCode":"PH","subdivisionName":"Agusan del Sur","code":"PH-AGS"},"PH-AKL":{"countryCode":"PH","subdivisionName":"Aklan","code":"PH-AKL"},"PH-ALB":{"countryCode":"PH","subdivisionName":"Albay","code":"PH-ALB"},"PH-ANT":{"countryCode":"PH","subdivisionName":"Antique","code":"PH-ANT"},"PH-APA":{"countryCode":"PH","subdivisionName":"Apayao","code":"PH-APA"},"PH-AUR":{"countryCode":"PH","subdivisionName":"Aurora","code":"PH-AUR"},"PH-BAS":{"countryCode":"PH","subdivisionName":"Basilan","code":"PH-BAS"},"PH-BAN":{"countryCode":"PH","subdivisionName":"Bataan","code":"PH-BAN"},"PH-BTN":{"countryCode":"PH","subdivisionName":"Batanes","code":"PH-BTN"},"PH-BTG":{"countryCode":"PH","subdivisionName":"Batangas","code":"PH-BTG"},"PH-BEN":{"countryCode":"PH","subdivisionName":"Benguet","code":"PH-BEN"},"PH-BIL":{"countryCode":"PH","subdivisionName":"Biliran","code":"PH-BIL"},"PH-BOH":{"countryCode":"PH","subdivisionName":"Bohol","code":"PH-BOH"},"PH-BUK":{"countryCode":"PH","subdivisionName":"Bukidnon","code":"PH-BUK"},"PH-BUL":{"countryCode":"PH","subdivisionName":"Bulacan","code":"PH-BUL"},"PH-CAG":{"countryCode":"PH","subdivisionName":"Cagayan","code":"PH-CAG"},"PH-CAN":{"countryCode":"PH","subdivisionName":"Camarines Norte","code":"PH-CAN"},"PH-CAS":{"countryCode":"PH","subdivisionName":"Camarines Sur","code":"PH-CAS"},"PH-CAM":{"countryCode":"PH","subdivisionName":"Camiguin","code":"PH-CAM"},"PH-CAP":{"countryCode":"PH","subdivisionName":"Capiz","code":"PH-CAP"},"PH-CAT":{"countryCode":"PH","subdivisionName":"Catanduanes","code":"PH-CAT"},"PH-CAV":{"countryCode":"PH","subdivisionName":"Cavite","code":"PH-CAV"},"PH-CEB":{"countryCode":"PH","subdivisionName":"Cebu","code":"PH-CEB"},"PH-NCO":{"countryCode":"PH","subdivisionName":"Cotabato","code":"PH-NCO"},"PH-DAO":{"countryCode":"PH","subdivisionName":"Davao Oriental","code":"PH-DAO"},"PH-COM":{"countryCode":"PH","subdivisionName":"Davao de Oro","code":"PH-COM"},"PH-DAV":{"countryCode":"PH","subdivisionName":"Davao del Norte","code":"PH-DAV"},"PH-DAS":{"countryCode":"PH","subdivisionName":"Davao del Sur","code":"PH-DAS"},"PH-DIN":{"countryCode":"PH","subdivisionName":"Dinagat Islands","code":"PH-DIN"},"PH-EAS":{"countryCode":"PH","subdivisionName":"Eastern Samar","code":"PH-EAS"},"PH-GUI":{"countryCode":"PH","subdivisionName":"Guimaras","code":"PH-GUI"},"PH-IFU":{"countryCode":"PH","subdivisionName":"Ifugao","code":"PH-IFU"},"PH-ILN":{"countryCode":"PH","subdivisionName":"Ilocos Norte","code":"PH-ILN"},"PH-ILS":{"countryCode":"PH","subdivisionName":"Ilocos Sur","code":"PH-ILS"},"PH-ILI":{"countryCode":"PH","subdivisionName":"Iloilo","code":"PH-ILI"},"PH-ISA":{"countryCode":"PH","subdivisionName":"Isabela","code":"PH-ISA"},"PH-KAL":{"countryCode":"PH","subdivisionName":"Kalinga","code":"PH-KAL"},"PH-LUN":{"countryCode":"PH","subdivisionName":"La Union","code":"PH-LUN"},"PH-LAG":{"countryCode":"PH","subdivisionName":"Laguna","code":"PH-LAG"},"PH-LAN":{"countryCode":"PH","subdivisionName":"Lanao del Norte","code":"PH-LAN"},"PH-LAS":{"countryCode":"PH","subdivisionName":"Lanao del Sur","code":"PH-LAS"},"PH-LEY":{"countryCode":"PH","subdivisionName":"Leyte","code":"PH-LEY"},"PH-MAG":{"countryCode":"PH","subdivisionName":"Maguindanao","code":"PH-MAG"},"PH-MAD":{"countryCode":"PH","subdivisionName":"Marinduque","code":"PH-MAD"},"PH-MAS":{"countryCode":"PH","subdivisionName":"Masbate","code":"PH-MAS"},"PH-MDC":{"countryCode":"PH","subdivisionName":"Mindoro Occidental","code":"PH-MDC"},"PH-MDR":{"countryCode":"PH","subdivisionName":"Mindoro Oriental","code":"PH-MDR"},"PH-MSC":{"countryCode":"PH","subdivisionName":"Misamis Occidental","code":"PH-MSC"},"PH-MSR":{"countryCode":"PH","subdivisionName":"Misamis Oriental","code":"PH-MSR"},"PH-MOU":{"countryCode":"PH","subdivisionName":"Mountain Province","code":"PH-MOU"},"PH-00":{"countryCode":"PH","subdivisionName":"National Capital Region","code":"PH-00"},"PH-NEC":{"countryCode":"PH","subdivisionName":"Negros Occidental","code":"PH-NEC"},"PH-NER":{"countryCode":"PH","subdivisionName":"Negros Oriental","code":"PH-NER"},"PH-NSA":{"countryCode":"PH","subdivisionName":"Northern Samar","code":"PH-NSA"},"PH-NUE":{"countryCode":"PH","subdivisionName":"Nueva Ecija","code":"PH-NUE"},"PH-NUV":{"countryCode":"PH","subdivisionName":"Nueva Vizcaya","code":"PH-NUV"},"PH-PLW":{"countryCode":"PH","subdivisionName":"Palawan","code":"PH-PLW"},"PH-PAM":{"countryCode":"PH","subdivisionName":"Pampanga","code":"PH-PAM"},"PH-PAN":{"countryCode":"PH","subdivisionName":"Pangasinan","code":"PH-PAN"},"PH-QUE":{"countryCode":"PH","subdivisionName":"Quezon","code":"PH-QUE"},"PH-QUI":{"countryCode":"PH","subdivisionName":"Quirino","code":"PH-QUI"},"PH-RIZ":{"countryCode":"PH","subdivisionName":"Rizal","code":"PH-RIZ"},"PH-ROM":{"countryCode":"PH","subdivisionName":"Romblon","code":"PH-ROM"},"PH-WSA":{"countryCode":"PH","subdivisionName":"Samar","code":"PH-WSA"},"PH-SAR":{"countryCode":"PH","subdivisionName":"Sarangani","code":"PH-SAR"},"PH-SIG":{"countryCode":"PH","subdivisionName":"Siquijor","code":"PH-SIG"},"PH-SOR":{"countryCode":"PH","subdivisionName":"Sorsogon","code":"PH-SOR"},"PH-SCO":{"countryCode":"PH","subdivisionName":"South Cotabato","code":"PH-SCO"},"PH-SLE":{"countryCode":"PH","subdivisionName":"Southern Leyte","code":"PH-SLE"},"PH-SUK":{"countryCode":"PH","subdivisionName":"Sultan Kudarat","code":"PH-SUK"},"PH-SLU":{"countryCode":"PH","subdivisionName":"Sulu","code":"PH-SLU"},"PH-SUN":{"countryCode":"PH","subdivisionName":"Surigao del Norte","code":"PH-SUN"},"PH-SUR":{"countryCode":"PH","subdivisionName":"Surigao del Sur","code":"PH-SUR"},"PH-TAR":{"countryCode":"PH","subdivisionName":"Tarlac","code":"PH-TAR"},"PH-TAW":{"countryCode":"PH","subdivisionName":"Tawi-Tawi","code":"PH-TAW"},"PH-ZMB":{"countryCode":"PH","subdivisionName":"Zambales","code":"PH-ZMB"},"PH-ZSI":{"countryCode":"PH","subdivisionName":"Zamboanga Sibugay","code":"PH-ZSI"},"PH-ZAN":{"countryCode":"PH","subdivisionName":"Zamboanga del Norte","code":"PH-ZAN"},"PH-ZAS":{"countryCode":"PH","subdivisionName":"Zamboanga del Sur","code":"PH-ZAS"},"PK-JK":{"countryCode":"PK","subdivisionName":"Azad Jammu and Kashmir","code":"PK-JK"},"PK-BA":{"countryCode":"PK","subdivisionName":"Balochistan","code":"PK-BA"},"PK-GB":{"countryCode":"PK","subdivisionName":"Gilgit-Baltistan","code":"PK-GB"},"PK-IS":{"countryCode":"PK","subdivisionName":"Islamabad","code":"PK-IS"},"PK-KP":{"countryCode":"PK","subdivisionName":"Khyber Pakhtunkhwa","code":"PK-KP"},"PK-PB":{"countryCode":"PK","subdivisionName":"Punjab","code":"PK-PB"},"PK-SD":{"countryCode":"PK","subdivisionName":"Sindh","code":"PK-SD"},"PL-02":{"countryCode":"PL","subdivisionName":"Dolnoslaskie","code":"PL-02"},"PL-04":{"countryCode":"PL","subdivisionName":"Kujawsko-pomorskie","code":"PL-04"},"PL-10":{"countryCode":"PL","subdivisionName":"Lodzkie","code":"PL-10"},"PL-06":{"countryCode":"PL","subdivisionName":"Lubelskie","code":"PL-06"},"PL-08":{"countryCode":"PL","subdivisionName":"Lubuskie","code":"PL-08"},"PL-12":{"countryCode":"PL","subdivisionName":"Malopolskie","code":"PL-12"},"PL-14":{"countryCode":"PL","subdivisionName":"Mazowieckie","code":"PL-14"},"PL-16":{"countryCode":"PL","subdivisionName":"Opolskie","code":"PL-16"},"PL-18":{"countryCode":"PL","subdivisionName":"Podkarpackie","code":"PL-18"},"PL-20":{"countryCode":"PL","subdivisionName":"Podlaskie","code":"PL-20"},"PL-22":{"countryCode":"PL","subdivisionName":"Pomorskie","code":"PL-22"},"PL-24":{"countryCode":"PL","subdivisionName":"Slaskie","code":"PL-24"},"PL-26":{"countryCode":"PL","subdivisionName":"Swietokrzyskie","code":"PL-26"},"PL-28":{"countryCode":"PL","subdivisionName":"Warminsko-mazurskie","code":"PL-28"},"PL-30":{"countryCode":"PL","subdivisionName":"Wielkopolskie","code":"PL-30"},"PL-32":{"countryCode":"PL","subdivisionName":"Zachodniopomorskie","code":"PL-32"},"PS-BTH":{"countryCode":"PS","subdivisionName":"Bethlehem","code":"PS-BTH"},"PS-DEB":{"countryCode":"PS","subdivisionName":"Deir El Balah","code":"PS-DEB"},"PS-GZA":{"countryCode":"PS","subdivisionName":"Gaza","code":"PS-GZA"},"PS-HBN":{"countryCode":"PS","subdivisionName":"Hebron","code":"PS-HBN"},"PS-JEN":{"countryCode":"PS","subdivisionName":"Jenin","code":"PS-JEN"},"PS-JRH":{"countryCode":"PS","subdivisionName":"Jericho and Al Aghwar","code":"PS-JRH"},"PS-JEM":{"countryCode":"PS","subdivisionName":"Jerusalem","code":"PS-JEM"},"PS-KYS":{"countryCode":"PS","subdivisionName":"Khan Yunis","code":"PS-KYS"},"PS-NBS":{"countryCode":"PS","subdivisionName":"Nablus","code":"PS-NBS"},"PS-QQA":{"countryCode":"PS","subdivisionName":"Qalqilya","code":"PS-QQA"},"PS-RFH":{"countryCode":"PS","subdivisionName":"Rafah","code":"PS-RFH"},"PS-RBH":{"countryCode":"PS","subdivisionName":"Ramallah","code":"PS-RBH"},"PS-SLT":{"countryCode":"PS","subdivisionName":"Salfit","code":"PS-SLT"},"PS-TBS":{"countryCode":"PS","subdivisionName":"Tubas","code":"PS-TBS"},"PS-TKM":{"countryCode":"PS","subdivisionName":"Tulkarm","code":"PS-TKM"},"PT-01":{"countryCode":"PT","subdivisionName":"Aveiro","code":"PT-01"},"PT-02":{"countryCode":"PT","subdivisionName":"Beja","code":"PT-02"},"PT-03":{"countryCode":"PT","subdivisionName":"Braga","code":"PT-03"},"PT-04":{"countryCode":"PT","subdivisionName":"Braganca","code":"PT-04"},"PT-05":{"countryCode":"PT","subdivisionName":"Castelo Branco","code":"PT-05"},"PT-06":{"countryCode":"PT","subdivisionName":"Coimbra","code":"PT-06"},"PT-07":{"countryCode":"PT","subdivisionName":"Evora","code":"PT-07"},"PT-08":{"countryCode":"PT","subdivisionName":"Faro","code":"PT-08"},"PT-09":{"countryCode":"PT","subdivisionName":"Guarda","code":"PT-09"},"PT-10":{"countryCode":"PT","subdivisionName":"Leiria","code":"PT-10"},"PT-11":{"countryCode":"PT","subdivisionName":"Lisboa","code":"PT-11"},"PT-12":{"countryCode":"PT","subdivisionName":"Portalegre","code":"PT-12"},"PT-13":{"countryCode":"PT","subdivisionName":"Porto","code":"PT-13"},"PT-30":{"countryCode":"PT","subdivisionName":"Regiao Autonoma da Madeira","code":"PT-30"},"PT-20":{"countryCode":"PT","subdivisionName":"Regiao Autonoma dos Acores","code":"PT-20"},"PT-14":{"countryCode":"PT","subdivisionName":"Santarem","code":"PT-14"},"PT-15":{"countryCode":"PT","subdivisionName":"Setubal","code":"PT-15"},"PT-16":{"countryCode":"PT","subdivisionName":"Viana do Castelo","code":"PT-16"},"PT-17":{"countryCode":"PT","subdivisionName":"Vila Real","code":"PT-17"},"PT-18":{"countryCode":"PT","subdivisionName":"Viseu","code":"PT-18"},"PW-004":{"countryCode":"PW","subdivisionName":"Airai","code":"PW-004"},"PW-150":{"countryCode":"PW","subdivisionName":"Koror","code":"PW-150"},"PW-212":{"countryCode":"PW","subdivisionName":"Melekeok","code":"PW-212"},"PY-10":{"countryCode":"PY","subdivisionName":"Alto Parana","code":"PY-10"},"PY-13":{"countryCode":"PY","subdivisionName":"Amambay","code":"PY-13"},"PY-ASU":{"countryCode":"PY","subdivisionName":"Asuncion","code":"PY-ASU"},"PY-19":{"countryCode":"PY","subdivisionName":"Boqueron","code":"PY-19"},"PY-5":{"countryCode":"PY","subdivisionName":"Caaguazu","code":"PY-5"},"PY-6":{"countryCode":"PY","subdivisionName":"Caazapa","code":"PY-6"},"PY-14":{"countryCode":"PY","subdivisionName":"Canindeyu","code":"PY-14"},"PY-11":{"countryCode":"PY","subdivisionName":"Central","code":"PY-11"},"PY-1":{"countryCode":"PY","subdivisionName":"Concepcion","code":"PY-1"},"PY-3":{"countryCode":"PY","subdivisionName":"Cordillera","code":"PY-3"},"PY-4":{"countryCode":"PY","subdivisionName":"Guaira","code":"PY-4"},"PY-7":{"countryCode":"PY","subdivisionName":"Itapua","code":"PY-7"},"PY-8":{"countryCode":"PY","subdivisionName":"Misiones","code":"PY-8"},"PY-12":{"countryCode":"PY","subdivisionName":"Neembucu","code":"PY-12"},"PY-9":{"countryCode":"PY","subdivisionName":"Paraguari","code":"PY-9"},"PY-15":{"countryCode":"PY","subdivisionName":"Presidente Hayes","code":"PY-15"},"PY-2":{"countryCode":"PY","subdivisionName":"San Pedro","code":"PY-2"},"QA-DA":{"countryCode":"QA","subdivisionName":"Ad Dawhah","code":"QA-DA"},"QA-KH":{"countryCode":"QA","subdivisionName":"Al Khawr wa adh Dhakhirah","code":"QA-KH"},"QA-WA":{"countryCode":"QA","subdivisionName":"Al Wakrah","code":"QA-WA"},"QA-RA":{"countryCode":"QA","subdivisionName":"Ar Rayyan","code":"QA-RA"},"QA-MS":{"countryCode":"QA","subdivisionName":"Ash Shamal","code":"QA-MS"},"QA-ZA":{"countryCode":"QA","subdivisionName":"Az Za'ayin","code":"QA-ZA"},"QA-US":{"countryCode":"QA","subdivisionName":"Umm Salal","code":"QA-US"},"RO-AB":{"countryCode":"RO","subdivisionName":"Alba","code":"RO-AB"},"RO-AR":{"countryCode":"RO","subdivisionName":"Arad","code":"RO-AR"},"RO-AG":{"countryCode":"RO","subdivisionName":"Arges","code":"RO-AG"},"RO-BC":{"countryCode":"RO","subdivisionName":"Bacau","code":"RO-BC"},"RO-BH":{"countryCode":"RO","subdivisionName":"Bihor","code":"RO-BH"},"RO-BN":{"countryCode":"RO","subdivisionName":"Bistrita-Nasaud","code":"RO-BN"},"RO-BT":{"countryCode":"RO","subdivisionName":"Botosani","code":"RO-BT"},"RO-BR":{"countryCode":"RO","subdivisionName":"Braila","code":"RO-BR"},"RO-BV":{"countryCode":"RO","subdivisionName":"Brasov","code":"RO-BV"},"RO-B":{"countryCode":"RO","subdivisionName":"Bucuresti","code":"RO-B"},"RO-BZ":{"countryCode":"RO","subdivisionName":"Buzau","code":"RO-BZ"},"RO-CL":{"countryCode":"RO","subdivisionName":"Calarasi","code":"RO-CL"},"RO-CS":{"countryCode":"RO","subdivisionName":"Caras-Severin","code":"RO-CS"},"RO-CJ":{"countryCode":"RO","subdivisionName":"Cluj","code":"RO-CJ"},"RO-CT":{"countryCode":"RO","subdivisionName":"Constanta","code":"RO-CT"},"RO-CV":{"countryCode":"RO","subdivisionName":"Covasna","code":"RO-CV"},"RO-DB":{"countryCode":"RO","subdivisionName":"Dambovita","code":"RO-DB"},"RO-DJ":{"countryCode":"RO","subdivisionName":"Dolj","code":"RO-DJ"},"RO-GL":{"countryCode":"RO","subdivisionName":"Galati","code":"RO-GL"},"RO-GR":{"countryCode":"RO","subdivisionName":"Giurgiu","code":"RO-GR"},"RO-GJ":{"countryCode":"RO","subdivisionName":"Gorj","code":"RO-GJ"},"RO-HR":{"countryCode":"RO","subdivisionName":"Harghita","code":"RO-HR"},"RO-HD":{"countryCode":"RO","subdivisionName":"Hunedoara","code":"RO-HD"},"RO-IL":{"countryCode":"RO","subdivisionName":"Ialomita","code":"RO-IL"},"RO-IS":{"countryCode":"RO","subdivisionName":"Iasi","code":"RO-IS"},"RO-IF":{"countryCode":"RO","subdivisionName":"Ilfov","code":"RO-IF"},"RO-MM":{"countryCode":"RO","subdivisionName":"Maramures","code":"RO-MM"},"RO-MH":{"countryCode":"RO","subdivisionName":"Mehedinti","code":"RO-MH"},"RO-MS":{"countryCode":"RO","subdivisionName":"Mures","code":"RO-MS"},"RO-NT":{"countryCode":"RO","subdivisionName":"Neamt","code":"RO-NT"},"RO-OT":{"countryCode":"RO","subdivisionName":"Olt","code":"RO-OT"},"RO-PH":{"countryCode":"RO","subdivisionName":"Prahova","code":"RO-PH"},"RO-SJ":{"countryCode":"RO","subdivisionName":"Salaj","code":"RO-SJ"},"RO-SM":{"countryCode":"RO","subdivisionName":"Satu Mare","code":"RO-SM"},"RO-SB":{"countryCode":"RO","subdivisionName":"Sibiu","code":"RO-SB"},"RO-SV":{"countryCode":"RO","subdivisionName":"Suceava","code":"RO-SV"},"RO-TR":{"countryCode":"RO","subdivisionName":"Teleorman","code":"RO-TR"},"RO-TM":{"countryCode":"RO","subdivisionName":"Timis","code":"RO-TM"},"RO-TL":{"countryCode":"RO","subdivisionName":"Tulcea","code":"RO-TL"},"RO-VL":{"countryCode":"RO","subdivisionName":"Valcea","code":"RO-VL"},"RO-VS":{"countryCode":"RO","subdivisionName":"Vaslui","code":"RO-VS"},"RO-VN":{"countryCode":"RO","subdivisionName":"Vrancea","code":"RO-VN"},"RS-00":{"countryCode":"RS","subdivisionName":"Beograd","code":"RS-00"},"RS-14":{"countryCode":"RS","subdivisionName":"Borski okrug","code":"RS-14"},"RS-11":{"countryCode":"RS","subdivisionName":"Branicevski okrug","code":"RS-11"},"RS-23":{"countryCode":"RS","subdivisionName":"Jablanicki okrug","code":"RS-23"},"RS-06":{"countryCode":"RS","subdivisionName":"Juznobacki okrug","code":"RS-06"},"RS-04":{"countryCode":"RS","subdivisionName":"Juznobanatski okrug","code":"RS-04"},"RS-09":{"countryCode":"RS","subdivisionName":"Kolubarski okrug","code":"RS-09"},"RS-28":{"countryCode":"RS","subdivisionName":"Kosovsko-Mitrovacki okrug","code":"RS-28"},"RS-08":{"countryCode":"RS","subdivisionName":"Macvanski okrug","code":"RS-08"},"RS-17":{"countryCode":"RS","subdivisionName":"Moravicki okrug","code":"RS-17"},"RS-20":{"countryCode":"RS","subdivisionName":"Nisavski okrug","code":"RS-20"},"RS-24":{"countryCode":"RS","subdivisionName":"Pcinjski okrug","code":"RS-24"},"RS-26":{"countryCode":"RS","subdivisionName":"Pecki okrug","code":"RS-26"},"RS-22":{"countryCode":"RS","subdivisionName":"Pirotski okrug","code":"RS-22"},"RS-10":{"countryCode":"RS","subdivisionName":"Podunavski okrug","code":"RS-10"},"RS-13":{"countryCode":"RS","subdivisionName":"Pomoravski okrug","code":"RS-13"},"RS-27":{"countryCode":"RS","subdivisionName":"Prizrenski okrug","code":"RS-27"},"RS-19":{"countryCode":"RS","subdivisionName":"Rasinski okrug","code":"RS-19"},"RS-18":{"countryCode":"RS","subdivisionName":"Raski okrug","code":"RS-18"},"RS-01":{"countryCode":"RS","subdivisionName":"Severnobacki okrug","code":"RS-01"},"RS-03":{"countryCode":"RS","subdivisionName":"Severnobanatski okrug","code":"RS-03"},"RS-02":{"countryCode":"RS","subdivisionName":"Srednjebanatski okrug","code":"RS-02"},"RS-07":{"countryCode":"RS","subdivisionName":"Sremski okrug","code":"RS-07"},"RS-12":{"countryCode":"RS","subdivisionName":"Sumadijski okrug","code":"RS-12"},"RS-21":{"countryCode":"RS","subdivisionName":"Toplicki okrug","code":"RS-21"},"RS-15":{"countryCode":"RS","subdivisionName":"Zajecarski okrug","code":"RS-15"},"RS-05":{"countryCode":"RS","subdivisionName":"Zapadnobacki okrug","code":"RS-05"},"RS-16":{"countryCode":"RS","subdivisionName":"Zlatiborski okrug","code":"RS-16"},"RU-AD":{"countryCode":"RU","subdivisionName":"Adygeya, Respublika","code":"RU-AD"},"RU-AL":{"countryCode":"RU","subdivisionName":"Altay, Respublika","code":"RU-AL"},"RU-ALT":{"countryCode":"RU","subdivisionName":"Altayskiy kray","code":"RU-ALT"},"RU-AMU":{"countryCode":"RU","subdivisionName":"Amurskaya oblast'","code":"RU-AMU"},"RU-ARK":{"countryCode":"RU","subdivisionName":"Arkhangel'skaya oblast'","code":"RU-ARK"},"RU-AST":{"countryCode":"RU","subdivisionName":"Astrakhanskaya oblast'","code":"RU-AST"},"RU-BA":{"countryCode":"RU","subdivisionName":"Bashkortostan, Respublika","code":"RU-BA"},"RU-BEL":{"countryCode":"RU","subdivisionName":"Belgorodskaya oblast'","code":"RU-BEL"},"RU-BRY":{"countryCode":"RU","subdivisionName":"Bryanskaya oblast'","code":"RU-BRY"},"RU-BU":{"countryCode":"RU","subdivisionName":"Buryatiya, Respublika","code":"RU-BU"},"RU-CE":{"countryCode":"RU","subdivisionName":"Chechenskaya Respublika","code":"RU-CE"},"RU-CHE":{"countryCode":"RU","subdivisionName":"Chelyabinskaya oblast'","code":"RU-CHE"},"RU-CHU":{"countryCode":"RU","subdivisionName":"Chukotskiy avtonomnyy okrug","code":"RU-CHU"},"RU-CU":{"countryCode":"RU","subdivisionName":"Chuvashskaya Respublika","code":"RU-CU"},"RU-DA":{"countryCode":"RU","subdivisionName":"Dagestan, Respublika","code":"RU-DA"},"RU-IN":{"countryCode":"RU","subdivisionName":"Ingushetiya, Respublika","code":"RU-IN"},"RU-IRK":{"countryCode":"RU","subdivisionName":"Irkutskaya oblast'","code":"RU-IRK"},"RU-IVA":{"countryCode":"RU","subdivisionName":"Ivanovskaya oblast'","code":"RU-IVA"},"RU-KB":{"countryCode":"RU","subdivisionName":"Kabardino-Balkarskaya Respublika","code":"RU-KB"},"RU-KGD":{"countryCode":"RU","subdivisionName":"Kaliningradskaya oblast'","code":"RU-KGD"},"RU-KL":{"countryCode":"RU","subdivisionName":"Kalmykiya, Respublika","code":"RU-KL"},"RU-KLU":{"countryCode":"RU","subdivisionName":"Kaluzhskaya oblast'","code":"RU-KLU"},"RU-KAM":{"countryCode":"RU","subdivisionName":"Kamchatskiy kray","code":"RU-KAM"},"RU-KC":{"countryCode":"RU","subdivisionName":"Karachayevo-Cherkesskaya Respublika","code":"RU-KC"},"RU-KR":{"countryCode":"RU","subdivisionName":"Kareliya, Respublika","code":"RU-KR"},"RU-KEM":{"countryCode":"RU","subdivisionName":"Kemerovskaya oblast'","code":"RU-KEM"},"RU-KHA":{"countryCode":"RU","subdivisionName":"Khabarovskiy kray","code":"RU-KHA"},"RU-KK":{"countryCode":"RU","subdivisionName":"Khakasiya, Respublika","code":"RU-KK"},"RU-KHM":{"countryCode":"RU","subdivisionName":"Khanty-Mansiyskiy avtonomnyy okrug","code":"RU-KHM"},"RU-KIR":{"countryCode":"RU","subdivisionName":"Kirovskaya oblast'","code":"RU-KIR"},"RU-KO":{"countryCode":"RU","subdivisionName":"Komi, Respublika","code":"RU-KO"},"RU-KOS":{"countryCode":"RU","subdivisionName":"Kostromskaya oblast'","code":"RU-KOS"},"RU-KDA":{"countryCode":"RU","subdivisionName":"Krasnodarskiy kray","code":"RU-KDA"},"RU-KYA":{"countryCode":"RU","subdivisionName":"Krasnoyarskiy kray","code":"RU-KYA"},"RU-KGN":{"countryCode":"RU","subdivisionName":"Kurganskaya oblast'","code":"RU-KGN"},"RU-KRS":{"countryCode":"RU","subdivisionName":"Kurskaya oblast'","code":"RU-KRS"},"RU-LEN":{"countryCode":"RU","subdivisionName":"Leningradskaya oblast'","code":"RU-LEN"},"RU-LIP":{"countryCode":"RU","subdivisionName":"Lipetskaya oblast'","code":"RU-LIP"},"RU-MAG":{"countryCode":"RU","subdivisionName":"Magadanskaya oblast'","code":"RU-MAG"},"RU-ME":{"countryCode":"RU","subdivisionName":"Mariy El, Respublika","code":"RU-ME"},"RU-MO":{"countryCode":"RU","subdivisionName":"Mordoviya, Respublika","code":"RU-MO"},"RU-MOS":{"countryCode":"RU","subdivisionName":"Moskovskaya oblast'","code":"RU-MOS"},"RU-MOW":{"countryCode":"RU","subdivisionName":"Moskva","code":"RU-MOW"},"RU-MUR":{"countryCode":"RU","subdivisionName":"Murmanskaya oblast'","code":"RU-MUR"},"RU-NEN":{"countryCode":"RU","subdivisionName":"Nenetskiy avtonomnyy okrug","code":"RU-NEN"},"RU-NIZ":{"countryCode":"RU","subdivisionName":"Nizhegorodskaya oblast'","code":"RU-NIZ"},"RU-NGR":{"countryCode":"RU","subdivisionName":"Novgorodskaya oblast'","code":"RU-NGR"},"RU-NVS":{"countryCode":"RU","subdivisionName":"Novosibirskaya oblast'","code":"RU-NVS"},"RU-OMS":{"countryCode":"RU","subdivisionName":"Omskaya oblast'","code":"RU-OMS"},"RU-ORE":{"countryCode":"RU","subdivisionName":"Orenburgskaya oblast'","code":"RU-ORE"},"RU-ORL":{"countryCode":"RU","subdivisionName":"Orlovskaya oblast'","code":"RU-ORL"},"RU-PNZ":{"countryCode":"RU","subdivisionName":"Penzenskaya oblast'","code":"RU-PNZ"},"RU-PER":{"countryCode":"RU","subdivisionName":"Permskiy kray","code":"RU-PER"},"RU-PRI":{"countryCode":"RU","subdivisionName":"Primorskiy kray","code":"RU-PRI"},"RU-PSK":{"countryCode":"RU","subdivisionName":"Pskovskaya oblast'","code":"RU-PSK"},"RU-ROS":{"countryCode":"RU","subdivisionName":"Rostovskaya oblast'","code":"RU-ROS"},"RU-RYA":{"countryCode":"RU","subdivisionName":"Ryazanskaya oblast'","code":"RU-RYA"},"RU-SA":{"countryCode":"RU","subdivisionName":"Saha, Respublika","code":"RU-SA"},"RU-SAK":{"countryCode":"RU","subdivisionName":"Sakhalinskaya oblast'","code":"RU-SAK"},"RU-SAM":{"countryCode":"RU","subdivisionName":"Samarskaya oblast'","code":"RU-SAM"},"RU-SPE":{"countryCode":"RU","subdivisionName":"Sankt-Peterburg","code":"RU-SPE"},"RU-SAR":{"countryCode":"RU","subdivisionName":"Saratovskaya oblast'","code":"RU-SAR"},"RU-SE":{"countryCode":"RU","subdivisionName":"Severnaya Osetiya, Respublika","code":"RU-SE"},"RU-SMO":{"countryCode":"RU","subdivisionName":"Smolenskaya oblast'","code":"RU-SMO"},"RU-STA":{"countryCode":"RU","subdivisionName":"Stavropol'skiy kray","code":"RU-STA"},"RU-SVE":{"countryCode":"RU","subdivisionName":"Sverdlovskaya oblast'","code":"RU-SVE"},"RU-TAM":{"countryCode":"RU","subdivisionName":"Tambovskaya oblast'","code":"RU-TAM"},"RU-TA":{"countryCode":"RU","subdivisionName":"Tatarstan, Respublika","code":"RU-TA"},"RU-TOM":{"countryCode":"RU","subdivisionName":"Tomskaya oblast'","code":"RU-TOM"},"RU-TUL":{"countryCode":"RU","subdivisionName":"Tul'skaya oblast'","code":"RU-TUL"},"RU-TVE":{"countryCode":"RU","subdivisionName":"Tverskaya oblast'","code":"RU-TVE"},"RU-TYU":{"countryCode":"RU","subdivisionName":"Tyumenskaya oblast'","code":"RU-TYU"},"RU-TY":{"countryCode":"RU","subdivisionName":"Tyva, Respublika","code":"RU-TY"},"RU-UD":{"countryCode":"RU","subdivisionName":"Udmurtskaya Respublika","code":"RU-UD"},"RU-ULY":{"countryCode":"RU","subdivisionName":"Ul'yanovskaya oblast'","code":"RU-ULY"},"RU-VLA":{"countryCode":"RU","subdivisionName":"Vladimirskaya oblast'","code":"RU-VLA"},"RU-VGG":{"countryCode":"RU","subdivisionName":"Volgogradskaya oblast'","code":"RU-VGG"},"RU-VLG":{"countryCode":"RU","subdivisionName":"Vologodskaya oblast'","code":"RU-VLG"},"RU-VOR":{"countryCode":"RU","subdivisionName":"Voronezhskaya oblast'","code":"RU-VOR"},"RU-YAN":{"countryCode":"RU","subdivisionName":"Yamalo-Nenetskiy avtonomnyy okrug","code":"RU-YAN"},"RU-YAR":{"countryCode":"RU","subdivisionName":"Yaroslavskaya oblast'","code":"RU-YAR"},"RU-YEV":{"countryCode":"RU","subdivisionName":"Yevreyskaya avtonomnaya oblast'","code":"RU-YEV"},"RU-ZAB":{"countryCode":"RU","subdivisionName":"Zabaykal'skiy kray","code":"RU-ZAB"},"RW-02":{"countryCode":"RW","subdivisionName":"Est","code":"RW-02"},"RW-03":{"countryCode":"RW","subdivisionName":"Nord","code":"RW-03"},"RW-04":{"countryCode":"RW","subdivisionName":"Ouest","code":"RW-04"},"RW-05":{"countryCode":"RW","subdivisionName":"Sud","code":"RW-05"},"RW-01":{"countryCode":"RW","subdivisionName":"Ville de Kigali","code":"RW-01"},"SA-14":{"countryCode":"SA","subdivisionName":"'Asir","code":"SA-14"},"SA-11":{"countryCode":"SA","subdivisionName":"Al Bahah","code":"SA-11"},"SA-08":{"countryCode":"SA","subdivisionName":"Al Hudud ash Shamaliyah","code":"SA-08"},"SA-12":{"countryCode":"SA","subdivisionName":"Al Jawf","code":"SA-12"},"SA-03":{"countryCode":"SA","subdivisionName":"Al Madinah al Munawwarah","code":"SA-03"},"SA-05":{"countryCode":"SA","subdivisionName":"Al Qasim","code":"SA-05"},"SA-01":{"countryCode":"SA","subdivisionName":"Ar Riyad","code":"SA-01"},"SA-04":{"countryCode":"SA","subdivisionName":"Ash Sharqiyah","code":"SA-04"},"SA-06":{"countryCode":"SA","subdivisionName":"Ha'il","code":"SA-06"},"SA-09":{"countryCode":"SA","subdivisionName":"Jazan","code":"SA-09"},"SA-02":{"countryCode":"SA","subdivisionName":"Makkah al Mukarramah","code":"SA-02"},"SA-10":{"countryCode":"SA","subdivisionName":"Najran","code":"SA-10"},"SA-07":{"countryCode":"SA","subdivisionName":"Tabuk","code":"SA-07"},"SB-CH":{"countryCode":"SB","subdivisionName":"Choiseul","code":"SB-CH"},"SB-GU":{"countryCode":"SB","subdivisionName":"Guadalcanal","code":"SB-GU"},"SB-WE":{"countryCode":"SB","subdivisionName":"Western","code":"SB-WE"},"SC-02":{"countryCode":"SC","subdivisionName":"Anse Boileau","code":"SC-02"},"SC-05":{"countryCode":"SC","subdivisionName":"Anse Royale","code":"SC-05"},"SC-01":{"countryCode":"SC","subdivisionName":"Anse aux Pins","code":"SC-01"},"SC-06":{"countryCode":"SC","subdivisionName":"Baie Lazare","code":"SC-06"},"SC-07":{"countryCode":"SC","subdivisionName":"Baie Sainte Anne","code":"SC-07"},"SC-08":{"countryCode":"SC","subdivisionName":"Beau Vallon","code":"SC-08"},"SC-10":{"countryCode":"SC","subdivisionName":"Bel Ombre","code":"SC-10"},"SC-11":{"countryCode":"SC","subdivisionName":"Cascade","code":"SC-11"},"SC-16":{"countryCode":"SC","subdivisionName":"English River","code":"SC-16"},"SC-13":{"countryCode":"SC","subdivisionName":"Grand Anse Mahe","code":"SC-13"},"SC-14":{"countryCode":"SC","subdivisionName":"Grand Anse Praslin","code":"SC-14"},"SC-15":{"countryCode":"SC","subdivisionName":"La Digue","code":"SC-15"},"SC-20":{"countryCode":"SC","subdivisionName":"Pointe Larue","code":"SC-20"},"SC-23":{"countryCode":"SC","subdivisionName":"Takamaka","code":"SC-23"},"SD-NB":{"countryCode":"SD","subdivisionName":"Blue Nile","code":"SD-NB"},"SD-DC":{"countryCode":"SD","subdivisionName":"Central Darfur","code":"SD-DC"},"SD-GD":{"countryCode":"SD","subdivisionName":"Gedaref","code":"SD-GD"},"SD-GZ":{"countryCode":"SD","subdivisionName":"Gezira","code":"SD-GZ"},"SD-KA":{"countryCode":"SD","subdivisionName":"Kassala","code":"SD-KA"},"SD-KH":{"countryCode":"SD","subdivisionName":"Khartoum","code":"SD-KH"},"SD-DN":{"countryCode":"SD","subdivisionName":"North Darfur","code":"SD-DN"},"SD-KN":{"countryCode":"SD","subdivisionName":"North Kordofan","code":"SD-KN"},"SD-NO":{"countryCode":"SD","subdivisionName":"Northern","code":"SD-NO"},"SD-RS":{"countryCode":"SD","subdivisionName":"Red Sea","code":"SD-RS"},"SD-NR":{"countryCode":"SD","subdivisionName":"River Nile","code":"SD-NR"},"SD-SI":{"countryCode":"SD","subdivisionName":"Sennar","code":"SD-SI"},"SD-DS":{"countryCode":"SD","subdivisionName":"South Darfur","code":"SD-DS"},"SD-KS":{"countryCode":"SD","subdivisionName":"South Kordofan","code":"SD-KS"},"SD-DW":{"countryCode":"SD","subdivisionName":"West Darfur","code":"SD-DW"},"SD-GK":{"countryCode":"SD","subdivisionName":"West Kordofan","code":"SD-GK"},"SD-NW":{"countryCode":"SD","subdivisionName":"White Nile","code":"SD-NW"},"SE-K":{"countryCode":"SE","subdivisionName":"Blekinge lan","code":"SE-K"},"SE-W":{"countryCode":"SE","subdivisionName":"Dalarnas lan","code":"SE-W"},"SE-X":{"countryCode":"SE","subdivisionName":"Gavleborgs lan","code":"SE-X"},"SE-I":{"countryCode":"SE","subdivisionName":"Gotlands lan","code":"SE-I"},"SE-N":{"countryCode":"SE","subdivisionName":"Hallands lan","code":"SE-N"},"SE-Z":{"countryCode":"SE","subdivisionName":"Jamtlands lan","code":"SE-Z"},"SE-F":{"countryCode":"SE","subdivisionName":"Jonkopings lan","code":"SE-F"},"SE-H":{"countryCode":"SE","subdivisionName":"Kalmar lan","code":"SE-H"},"SE-G":{"countryCode":"SE","subdivisionName":"Kronobergs lan","code":"SE-G"},"SE-BD":{"countryCode":"SE","subdivisionName":"Norrbottens lan","code":"SE-BD"},"SE-T":{"countryCode":"SE","subdivisionName":"Orebro lan","code":"SE-T"},"SE-E":{"countryCode":"SE","subdivisionName":"Ostergotlands lan","code":"SE-E"},"SE-M":{"countryCode":"SE","subdivisionName":"Skane lan","code":"SE-M"},"SE-D":{"countryCode":"SE","subdivisionName":"Sodermanlands lan","code":"SE-D"},"SE-AB":{"countryCode":"SE","subdivisionName":"Stockholms lan","code":"SE-AB"},"SE-C":{"countryCode":"SE","subdivisionName":"Uppsala lan","code":"SE-C"},"SE-S":{"countryCode":"SE","subdivisionName":"Varmlands lan","code":"SE-S"},"SE-AC":{"countryCode":"SE","subdivisionName":"Vasterbottens lan","code":"SE-AC"},"SE-Y":{"countryCode":"SE","subdivisionName":"Vasternorrlands lan","code":"SE-Y"},"SE-U":{"countryCode":"SE","subdivisionName":"Vastmanlands lan","code":"SE-U"},"SE-O":{"countryCode":"SE","subdivisionName":"Vastra Gotalands lan","code":"SE-O"},"SH-HL":{"countryCode":"SH","subdivisionName":"Saint Helena","code":"SH-HL"},"SI-001":{"countryCode":"SI","subdivisionName":"Ajdovscina","code":"SI-001"},"SI-213":{"countryCode":"SI","subdivisionName":"Ankaran","code":"SI-213"},"SI-195":{"countryCode":"SI","subdivisionName":"Apace","code":"SI-195"},"SI-002":{"countryCode":"SI","subdivisionName":"Beltinci","code":"SI-002"},"SI-148":{"countryCode":"SI","subdivisionName":"Benedikt","code":"SI-148"},"SI-149":{"countryCode":"SI","subdivisionName":"Bistrica ob Sotli","code":"SI-149"},"SI-003":{"countryCode":"SI","subdivisionName":"Bled","code":"SI-003"},"SI-150":{"countryCode":"SI","subdivisionName":"Bloke","code":"SI-150"},"SI-004":{"countryCode":"SI","subdivisionName":"Bohinj","code":"SI-004"},"SI-005":{"countryCode":"SI","subdivisionName":"Borovnica","code":"SI-005"},"SI-006":{"countryCode":"SI","subdivisionName":"Bovec","code":"SI-006"},"SI-151":{"countryCode":"SI","subdivisionName":"Braslovce","code":"SI-151"},"SI-007":{"countryCode":"SI","subdivisionName":"Brda","code":"SI-007"},"SI-009":{"countryCode":"SI","subdivisionName":"Brezice","code":"SI-009"},"SI-008":{"countryCode":"SI","subdivisionName":"Brezovica","code":"SI-008"},"SI-152":{"countryCode":"SI","subdivisionName":"Cankova","code":"SI-152"},"SI-011":{"countryCode":"SI","subdivisionName":"Celje","code":"SI-011"},"SI-012":{"countryCode":"SI","subdivisionName":"Cerklje na Gorenjskem","code":"SI-012"},"SI-013":{"countryCode":"SI","subdivisionName":"Cerknica","code":"SI-013"},"SI-014":{"countryCode":"SI","subdivisionName":"Cerkno","code":"SI-014"},"SI-196":{"countryCode":"SI","subdivisionName":"Cirkulane","code":"SI-196"},"SI-015":{"countryCode":"SI","subdivisionName":"Crensovci","code":"SI-015"},"SI-017":{"countryCode":"SI","subdivisionName":"Crnomelj","code":"SI-017"},"SI-018":{"countryCode":"SI","subdivisionName":"Destrnik","code":"SI-018"},"SI-019":{"countryCode":"SI","subdivisionName":"Divaca","code":"SI-019"},"SI-154":{"countryCode":"SI","subdivisionName":"Dobje","code":"SI-154"},"SI-020":{"countryCode":"SI","subdivisionName":"Dobrepolje","code":"SI-020"},"SI-155":{"countryCode":"SI","subdivisionName":"Dobrna","code":"SI-155"},"SI-021":{"countryCode":"SI","subdivisionName":"Dobrova-Polhov Gradec","code":"SI-021"},"SI-156":{"countryCode":"SI","subdivisionName":"Dobrovnik","code":"SI-156"},"SI-023":{"countryCode":"SI","subdivisionName":"Domzale","code":"SI-023"},"SI-024":{"countryCode":"SI","subdivisionName":"Dornava","code":"SI-024"},"SI-025":{"countryCode":"SI","subdivisionName":"Dravograd","code":"SI-025"},"SI-026":{"countryCode":"SI","subdivisionName":"Duplek","code":"SI-026"},"SI-207":{"countryCode":"SI","subdivisionName":"Gorje","code":"SI-207"},"SI-029":{"countryCode":"SI","subdivisionName":"Gornja Radgona","code":"SI-029"},"SI-031":{"countryCode":"SI","subdivisionName":"Gornji Petrovci","code":"SI-031"},"SI-158":{"countryCode":"SI","subdivisionName":"Grad","code":"SI-158"},"SI-032":{"countryCode":"SI","subdivisionName":"Grosuplje","code":"SI-032"},"SI-159":{"countryCode":"SI","subdivisionName":"Hajdina","code":"SI-159"},"SI-160":{"countryCode":"SI","subdivisionName":"Hoce-Slivnica","code":"SI-160"},"SI-161":{"countryCode":"SI","subdivisionName":"Hodos","code":"SI-161"},"SI-162":{"countryCode":"SI","subdivisionName":"Horjul","code":"SI-162"},"SI-034":{"countryCode":"SI","subdivisionName":"Hrastnik","code":"SI-034"},"SI-035":{"countryCode":"SI","subdivisionName":"Hrpelje-Kozina","code":"SI-035"},"SI-036":{"countryCode":"SI","subdivisionName":"Idrija","code":"SI-036"},"SI-037":{"countryCode":"SI","subdivisionName":"Ig","code":"SI-037"},"SI-038":{"countryCode":"SI","subdivisionName":"Ilirska Bistrica","code":"SI-038"},"SI-039":{"countryCode":"SI","subdivisionName":"Ivancna Gorica","code":"SI-039"},"SI-040":{"countryCode":"SI","subdivisionName":"Izola","code":"SI-040"},"SI-041":{"countryCode":"SI","subdivisionName":"Jesenice","code":"SI-041"},"SI-042":{"countryCode":"SI","subdivisionName":"Jursinci","code":"SI-042"},"SI-043":{"countryCode":"SI","subdivisionName":"Kamnik","code":"SI-043"},"SI-044":{"countryCode":"SI","subdivisionName":"Kanal","code":"SI-044"},"SI-045":{"countryCode":"SI","subdivisionName":"Kidricevo","code":"SI-045"},"SI-046":{"countryCode":"SI","subdivisionName":"Kobarid","code":"SI-046"},"SI-047":{"countryCode":"SI","subdivisionName":"Kobilje","code":"SI-047"},"SI-048":{"countryCode":"SI","subdivisionName":"Kocevje","code":"SI-048"},"SI-049":{"countryCode":"SI","subdivisionName":"Komen","code":"SI-049"},"SI-164":{"countryCode":"SI","subdivisionName":"Komenda","code":"SI-164"},"SI-050":{"countryCode":"SI","subdivisionName":"Koper","code":"SI-050"},"SI-197":{"countryCode":"SI","subdivisionName":"Kosanjevica na Krki","code":"SI-197"},"SI-165":{"countryCode":"SI","subdivisionName":"Kostel","code":"SI-165"},"SI-052":{"countryCode":"SI","subdivisionName":"Kranj","code":"SI-052"},"SI-053":{"countryCode":"SI","subdivisionName":"Kranjska Gora","code":"SI-053"},"SI-166":{"countryCode":"SI","subdivisionName":"Krizevci","code":"SI-166"},"SI-054":{"countryCode":"SI","subdivisionName":"Krsko","code":"SI-054"},"SI-055":{"countryCode":"SI","subdivisionName":"Kungota","code":"SI-055"},"SI-056":{"countryCode":"SI","subdivisionName":"Kuzma","code":"SI-056"},"SI-057":{"countryCode":"SI","subdivisionName":"Lasko","code":"SI-057"},"SI-058":{"countryCode":"SI","subdivisionName":"Lenart","code":"SI-058"},"SI-059":{"countryCode":"SI","subdivisionName":"Lendava","code":"SI-059"},"SI-060":{"countryCode":"SI","subdivisionName":"Litija","code":"SI-060"},"SI-061":{"countryCode":"SI","subdivisionName":"Ljubljana","code":"SI-061"},"SI-063":{"countryCode":"SI","subdivisionName":"Ljutomer","code":"SI-063"},"SI-208":{"countryCode":"SI","subdivisionName":"Log-Dragomer","code":"SI-208"},"SI-064":{"countryCode":"SI","subdivisionName":"Logatec","code":"SI-064"},"SI-065":{"countryCode":"SI","subdivisionName":"Loska dolina","code":"SI-065"},"SI-066":{"countryCode":"SI","subdivisionName":"Loski Potok","code":"SI-066"},"SI-167":{"countryCode":"SI","subdivisionName":"Lovrenc na Pohorju","code":"SI-167"},"SI-067":{"countryCode":"SI","subdivisionName":"Luce","code":"SI-067"},"SI-068":{"countryCode":"SI","subdivisionName":"Lukovica","code":"SI-068"},"SI-069":{"countryCode":"SI","subdivisionName":"Majsperk","code":"SI-069"},"SI-198":{"countryCode":"SI","subdivisionName":"Makole","code":"SI-198"},"SI-070":{"countryCode":"SI","subdivisionName":"Maribor","code":"SI-070"},"SI-168":{"countryCode":"SI","subdivisionName":"Markovci","code":"SI-168"},"SI-071":{"countryCode":"SI","subdivisionName":"Medvode","code":"SI-071"},"SI-072":{"countryCode":"SI","subdivisionName":"Menges","code":"SI-072"},"SI-073":{"countryCode":"SI","subdivisionName":"Metlika","code":"SI-073"},"SI-074":{"countryCode":"SI","subdivisionName":"Mezica","code":"SI-074"},"SI-169":{"countryCode":"SI","subdivisionName":"Miklavz na Dravskem polju","code":"SI-169"},"SI-075":{"countryCode":"SI","subdivisionName":"Miren-Kostanjevica","code":"SI-075"},"SI-212":{"countryCode":"SI","subdivisionName":"Mirna","code":"SI-212"},"SI-170":{"countryCode":"SI","subdivisionName":"Mirna Pec","code":"SI-170"},"SI-076":{"countryCode":"SI","subdivisionName":"Mislinja","code":"SI-076"},"SI-199":{"countryCode":"SI","subdivisionName":"Mokronog-Trebelno","code":"SI-199"},"SI-077":{"countryCode":"SI","subdivisionName":"Moravce","code":"SI-077"},"SI-079":{"countryCode":"SI","subdivisionName":"Mozirje","code":"SI-079"},"SI-080":{"countryCode":"SI","subdivisionName":"Murska Sobota","code":"SI-080"},"SI-081":{"countryCode":"SI","subdivisionName":"Muta","code":"SI-081"},"SI-082":{"countryCode":"SI","subdivisionName":"Naklo","code":"SI-082"},"SI-083":{"countryCode":"SI","subdivisionName":"Nazarje","code":"SI-083"},"SI-084":{"countryCode":"SI","subdivisionName":"Nova Gorica","code":"SI-084"},"SI-085":{"countryCode":"SI","subdivisionName":"Novo Mesto","code":"SI-085"},"SI-086":{"countryCode":"SI","subdivisionName":"Odranci","code":"SI-086"},"SI-171":{"countryCode":"SI","subdivisionName":"Oplotnica","code":"SI-171"},"SI-087":{"countryCode":"SI","subdivisionName":"Ormoz","code":"SI-087"},"SI-090":{"countryCode":"SI","subdivisionName":"Piran","code":"SI-090"},"SI-091":{"countryCode":"SI","subdivisionName":"Pivka","code":"SI-091"},"SI-092":{"countryCode":"SI","subdivisionName":"Podcetrtek","code":"SI-092"},"SI-172":{"countryCode":"SI","subdivisionName":"Podlehnik","code":"SI-172"},"SI-200":{"countryCode":"SI","subdivisionName":"Poljcane","code":"SI-200"},"SI-173":{"countryCode":"SI","subdivisionName":"Polzela","code":"SI-173"},"SI-094":{"countryCode":"SI","subdivisionName":"Postojna","code":"SI-094"},"SI-174":{"countryCode":"SI","subdivisionName":"Prebold","code":"SI-174"},"SI-095":{"countryCode":"SI","subdivisionName":"Preddvor","code":"SI-095"},"SI-175":{"countryCode":"SI","subdivisionName":"Prevalje","code":"SI-175"},"SI-096":{"countryCode":"SI","subdivisionName":"Ptuj","code":"SI-096"},"SI-097":{"countryCode":"SI","subdivisionName":"Puconci","code":"SI-097"},"SI-098":{"countryCode":"SI","subdivisionName":"Race-Fram","code":"SI-098"},"SI-099":{"countryCode":"SI","subdivisionName":"Radece","code":"SI-099"},"SI-100":{"countryCode":"SI","subdivisionName":"Radenci","code":"SI-100"},"SI-101":{"countryCode":"SI","subdivisionName":"Radlje ob Dravi","code":"SI-101"},"SI-102":{"countryCode":"SI","subdivisionName":"Radovljica","code":"SI-102"},"SI-103":{"countryCode":"SI","subdivisionName":"Ravne na Koroskem","code":"SI-103"},"SI-176":{"countryCode":"SI","subdivisionName":"Razkrizje","code":"SI-176"},"SI-209":{"countryCode":"SI","subdivisionName":"Recica ob Savinji","code":"SI-209"},"SI-201":{"countryCode":"SI","subdivisionName":"Rence-Vogrsko","code":"SI-201"},"SI-104":{"countryCode":"SI","subdivisionName":"Ribnica","code":"SI-104"},"SI-106":{"countryCode":"SI","subdivisionName":"Rogaska Slatina","code":"SI-106"},"SI-105":{"countryCode":"SI","subdivisionName":"Rogasovci","code":"SI-105"},"SI-108":{"countryCode":"SI","subdivisionName":"Ruse","code":"SI-108"},"SI-033":{"countryCode":"SI","subdivisionName":"Salovci","code":"SI-033"},"SI-109":{"countryCode":"SI","subdivisionName":"Semic","code":"SI-109"},"SI-183":{"countryCode":"SI","subdivisionName":"Sempeter-Vrtojba","code":"SI-183"},"SI-117":{"countryCode":"SI","subdivisionName":"Sencur","code":"SI-117"},"SI-118":{"countryCode":"SI","subdivisionName":"Sentilj","code":"SI-118"},"SI-119":{"countryCode":"SI","subdivisionName":"Sentjernej","code":"SI-119"},"SI-120":{"countryCode":"SI","subdivisionName":"Sentjur","code":"SI-120"},"SI-211":{"countryCode":"SI","subdivisionName":"Sentrupert","code":"SI-211"},"SI-110":{"countryCode":"SI","subdivisionName":"Sevnica","code":"SI-110"},"SI-111":{"countryCode":"SI","subdivisionName":"Sezana","code":"SI-111"},"SI-121":{"countryCode":"SI","subdivisionName":"Skocjan","code":"SI-121"},"SI-122":{"countryCode":"SI","subdivisionName":"Skofja Loka","code":"SI-122"},"SI-123":{"countryCode":"SI","subdivisionName":"Skofljica","code":"SI-123"},"SI-112":{"countryCode":"SI","subdivisionName":"Slovenj Gradec","code":"SI-112"},"SI-113":{"countryCode":"SI","subdivisionName":"Slovenska Bistrica","code":"SI-113"},"SI-114":{"countryCode":"SI","subdivisionName":"Slovenske Konjice","code":"SI-114"},"SI-124":{"countryCode":"SI","subdivisionName":"Smarje pri Jelsah","code":"SI-124"},"SI-206":{"countryCode":"SI","subdivisionName":"Smarjeske Toplice","code":"SI-206"},"SI-125":{"countryCode":"SI","subdivisionName":"Smartno ob Paki","code":"SI-125"},"SI-194":{"countryCode":"SI","subdivisionName":"Smartno pri Litiji","code":"SI-194"},"SI-179":{"countryCode":"SI","subdivisionName":"Sodrazica","code":"SI-179"},"SI-180":{"countryCode":"SI","subdivisionName":"Solcava","code":"SI-180"},"SI-126":{"countryCode":"SI","subdivisionName":"Sostanj","code":"SI-126"},"SI-115":{"countryCode":"SI","subdivisionName":"Starse","code":"SI-115"},"SI-127":{"countryCode":"SI","subdivisionName":"Store","code":"SI-127"},"SI-203":{"countryCode":"SI","subdivisionName":"Straza","code":"SI-203"},"SI-204":{"countryCode":"SI","subdivisionName":"Sveta Trojica v Slovenskih goricah","code":"SI-204"},"SI-182":{"countryCode":"SI","subdivisionName":"Sveti Andraz v Slovenskih Goricah","code":"SI-182"},"SI-116":{"countryCode":"SI","subdivisionName":"Sveti Jurij ob Scavnici","code":"SI-116"},"SI-210":{"countryCode":"SI","subdivisionName":"Sveti Jurij v Slovenskih goricah","code":"SI-210"},"SI-205":{"countryCode":"SI","subdivisionName":"Sveti Tomaz","code":"SI-205"},"SI-184":{"countryCode":"SI","subdivisionName":"Tabor","code":"SI-184"},"SI-010":{"countryCode":"SI","subdivisionName":"Tisina","code":"SI-010"},"SI-128":{"countryCode":"SI","subdivisionName":"Tolmin","code":"SI-128"},"SI-129":{"countryCode":"SI","subdivisionName":"Trbovlje","code":"SI-129"},"SI-130":{"countryCode":"SI","subdivisionName":"Trebnje","code":"SI-130"},"SI-185":{"countryCode":"SI","subdivisionName":"Trnovska Vas","code":"SI-185"},"SI-131":{"countryCode":"SI","subdivisionName":"Trzic","code":"SI-131"},"SI-186":{"countryCode":"SI","subdivisionName":"Trzin","code":"SI-186"},"SI-132":{"countryCode":"SI","subdivisionName":"Turnisce","code":"SI-132"},"SI-133":{"countryCode":"SI","subdivisionName":"Velenje","code":"SI-133"},"SI-187":{"countryCode":"SI","subdivisionName":"Velika Polana","code":"SI-187"},"SI-134":{"countryCode":"SI","subdivisionName":"Velike Lasce","code":"SI-134"},"SI-188":{"countryCode":"SI","subdivisionName":"Verzej","code":"SI-188"},"SI-135":{"countryCode":"SI","subdivisionName":"Videm","code":"SI-135"},"SI-136":{"countryCode":"SI","subdivisionName":"Vipava","code":"SI-136"},"SI-137":{"countryCode":"SI","subdivisionName":"Vitanje","code":"SI-137"},"SI-138":{"countryCode":"SI","subdivisionName":"Vodice","code":"SI-138"},"SI-139":{"countryCode":"SI","subdivisionName":"Vojnik","code":"SI-139"},"SI-189":{"countryCode":"SI","subdivisionName":"Vransko","code":"SI-189"},"SI-140":{"countryCode":"SI","subdivisionName":"Vrhnika","code":"SI-140"},"SI-141":{"countryCode":"SI","subdivisionName":"Vuzenica","code":"SI-141"},"SI-142":{"countryCode":"SI","subdivisionName":"Zagorje ob Savi","code":"SI-142"},"SI-190":{"countryCode":"SI","subdivisionName":"Zalec","code":"SI-190"},"SI-143":{"countryCode":"SI","subdivisionName":"Zavrc","code":"SI-143"},"SI-146":{"countryCode":"SI","subdivisionName":"Zelezniki","code":"SI-146"},"SI-191":{"countryCode":"SI","subdivisionName":"Zetale","code":"SI-191"},"SI-147":{"countryCode":"SI","subdivisionName":"Ziri","code":"SI-147"},"SI-144":{"countryCode":"SI","subdivisionName":"Zrece","code":"SI-144"},"SI-193":{"countryCode":"SI","subdivisionName":"Zuzemberk","code":"SI-193"},"SK-BC":{"countryCode":"SK","subdivisionName":"Banskobystricky kraj","code":"SK-BC"},"SK-BL":{"countryCode":"SK","subdivisionName":"Bratislavsky kraj","code":"SK-BL"},"SK-KI":{"countryCode":"SK","subdivisionName":"Kosicky kraj","code":"SK-KI"},"SK-NI":{"countryCode":"SK","subdivisionName":"Nitriansky kraj","code":"SK-NI"},"SK-PV":{"countryCode":"SK","subdivisionName":"Presovsky kraj","code":"SK-PV"},"SK-TC":{"countryCode":"SK","subdivisionName":"Trenciansky kraj","code":"SK-TC"},"SK-TA":{"countryCode":"SK","subdivisionName":"Trnavsky kraj","code":"SK-TA"},"SK-ZI":{"countryCode":"SK","subdivisionName":"Zilinsky kraj","code":"SK-ZI"},"SL-E":{"countryCode":"SL","subdivisionName":"Eastern","code":"SL-E"},"SL-NW":{"countryCode":"SL","subdivisionName":"North Western","code":"SL-NW"},"SL-N":{"countryCode":"SL","subdivisionName":"Northern","code":"SL-N"},"SL-S":{"countryCode":"SL","subdivisionName":"Southern","code":"SL-S"},"SL-W":{"countryCode":"SL","subdivisionName":"Western Area","code":"SL-W"},"SM-02":{"countryCode":"SM","subdivisionName":"Chiesanuova","code":"SM-02"},"SM-07":{"countryCode":"SM","subdivisionName":"Citta di San Marino","code":"SM-07"},"SM-04":{"countryCode":"SM","subdivisionName":"Faetano","code":"SM-04"},"SM-09":{"countryCode":"SM","subdivisionName":"Serravalle","code":"SM-09"},"SN-DK":{"countryCode":"SN","subdivisionName":"Dakar","code":"SN-DK"},"SN-DB":{"countryCode":"SN","subdivisionName":"Diourbel","code":"SN-DB"},"SN-FK":{"countryCode":"SN","subdivisionName":"Fatick","code":"SN-FK"},"SN-KA":{"countryCode":"SN","subdivisionName":"Kaffrine","code":"SN-KA"},"SN-KL":{"countryCode":"SN","subdivisionName":"Kaolack","code":"SN-KL"},"SN-KE":{"countryCode":"SN","subdivisionName":"Kedougou","code":"SN-KE"},"SN-KD":{"countryCode":"SN","subdivisionName":"Kolda","code":"SN-KD"},"SN-LG":{"countryCode":"SN","subdivisionName":"Louga","code":"SN-LG"},"SN-MT":{"countryCode":"SN","subdivisionName":"Matam","code":"SN-MT"},"SN-SL":{"countryCode":"SN","subdivisionName":"Saint-Louis","code":"SN-SL"},"SN-SE":{"countryCode":"SN","subdivisionName":"Sedhiou","code":"SN-SE"},"SN-TC":{"countryCode":"SN","subdivisionName":"Tambacounda","code":"SN-TC"},"SN-TH":{"countryCode":"SN","subdivisionName":"Thies","code":"SN-TH"},"SN-ZG":{"countryCode":"SN","subdivisionName":"Ziguinchor","code":"SN-ZG"},"SO-AW":{"countryCode":"SO","subdivisionName":"Awdal","code":"SO-AW"},"SO-BN":{"countryCode":"SO","subdivisionName":"Banaadir","code":"SO-BN"},"SO-BR":{"countryCode":"SO","subdivisionName":"Bari","code":"SO-BR"},"SO-BY":{"countryCode":"SO","subdivisionName":"Bay","code":"SO-BY"},"SO-GA":{"countryCode":"SO","subdivisionName":"Galguduud","code":"SO-GA"},"SO-GE":{"countryCode":"SO","subdivisionName":"Gedo","code":"SO-GE"},"SO-HI":{"countryCode":"SO","subdivisionName":"Hiiraan","code":"SO-HI"},"SO-JH":{"countryCode":"SO","subdivisionName":"Jubbada Hoose","code":"SO-JH"},"SO-MU":{"countryCode":"SO","subdivisionName":"Mudug","code":"SO-MU"},"SO-NU":{"countryCode":"SO","subdivisionName":"Nugaal","code":"SO-NU"},"SO-SO":{"countryCode":"SO","subdivisionName":"Sool","code":"SO-SO"},"SO-TO":{"countryCode":"SO","subdivisionName":"Togdheer","code":"SO-TO"},"SO-WO":{"countryCode":"SO","subdivisionName":"Woqooyi Galbeed","code":"SO-WO"},"SR-BR":{"countryCode":"SR","subdivisionName":"Brokopondo","code":"SR-BR"},"SR-CM":{"countryCode":"SR","subdivisionName":"Commewijne","code":"SR-CM"},"SR-CR":{"countryCode":"SR","subdivisionName":"Coronie","code":"SR-CR"},"SR-NI":{"countryCode":"SR","subdivisionName":"Nickerie","code":"SR-NI"},"SR-PR":{"countryCode":"SR","subdivisionName":"Para","code":"SR-PR"},"SR-PM":{"countryCode":"SR","subdivisionName":"Paramaribo","code":"SR-PM"},"SR-SA":{"countryCode":"SR","subdivisionName":"Saramacca","code":"SR-SA"},"SR-SI":{"countryCode":"SR","subdivisionName":"Sipaliwini","code":"SR-SI"},"SR-WA":{"countryCode":"SR","subdivisionName":"Wanica","code":"SR-WA"},"SS-EC":{"countryCode":"SS","subdivisionName":"Central Equatoria","code":"SS-EC"},"SS-EE":{"countryCode":"SS","subdivisionName":"Eastern Equatoria","code":"SS-EE"},"SS-BN":{"countryCode":"SS","subdivisionName":"Northern Bahr el Ghazal","code":"SS-BN"},"SS-UY":{"countryCode":"SS","subdivisionName":"Unity","code":"SS-UY"},"SS-NU":{"countryCode":"SS","subdivisionName":"Upper Nile","code":"SS-NU"},"SS-EW":{"countryCode":"SS","subdivisionName":"Western Equatoria","code":"SS-EW"},"ST-01":{"countryCode":"ST","subdivisionName":"Agua Grande","code":"ST-01"},"SV-AH":{"countryCode":"SV","subdivisionName":"Ahuachapan","code":"SV-AH"},"SV-CA":{"countryCode":"SV","subdivisionName":"Cabanas","code":"SV-CA"},"SV-CH":{"countryCode":"SV","subdivisionName":"Chalatenango","code":"SV-CH"},"SV-CU":{"countryCode":"SV","subdivisionName":"Cuscatlan","code":"SV-CU"},"SV-LI":{"countryCode":"SV","subdivisionName":"La Libertad","code":"SV-LI"},"SV-PA":{"countryCode":"SV","subdivisionName":"La Paz","code":"SV-PA"},"SV-UN":{"countryCode":"SV","subdivisionName":"La Union","code":"SV-UN"},"SV-MO":{"countryCode":"SV","subdivisionName":"Morazan","code":"SV-MO"},"SV-SM":{"countryCode":"SV","subdivisionName":"San Miguel","code":"SV-SM"},"SV-SS":{"countryCode":"SV","subdivisionName":"San Salvador","code":"SV-SS"},"SV-SV":{"countryCode":"SV","subdivisionName":"San Vicente","code":"SV-SV"},"SV-SA":{"countryCode":"SV","subdivisionName":"Santa Ana","code":"SV-SA"},"SV-SO":{"countryCode":"SV","subdivisionName":"Sonsonate","code":"SV-SO"},"SV-US":{"countryCode":"SV","subdivisionName":"Usulutan","code":"SV-US"},"SY-HA":{"countryCode":"SY","subdivisionName":"Al Hasakah","code":"SY-HA"},"SY-LA":{"countryCode":"SY","subdivisionName":"Al Ladhiqiyah","code":"SY-LA"},"SY-QU":{"countryCode":"SY","subdivisionName":"Al Qunaytirah","code":"SY-QU"},"SY-RA":{"countryCode":"SY","subdivisionName":"Ar Raqqah","code":"SY-RA"},"SY-SU":{"countryCode":"SY","subdivisionName":"As Suwayda'","code":"SY-SU"},"SY-DR":{"countryCode":"SY","subdivisionName":"Dar'a","code":"SY-DR"},"SY-DY":{"countryCode":"SY","subdivisionName":"Dayr az Zawr","code":"SY-DY"},"SY-DI":{"countryCode":"SY","subdivisionName":"Dimashq","code":"SY-DI"},"SY-HL":{"countryCode":"SY","subdivisionName":"Halab","code":"SY-HL"},"SY-HM":{"countryCode":"SY","subdivisionName":"Hamah","code":"SY-HM"},"SY-HI":{"countryCode":"SY","subdivisionName":"Hims","code":"SY-HI"},"SY-ID":{"countryCode":"SY","subdivisionName":"Idlib","code":"SY-ID"},"SY-RD":{"countryCode":"SY","subdivisionName":"Rif Dimashq","code":"SY-RD"},"SY-TA":{"countryCode":"SY","subdivisionName":"Tartus","code":"SY-TA"},"SZ-HH":{"countryCode":"SZ","subdivisionName":"Hhohho","code":"SZ-HH"},"SZ-LU":{"countryCode":"SZ","subdivisionName":"Lubombo","code":"SZ-LU"},"SZ-MA":{"countryCode":"SZ","subdivisionName":"Manzini","code":"SZ-MA"},"TD-BG":{"countryCode":"TD","subdivisionName":"Bahr el Ghazal","code":"TD-BG"},"TD-CB":{"countryCode":"TD","subdivisionName":"Chari-Baguirmi","code":"TD-CB"},"TD-GR":{"countryCode":"TD","subdivisionName":"Guera","code":"TD-GR"},"TD-LC":{"countryCode":"TD","subdivisionName":"Lac","code":"TD-LC"},"TD-LR":{"countryCode":"TD","subdivisionName":"Logone-Oriental","code":"TD-LR"},"TD-OD":{"countryCode":"TD","subdivisionName":"Ouaddai","code":"TD-OD"},"TD-ND":{"countryCode":"TD","subdivisionName":"Ville de Ndjamena","code":"TD-ND"},"TG-C":{"countryCode":"TG","subdivisionName":"Centrale","code":"TG-C"},"TG-K":{"countryCode":"TG","subdivisionName":"Kara","code":"TG-K"},"TG-M":{"countryCode":"TG","subdivisionName":"Maritime","code":"TG-M"},"TG-P":{"countryCode":"TG","subdivisionName":"Plateaux","code":"TG-P"},"TG-S":{"countryCode":"TG","subdivisionName":"Savanes","code":"TG-S"},"TH-37":{"countryCode":"TH","subdivisionName":"Amnat Charoen","code":"TH-37"},"TH-15":{"countryCode":"TH","subdivisionName":"Ang Thong","code":"TH-15"},"TH-38":{"countryCode":"TH","subdivisionName":"Bueng Kan","code":"TH-38"},"TH-31":{"countryCode":"TH","subdivisionName":"Buri Ram","code":"TH-31"},"TH-24":{"countryCode":"TH","subdivisionName":"Chachoengsao","code":"TH-24"},"TH-18":{"countryCode":"TH","subdivisionName":"Chai Nat","code":"TH-18"},"TH-36":{"countryCode":"TH","subdivisionName":"Chaiyaphum","code":"TH-36"},"TH-22":{"countryCode":"TH","subdivisionName":"Chanthaburi","code":"TH-22"},"TH-50":{"countryCode":"TH","subdivisionName":"Chiang Mai","code":"TH-50"},"TH-57":{"countryCode":"TH","subdivisionName":"Chiang Rai","code":"TH-57"},"TH-20":{"countryCode":"TH","subdivisionName":"Chon Buri","code":"TH-20"},"TH-86":{"countryCode":"TH","subdivisionName":"Chumphon","code":"TH-86"},"TH-46":{"countryCode":"TH","subdivisionName":"Kalasin","code":"TH-46"},"TH-62":{"countryCode":"TH","subdivisionName":"Kamphaeng Phet","code":"TH-62"},"TH-71":{"countryCode":"TH","subdivisionName":"Kanchanaburi","code":"TH-71"},"TH-40":{"countryCode":"TH","subdivisionName":"Khon Kaen","code":"TH-40"},"TH-81":{"countryCode":"TH","subdivisionName":"Krabi","code":"TH-81"},"TH-10":{"countryCode":"TH","subdivisionName":"Krung Thep Maha Nakhon","code":"TH-10"},"TH-52":{"countryCode":"TH","subdivisionName":"Lampang","code":"TH-52"},"TH-51":{"countryCode":"TH","subdivisionName":"Lamphun","code":"TH-51"},"TH-42":{"countryCode":"TH","subdivisionName":"Loei","code":"TH-42"},"TH-16":{"countryCode":"TH","subdivisionName":"Lop Buri","code":"TH-16"},"TH-58":{"countryCode":"TH","subdivisionName":"Mae Hong Son","code":"TH-58"},"TH-44":{"countryCode":"TH","subdivisionName":"Maha Sarakham","code":"TH-44"},"TH-49":{"countryCode":"TH","subdivisionName":"Mukdahan","code":"TH-49"},"TH-26":{"countryCode":"TH","subdivisionName":"Nakhon Nayok","code":"TH-26"},"TH-73":{"countryCode":"TH","subdivisionName":"Nakhon Pathom","code":"TH-73"},"TH-48":{"countryCode":"TH","subdivisionName":"Nakhon Phanom","code":"TH-48"},"TH-30":{"countryCode":"TH","subdivisionName":"Nakhon Ratchasima","code":"TH-30"},"TH-60":{"countryCode":"TH","subdivisionName":"Nakhon Sawan","code":"TH-60"},"TH-80":{"countryCode":"TH","subdivisionName":"Nakhon Si Thammarat","code":"TH-80"},"TH-55":{"countryCode":"TH","subdivisionName":"Nan","code":"TH-55"},"TH-96":{"countryCode":"TH","subdivisionName":"Narathiwat","code":"TH-96"},"TH-39":{"countryCode":"TH","subdivisionName":"Nong Bua Lam Phu","code":"TH-39"},"TH-43":{"countryCode":"TH","subdivisionName":"Nong Khai","code":"TH-43"},"TH-12":{"countryCode":"TH","subdivisionName":"Nonthaburi","code":"TH-12"},"TH-13":{"countryCode":"TH","subdivisionName":"Pathum Thani","code":"TH-13"},"TH-94":{"countryCode":"TH","subdivisionName":"Pattani","code":"TH-94"},"TH-82":{"countryCode":"TH","subdivisionName":"Phangnga","code":"TH-82"},"TH-93":{"countryCode":"TH","subdivisionName":"Phatthalung","code":"TH-93"},"TH-56":{"countryCode":"TH","subdivisionName":"Phayao","code":"TH-56"},"TH-67":{"countryCode":"TH","subdivisionName":"Phetchabun","code":"TH-67"},"TH-76":{"countryCode":"TH","subdivisionName":"Phetchaburi","code":"TH-76"},"TH-66":{"countryCode":"TH","subdivisionName":"Phichit","code":"TH-66"},"TH-65":{"countryCode":"TH","subdivisionName":"Phitsanulok","code":"TH-65"},"TH-14":{"countryCode":"TH","subdivisionName":"Phra Nakhon Si Ayutthaya","code":"TH-14"},"TH-54":{"countryCode":"TH","subdivisionName":"Phrae","code":"TH-54"},"TH-83":{"countryCode":"TH","subdivisionName":"Phuket","code":"TH-83"},"TH-25":{"countryCode":"TH","subdivisionName":"Prachin Buri","code":"TH-25"},"TH-77":{"countryCode":"TH","subdivisionName":"Prachuap Khiri Khan","code":"TH-77"},"TH-85":{"countryCode":"TH","subdivisionName":"Ranong","code":"TH-85"},"TH-70":{"countryCode":"TH","subdivisionName":"Ratchaburi","code":"TH-70"},"TH-21":{"countryCode":"TH","subdivisionName":"Rayong","code":"TH-21"},"TH-45":{"countryCode":"TH","subdivisionName":"Roi Et","code":"TH-45"},"TH-27":{"countryCode":"TH","subdivisionName":"Sa Kaeo","code":"TH-27"},"TH-47":{"countryCode":"TH","subdivisionName":"Sakon Nakhon","code":"TH-47"},"TH-11":{"countryCode":"TH","subdivisionName":"Samut Prakan","code":"TH-11"},"TH-74":{"countryCode":"TH","subdivisionName":"Samut Sakhon","code":"TH-74"},"TH-75":{"countryCode":"TH","subdivisionName":"Samut Songkhram","code":"TH-75"},"TH-19":{"countryCode":"TH","subdivisionName":"Saraburi","code":"TH-19"},"TH-91":{"countryCode":"TH","subdivisionName":"Satun","code":"TH-91"},"TH-33":{"countryCode":"TH","subdivisionName":"Si Sa Ket","code":"TH-33"},"TH-17":{"countryCode":"TH","subdivisionName":"Sing Buri","code":"TH-17"},"TH-90":{"countryCode":"TH","subdivisionName":"Songkhla","code":"TH-90"},"TH-64":{"countryCode":"TH","subdivisionName":"Sukhothai","code":"TH-64"},"TH-72":{"countryCode":"TH","subdivisionName":"Suphan Buri","code":"TH-72"},"TH-84":{"countryCode":"TH","subdivisionName":"Surat Thani","code":"TH-84"},"TH-32":{"countryCode":"TH","subdivisionName":"Surin","code":"TH-32"},"TH-63":{"countryCode":"TH","subdivisionName":"Tak","code":"TH-63"},"TH-92":{"countryCode":"TH","subdivisionName":"Trang","code":"TH-92"},"TH-23":{"countryCode":"TH","subdivisionName":"Trat","code":"TH-23"},"TH-34":{"countryCode":"TH","subdivisionName":"Ubon Ratchathani","code":"TH-34"},"TH-41":{"countryCode":"TH","subdivisionName":"Udon Thani","code":"TH-41"},"TH-61":{"countryCode":"TH","subdivisionName":"Uthai Thani","code":"TH-61"},"TH-53":{"countryCode":"TH","subdivisionName":"Uttaradit","code":"TH-53"},"TH-95":{"countryCode":"TH","subdivisionName":"Yala","code":"TH-95"},"TH-35":{"countryCode":"TH","subdivisionName":"Yasothon","code":"TH-35"},"TJ-DU":{"countryCode":"TJ","subdivisionName":"Dushanbe","code":"TJ-DU"},"TJ-KT":{"countryCode":"TJ","subdivisionName":"Khatlon","code":"TJ-KT"},"TJ-GB":{"countryCode":"TJ","subdivisionName":"Kuhistoni Badakhshon","code":"TJ-GB"},"TJ-RA":{"countryCode":"TJ","subdivisionName":"Nohiyahoi Tobei Jumhuri","code":"TJ-RA"},"TJ-SU":{"countryCode":"TJ","subdivisionName":"Sughd","code":"TJ-SU"},"TL-AL":{"countryCode":"TL","subdivisionName":"Aileu","code":"TL-AL"},"TL-AN":{"countryCode":"TL","subdivisionName":"Ainaro","code":"TL-AN"},"TL-CO":{"countryCode":"TL","subdivisionName":"Cova Lima","code":"TL-CO"},"TL-DI":{"countryCode":"TL","subdivisionName":"Dili","code":"TL-DI"},"TL-LI":{"countryCode":"TL","subdivisionName":"Liquica","code":"TL-LI"},"TM-A":{"countryCode":"TM","subdivisionName":"Ahal","code":"TM-A"},"TM-B":{"countryCode":"TM","subdivisionName":"Balkan","code":"TM-B"},"TM-D":{"countryCode":"TM","subdivisionName":"Dasoguz","code":"TM-D"},"TM-L":{"countryCode":"TM","subdivisionName":"Lebap","code":"TM-L"},"TM-M":{"countryCode":"TM","subdivisionName":"Mary","code":"TM-M"},"TN-31":{"countryCode":"TN","subdivisionName":"Beja","code":"TN-31"},"TN-13":{"countryCode":"TN","subdivisionName":"Ben Arous","code":"TN-13"},"TN-23":{"countryCode":"TN","subdivisionName":"Bizerte","code":"TN-23"},"TN-81":{"countryCode":"TN","subdivisionName":"Gabes","code":"TN-81"},"TN-71":{"countryCode":"TN","subdivisionName":"Gafsa","code":"TN-71"},"TN-32":{"countryCode":"TN","subdivisionName":"Jendouba","code":"TN-32"},"TN-41":{"countryCode":"TN","subdivisionName":"Kairouan","code":"TN-41"},"TN-42":{"countryCode":"TN","subdivisionName":"Kasserine","code":"TN-42"},"TN-73":{"countryCode":"TN","subdivisionName":"Kebili","code":"TN-73"},"TN-12":{"countryCode":"TN","subdivisionName":"L'Ariana","code":"TN-12"},"TN-14":{"countryCode":"TN","subdivisionName":"La Manouba","code":"TN-14"},"TN-33":{"countryCode":"TN","subdivisionName":"Le Kef","code":"TN-33"},"TN-53":{"countryCode":"TN","subdivisionName":"Mahdia","code":"TN-53"},"TN-82":{"countryCode":"TN","subdivisionName":"Medenine","code":"TN-82"},"TN-52":{"countryCode":"TN","subdivisionName":"Monastir","code":"TN-52"},"TN-21":{"countryCode":"TN","subdivisionName":"Nabeul","code":"TN-21"},"TN-61":{"countryCode":"TN","subdivisionName":"Sfax","code":"TN-61"},"TN-43":{"countryCode":"TN","subdivisionName":"Sidi Bouzid","code":"TN-43"},"TN-34":{"countryCode":"TN","subdivisionName":"Siliana","code":"TN-34"},"TN-51":{"countryCode":"TN","subdivisionName":"Sousse","code":"TN-51"},"TN-83":{"countryCode":"TN","subdivisionName":"Tataouine","code":"TN-83"},"TN-72":{"countryCode":"TN","subdivisionName":"Tozeur","code":"TN-72"},"TN-11":{"countryCode":"TN","subdivisionName":"Tunis","code":"TN-11"},"TN-22":{"countryCode":"TN","subdivisionName":"Zaghouan","code":"TN-22"},"TO-01":{"countryCode":"TO","subdivisionName":"'Eua","code":"TO-01"},"TO-02":{"countryCode":"TO","subdivisionName":"Ha'apai","code":"TO-02"},"TO-03":{"countryCode":"TO","subdivisionName":"Niuas","code":"TO-03"},"TO-04":{"countryCode":"TO","subdivisionName":"Tongatapu","code":"TO-04"},"TO-05":{"countryCode":"TO","subdivisionName":"Vava'u","code":"TO-05"},"TR-01":{"countryCode":"TR","subdivisionName":"Adana","code":"TR-01"},"TR-02":{"countryCode":"TR","subdivisionName":"Adiyaman","code":"TR-02"},"TR-03":{"countryCode":"TR","subdivisionName":"Afyonkarahisar","code":"TR-03"},"TR-04":{"countryCode":"TR","subdivisionName":"Agri","code":"TR-04"},"TR-68":{"countryCode":"TR","subdivisionName":"Aksaray","code":"TR-68"},"TR-05":{"countryCode":"TR","subdivisionName":"Amasya","code":"TR-05"},"TR-06":{"countryCode":"TR","subdivisionName":"Ankara","code":"TR-06"},"TR-07":{"countryCode":"TR","subdivisionName":"Antalya","code":"TR-07"},"TR-75":{"countryCode":"TR","subdivisionName":"Ardahan","code":"TR-75"},"TR-08":{"countryCode":"TR","subdivisionName":"Artvin","code":"TR-08"},"TR-09":{"countryCode":"TR","subdivisionName":"Aydin","code":"TR-09"},"TR-10":{"countryCode":"TR","subdivisionName":"Balikesir","code":"TR-10"},"TR-74":{"countryCode":"TR","subdivisionName":"Bartin","code":"TR-74"},"TR-72":{"countryCode":"TR","subdivisionName":"Batman","code":"TR-72"},"TR-69":{"countryCode":"TR","subdivisionName":"Bayburt","code":"TR-69"},"TR-11":{"countryCode":"TR","subdivisionName":"Bilecik","code":"TR-11"},"TR-12":{"countryCode":"TR","subdivisionName":"Bingol","code":"TR-12"},"TR-13":{"countryCode":"TR","subdivisionName":"Bitlis","code":"TR-13"},"TR-14":{"countryCode":"TR","subdivisionName":"Bolu","code":"TR-14"},"TR-15":{"countryCode":"TR","subdivisionName":"Burdur","code":"TR-15"},"TR-16":{"countryCode":"TR","subdivisionName":"Bursa","code":"TR-16"},"TR-17":{"countryCode":"TR","subdivisionName":"Canakkale","code":"TR-17"},"TR-18":{"countryCode":"TR","subdivisionName":"Cankiri","code":"TR-18"},"TR-19":{"countryCode":"TR","subdivisionName":"Corum","code":"TR-19"},"TR-20":{"countryCode":"TR","subdivisionName":"Denizli","code":"TR-20"},"TR-21":{"countryCode":"TR","subdivisionName":"Diyarbakir","code":"TR-21"},"TR-81":{"countryCode":"TR","subdivisionName":"Duzce","code":"TR-81"},"TR-22":{"countryCode":"TR","subdivisionName":"Edirne","code":"TR-22"},"TR-23":{"countryCode":"TR","subdivisionName":"Elazig","code":"TR-23"},"TR-24":{"countryCode":"TR","subdivisionName":"Erzincan","code":"TR-24"},"TR-25":{"countryCode":"TR","subdivisionName":"Erzurum","code":"TR-25"},"TR-26":{"countryCode":"TR","subdivisionName":"Eskisehir","code":"TR-26"},"TR-27":{"countryCode":"TR","subdivisionName":"Gaziantep","code":"TR-27"},"TR-28":{"countryCode":"TR","subdivisionName":"Giresun","code":"TR-28"},"TR-29":{"countryCode":"TR","subdivisionName":"Gumushane","code":"TR-29"},"TR-30":{"countryCode":"TR","subdivisionName":"Hakkari","code":"TR-30"},"TR-31":{"countryCode":"TR","subdivisionName":"Hatay","code":"TR-31"},"TR-76":{"countryCode":"TR","subdivisionName":"Igdir","code":"TR-76"},"TR-32":{"countryCode":"TR","subdivisionName":"Isparta","code":"TR-32"},"TR-34":{"countryCode":"TR","subdivisionName":"Istanbul","code":"TR-34"},"TR-35":{"countryCode":"TR","subdivisionName":"Izmir","code":"TR-35"},"TR-46":{"countryCode":"TR","subdivisionName":"Kahramanmaras","code":"TR-46"},"TR-78":{"countryCode":"TR","subdivisionName":"Karabuk","code":"TR-78"},"TR-70":{"countryCode":"TR","subdivisionName":"Karaman","code":"TR-70"},"TR-36":{"countryCode":"TR","subdivisionName":"Kars","code":"TR-36"},"TR-37":{"countryCode":"TR","subdivisionName":"Kastamonu","code":"TR-37"},"TR-38":{"countryCode":"TR","subdivisionName":"Kayseri","code":"TR-38"},"TR-79":{"countryCode":"TR","subdivisionName":"Kilis","code":"TR-79"},"TR-71":{"countryCode":"TR","subdivisionName":"Kirikkale","code":"TR-71"},"TR-39":{"countryCode":"TR","subdivisionName":"Kirklareli","code":"TR-39"},"TR-40":{"countryCode":"TR","subdivisionName":"Kirsehir","code":"TR-40"},"TR-41":{"countryCode":"TR","subdivisionName":"Kocaeli","code":"TR-41"},"TR-42":{"countryCode":"TR","subdivisionName":"Konya","code":"TR-42"},"TR-43":{"countryCode":"TR","subdivisionName":"Kutahya","code":"TR-43"},"TR-44":{"countryCode":"TR","subdivisionName":"Malatya","code":"TR-44"},"TR-45":{"countryCode":"TR","subdivisionName":"Manisa","code":"TR-45"},"TR-47":{"countryCode":"TR","subdivisionName":"Mardin","code":"TR-47"},"TR-33":{"countryCode":"TR","subdivisionName":"Mersin","code":"TR-33"},"TR-48":{"countryCode":"TR","subdivisionName":"Mugla","code":"TR-48"},"TR-49":{"countryCode":"TR","subdivisionName":"Mus","code":"TR-49"},"TR-50":{"countryCode":"TR","subdivisionName":"Nevsehir","code":"TR-50"},"TR-51":{"countryCode":"TR","subdivisionName":"Nigde","code":"TR-51"},"TR-52":{"countryCode":"TR","subdivisionName":"Ordu","code":"TR-52"},"TR-80":{"countryCode":"TR","subdivisionName":"Osmaniye","code":"TR-80"},"TR-53":{"countryCode":"TR","subdivisionName":"Rize","code":"TR-53"},"TR-54":{"countryCode":"TR","subdivisionName":"Sakarya","code":"TR-54"},"TR-55":{"countryCode":"TR","subdivisionName":"Samsun","code":"TR-55"},"TR-63":{"countryCode":"TR","subdivisionName":"Sanliurfa","code":"TR-63"},"TR-56":{"countryCode":"TR","subdivisionName":"Siirt","code":"TR-56"},"TR-57":{"countryCode":"TR","subdivisionName":"Sinop","code":"TR-57"},"TR-73":{"countryCode":"TR","subdivisionName":"Sirnak","code":"TR-73"},"TR-58":{"countryCode":"TR","subdivisionName":"Sivas","code":"TR-58"},"TR-59":{"countryCode":"TR","subdivisionName":"Tekirdag","code":"TR-59"},"TR-60":{"countryCode":"TR","subdivisionName":"Tokat","code":"TR-60"},"TR-61":{"countryCode":"TR","subdivisionName":"Trabzon","code":"TR-61"},"TR-62":{"countryCode":"TR","subdivisionName":"Tunceli","code":"TR-62"},"TR-64":{"countryCode":"TR","subdivisionName":"Usak","code":"TR-64"},"TR-65":{"countryCode":"TR","subdivisionName":"Van","code":"TR-65"},"TR-77":{"countryCode":"TR","subdivisionName":"Yalova","code":"TR-77"},"TR-66":{"countryCode":"TR","subdivisionName":"Yozgat","code":"TR-66"},"TR-67":{"countryCode":"TR","subdivisionName":"Zonguldak","code":"TR-67"},"TT-ARI":{"countryCode":"TT","subdivisionName":"Arima","code":"TT-ARI"},"TT-CHA":{"countryCode":"TT","subdivisionName":"Chaguanas","code":"TT-CHA"},"TT-CTT":{"countryCode":"TT","subdivisionName":"Couva-Tabaquite-Talparo","code":"TT-CTT"},"TT-DMN":{"countryCode":"TT","subdivisionName":"Diego Martin","code":"TT-DMN"},"TT-MRC":{"countryCode":"TT","subdivisionName":"Mayaro-Rio Claro","code":"TT-MRC"},"TT-PED":{"countryCode":"TT","subdivisionName":"Penal-Debe","code":"TT-PED"},"TT-PTF":{"countryCode":"TT","subdivisionName":"Point Fortin","code":"TT-PTF"},"TT-POS":{"countryCode":"TT","subdivisionName":"Port of Spain","code":"TT-POS"},"TT-PRT":{"countryCode":"TT","subdivisionName":"Princes Town","code":"TT-PRT"},"TT-SFO":{"countryCode":"TT","subdivisionName":"San Fernando","code":"TT-SFO"},"TT-SJL":{"countryCode":"TT","subdivisionName":"San Juan-Laventille","code":"TT-SJL"},"TT-SGE":{"countryCode":"TT","subdivisionName":"Sangre Grande","code":"TT-SGE"},"TT-SIP":{"countryCode":"TT","subdivisionName":"Siparia","code":"TT-SIP"},"TT-TOB":{"countryCode":"TT","subdivisionName":"Tobago","code":"TT-TOB"},"TT-TUP":{"countryCode":"TT","subdivisionName":"Tunapuna-Piarco","code":"TT-TUP"},"TV-FUN":{"countryCode":"TV","subdivisionName":"Funafuti","code":"TV-FUN"},"TW-CHA":{"countryCode":"TW","subdivisionName":"Changhua","code":"TW-CHA"},"TW-CYQ":{"countryCode":"TW","subdivisionName":"Chiayi","code":"TW-CYQ"},"TW-HSQ":{"countryCode":"TW","subdivisionName":"Hsinchu","code":"TW-HSQ"},"TW-HUA":{"countryCode":"TW","subdivisionName":"Hualien","code":"TW-HUA"},"TW-KHH":{"countryCode":"TW","subdivisionName":"Kaohsiung","code":"TW-KHH"},"TW-KEE":{"countryCode":"TW","subdivisionName":"Keelung","code":"TW-KEE"},"TW-KIN":{"countryCode":"TW","subdivisionName":"Kinmen","code":"TW-KIN"},"TW-LIE":{"countryCode":"TW","subdivisionName":"Lienchiang","code":"TW-LIE"},"TW-MIA":{"countryCode":"TW","subdivisionName":"Miaoli","code":"TW-MIA"},"TW-NAN":{"countryCode":"TW","subdivisionName":"Nantou","code":"TW-NAN"},"TW-NWT":{"countryCode":"TW","subdivisionName":"New Taipei","code":"TW-NWT"},"TW-PEN":{"countryCode":"TW","subdivisionName":"Penghu","code":"TW-PEN"},"TW-PIF":{"countryCode":"TW","subdivisionName":"Pingtung","code":"TW-PIF"},"TW-TXG":{"countryCode":"TW","subdivisionName":"Taichung","code":"TW-TXG"},"TW-TNN":{"countryCode":"TW","subdivisionName":"Tainan","code":"TW-TNN"},"TW-TPE":{"countryCode":"TW","subdivisionName":"Taipei","code":"TW-TPE"},"TW-TTT":{"countryCode":"TW","subdivisionName":"Taitung","code":"TW-TTT"},"TW-TAO":{"countryCode":"TW","subdivisionName":"Taoyuan","code":"TW-TAO"},"TW-ILA":{"countryCode":"TW","subdivisionName":"Yilan","code":"TW-ILA"},"TW-YUN":{"countryCode":"TW","subdivisionName":"Yunlin","code":"TW-YUN"},"TZ-01":{"countryCode":"TZ","subdivisionName":"Arusha","code":"TZ-01"},"TZ-02":{"countryCode":"TZ","subdivisionName":"Dar es Salaam","code":"TZ-02"},"TZ-03":{"countryCode":"TZ","subdivisionName":"Dodoma","code":"TZ-03"},"TZ-27":{"countryCode":"TZ","subdivisionName":"Geita","code":"TZ-27"},"TZ-04":{"countryCode":"TZ","subdivisionName":"Iringa","code":"TZ-04"},"TZ-05":{"countryCode":"TZ","subdivisionName":"Kagera","code":"TZ-05"},"TZ-06":{"countryCode":"TZ","subdivisionName":"Kaskazini Pemba","code":"TZ-06"},"TZ-07":{"countryCode":"TZ","subdivisionName":"Kaskazini Unguja","code":"TZ-07"},"TZ-28":{"countryCode":"TZ","subdivisionName":"Katavi","code":"TZ-28"},"TZ-08":{"countryCode":"TZ","subdivisionName":"Kigoma","code":"TZ-08"},"TZ-09":{"countryCode":"TZ","subdivisionName":"Kilimanjaro","code":"TZ-09"},"TZ-10":{"countryCode":"TZ","subdivisionName":"Kusini Pemba","code":"TZ-10"},"TZ-11":{"countryCode":"TZ","subdivisionName":"Kusini Unguja","code":"TZ-11"},"TZ-12":{"countryCode":"TZ","subdivisionName":"Lindi","code":"TZ-12"},"TZ-26":{"countryCode":"TZ","subdivisionName":"Manyara","code":"TZ-26"},"TZ-13":{"countryCode":"TZ","subdivisionName":"Mara","code":"TZ-13"},"TZ-14":{"countryCode":"TZ","subdivisionName":"Mbeya","code":"TZ-14"},"TZ-15":{"countryCode":"TZ","subdivisionName":"Mjini Magharibi","code":"TZ-15"},"TZ-16":{"countryCode":"TZ","subdivisionName":"Morogoro","code":"TZ-16"},"TZ-17":{"countryCode":"TZ","subdivisionName":"Mtwara","code":"TZ-17"},"TZ-18":{"countryCode":"TZ","subdivisionName":"Mwanza","code":"TZ-18"},"TZ-29":{"countryCode":"TZ","subdivisionName":"Njombe","code":"TZ-29"},"TZ-19":{"countryCode":"TZ","subdivisionName":"Pwani","code":"TZ-19"},"TZ-20":{"countryCode":"TZ","subdivisionName":"Rukwa","code":"TZ-20"},"TZ-21":{"countryCode":"TZ","subdivisionName":"Ruvuma","code":"TZ-21"},"TZ-22":{"countryCode":"TZ","subdivisionName":"Shinyanga","code":"TZ-22"},"TZ-30":{"countryCode":"TZ","subdivisionName":"Simiyu","code":"TZ-30"},"TZ-23":{"countryCode":"TZ","subdivisionName":"Singida","code":"TZ-23"},"TZ-31":{"countryCode":"TZ","subdivisionName":"Songwe","code":"TZ-31"},"TZ-24":{"countryCode":"TZ","subdivisionName":"Tabora","code":"TZ-24"},"TZ-25":{"countryCode":"TZ","subdivisionName":"Tanga","code":"TZ-25"},"UA-43":{"countryCode":"UA","subdivisionName":"Avtonomna Respublika Krym","code":"UA-43"},"UA-71":{"countryCode":"UA","subdivisionName":"Cherkaska oblast","code":"UA-71"},"UA-74":{"countryCode":"UA","subdivisionName":"Chernihivska oblast","code":"UA-74"},"UA-77":{"countryCode":"UA","subdivisionName":"Chernivetska oblast","code":"UA-77"},"UA-12":{"countryCode":"UA","subdivisionName":"Dnipropetrovska oblast","code":"UA-12"},"UA-14":{"countryCode":"UA","subdivisionName":"Donetska oblast","code":"UA-14"},"UA-26":{"countryCode":"UA","subdivisionName":"Ivano-Frankivska oblast","code":"UA-26"},"UA-63":{"countryCode":"UA","subdivisionName":"Kharkivska oblast","code":"UA-63"},"UA-65":{"countryCode":"UA","subdivisionName":"Khersonska oblast","code":"UA-65"},"UA-68":{"countryCode":"UA","subdivisionName":"Khmelnytska oblast","code":"UA-68"},"UA-35":{"countryCode":"UA","subdivisionName":"Kirovohradska oblast","code":"UA-35"},"UA-30":{"countryCode":"UA","subdivisionName":"Kyiv","code":"UA-30"},"UA-32":{"countryCode":"UA","subdivisionName":"Kyivska oblast","code":"UA-32"},"UA-09":{"countryCode":"UA","subdivisionName":"Luhanska oblast","code":"UA-09"},"UA-46":{"countryCode":"UA","subdivisionName":"Lvivska oblast","code":"UA-46"},"UA-48":{"countryCode":"UA","subdivisionName":"Mykolaivska oblast","code":"UA-48"},"UA-51":{"countryCode":"UA","subdivisionName":"Odeska oblast","code":"UA-51"},"UA-53":{"countryCode":"UA","subdivisionName":"Poltavska oblast","code":"UA-53"},"UA-56":{"countryCode":"UA","subdivisionName":"Rivnenska oblast","code":"UA-56"},"UA-40":{"countryCode":"UA","subdivisionName":"Sevastopol","code":"UA-40"},"UA-59":{"countryCode":"UA","subdivisionName":"Sumska oblast","code":"UA-59"},"UA-61":{"countryCode":"UA","subdivisionName":"Ternopilska oblast","code":"UA-61"},"UA-05":{"countryCode":"UA","subdivisionName":"Vinnytska oblast","code":"UA-05"},"UA-07":{"countryCode":"UA","subdivisionName":"Volynska oblast","code":"UA-07"},"UA-21":{"countryCode":"UA","subdivisionName":"Zakarpatska oblast","code":"UA-21"},"UA-23":{"countryCode":"UA","subdivisionName":"Zaporizka oblast","code":"UA-23"},"UA-18":{"countryCode":"UA","subdivisionName":"Zhytomyrska oblast","code":"UA-18"},"UG-314":{"countryCode":"UG","subdivisionName":"Abim","code":"UG-314"},"UG-301":{"countryCode":"UG","subdivisionName":"Adjumani","code":"UG-301"},"UG-322":{"countryCode":"UG","subdivisionName":"Agago","code":"UG-322"},"UG-323":{"countryCode":"UG","subdivisionName":"Alebtong","code":"UG-323"},"UG-302":{"countryCode":"UG","subdivisionName":"Apac","code":"UG-302"},"UG-303":{"countryCode":"UG","subdivisionName":"Arua","code":"UG-303"},"UG-218":{"countryCode":"UG","subdivisionName":"Bududa","code":"UG-218"},"UG-201":{"countryCode":"UG","subdivisionName":"Bugiri","code":"UG-201"},"UG-420":{"countryCode":"UG","subdivisionName":"Buhweju","code":"UG-420"},"UG-117":{"countryCode":"UG","subdivisionName":"Buikwe","code":"UG-117"},"UG-219":{"countryCode":"UG","subdivisionName":"Bukedea","code":"UG-219"},"UG-118":{"countryCode":"UG","subdivisionName":"Bukomansibi","code":"UG-118"},"UG-225":{"countryCode":"UG","subdivisionName":"Bulambuli","code":"UG-225"},"UG-401":{"countryCode":"UG","subdivisionName":"Bundibugyo","code":"UG-401"},"UG-402":{"countryCode":"UG","subdivisionName":"Bushenyi","code":"UG-402"},"UG-202":{"countryCode":"UG","subdivisionName":"Busia","code":"UG-202"},"UG-120":{"countryCode":"UG","subdivisionName":"Buvuma","code":"UG-120"},"UG-226":{"countryCode":"UG","subdivisionName":"Buyende","code":"UG-226"},"UG-121":{"countryCode":"UG","subdivisionName":"Gomba","code":"UG-121"},"UG-304":{"countryCode":"UG","subdivisionName":"Gulu","code":"UG-304"},"UG-403":{"countryCode":"UG","subdivisionName":"Hoima","code":"UG-403"},"UG-417":{"countryCode":"UG","subdivisionName":"Ibanda","code":"UG-417"},"UG-203":{"countryCode":"UG","subdivisionName":"Iganga","code":"UG-203"},"UG-418":{"countryCode":"UG","subdivisionName":"Isingiro","code":"UG-418"},"UG-204":{"countryCode":"UG","subdivisionName":"Jinja","code":"UG-204"},"UG-318":{"countryCode":"UG","subdivisionName":"Kaabong","code":"UG-318"},"UG-404":{"countryCode":"UG","subdivisionName":"Kabale","code":"UG-404"},"UG-405":{"countryCode":"UG","subdivisionName":"Kabarole","code":"UG-405"},"UG-213":{"countryCode":"UG","subdivisionName":"Kaberamaido","code":"UG-213"},"UG-101":{"countryCode":"UG","subdivisionName":"Kalangala","code":"UG-101"},"UG-222":{"countryCode":"UG","subdivisionName":"Kaliro","code":"UG-222"},"UG-122":{"countryCode":"UG","subdivisionName":"Kalungu","code":"UG-122"},"UG-102":{"countryCode":"UG","subdivisionName":"Kampala","code":"UG-102"},"UG-205":{"countryCode":"UG","subdivisionName":"Kamuli","code":"UG-205"},"UG-413":{"countryCode":"UG","subdivisionName":"Kamwenge","code":"UG-413"},"UG-414":{"countryCode":"UG","subdivisionName":"Kanungu","code":"UG-414"},"UG-206":{"countryCode":"UG","subdivisionName":"Kapchorwa","code":"UG-206"},"UG-406":{"countryCode":"UG","subdivisionName":"Kasese","code":"UG-406"},"UG-207":{"countryCode":"UG","subdivisionName":"Katakwi","code":"UG-207"},"UG-112":{"countryCode":"UG","subdivisionName":"Kayunga","code":"UG-112"},"UG-407":{"countryCode":"UG","subdivisionName":"Kibaale","code":"UG-407"},"UG-103":{"countryCode":"UG","subdivisionName":"Kiboga","code":"UG-103"},"UG-227":{"countryCode":"UG","subdivisionName":"Kibuku","code":"UG-227"},"UG-419":{"countryCode":"UG","subdivisionName":"Kiruhura","code":"UG-419"},"UG-421":{"countryCode":"UG","subdivisionName":"Kiryandongo","code":"UG-421"},"UG-408":{"countryCode":"UG","subdivisionName":"Kisoro","code":"UG-408"},"UG-305":{"countryCode":"UG","subdivisionName":"Kitgum","code":"UG-305"},"UG-319":{"countryCode":"UG","subdivisionName":"Koboko","code":"UG-319"},"UG-325":{"countryCode":"UG","subdivisionName":"Kole","code":"UG-325"},"UG-228":{"countryCode":"UG","subdivisionName":"Kween","code":"UG-228"},"UG-123":{"countryCode":"UG","subdivisionName":"Kyankwanzi","code":"UG-123"},"UG-422":{"countryCode":"UG","subdivisionName":"Kyegegwa","code":"UG-422"},"UG-415":{"countryCode":"UG","subdivisionName":"Kyenjojo","code":"UG-415"},"UG-326":{"countryCode":"UG","subdivisionName":"Lamwo","code":"UG-326"},"UG-307":{"countryCode":"UG","subdivisionName":"Lira","code":"UG-307"},"UG-229":{"countryCode":"UG","subdivisionName":"Luuka","code":"UG-229"},"UG-104":{"countryCode":"UG","subdivisionName":"Luwero","code":"UG-104"},"UG-124":{"countryCode":"UG","subdivisionName":"Lwengo","code":"UG-124"},"UG-114":{"countryCode":"UG","subdivisionName":"Lyantonde","code":"UG-114"},"UG-223":{"countryCode":"UG","subdivisionName":"Manafwa","code":"UG-223"},"UG-320":{"countryCode":"UG","subdivisionName":"Maracha","code":"UG-320"},"UG-105":{"countryCode":"UG","subdivisionName":"Masaka","code":"UG-105"},"UG-409":{"countryCode":"UG","subdivisionName":"Masindi","code":"UG-409"},"UG-214":{"countryCode":"UG","subdivisionName":"Mayuge","code":"UG-214"},"UG-209":{"countryCode":"UG","subdivisionName":"Mbale","code":"UG-209"},"UG-410":{"countryCode":"UG","subdivisionName":"Mbarara","code":"UG-410"},"UG-423":{"countryCode":"UG","subdivisionName":"Mitooma","code":"UG-423"},"UG-115":{"countryCode":"UG","subdivisionName":"Mityana","code":"UG-115"},"UG-308":{"countryCode":"UG","subdivisionName":"Moroto","code":"UG-308"},"UG-309":{"countryCode":"UG","subdivisionName":"Moyo","code":"UG-309"},"UG-106":{"countryCode":"UG","subdivisionName":"Mpigi","code":"UG-106"},"UG-107":{"countryCode":"UG","subdivisionName":"Mubende","code":"UG-107"},"UG-108":{"countryCode":"UG","subdivisionName":"Mukono","code":"UG-108"},"UG-311":{"countryCode":"UG","subdivisionName":"Nakapiripirit","code":"UG-311"},"UG-116":{"countryCode":"UG","subdivisionName":"Nakaseke","code":"UG-116"},"UG-109":{"countryCode":"UG","subdivisionName":"Nakasongola","code":"UG-109"},"UG-230":{"countryCode":"UG","subdivisionName":"Namayingo","code":"UG-230"},"UG-327":{"countryCode":"UG","subdivisionName":"Napak","code":"UG-327"},"UG-310":{"countryCode":"UG","subdivisionName":"Nebbi","code":"UG-310"},"UG-424":{"countryCode":"UG","subdivisionName":"Ntoroko","code":"UG-424"},"UG-411":{"countryCode":"UG","subdivisionName":"Ntungamo","code":"UG-411"},"UG-328":{"countryCode":"UG","subdivisionName":"Nwoya","code":"UG-328"},"UG-321":{"countryCode":"UG","subdivisionName":"Oyam","code":"UG-321"},"UG-312":{"countryCode":"UG","subdivisionName":"Pader","code":"UG-312"},"UG-110":{"countryCode":"UG","subdivisionName":"Rakai","code":"UG-110"},"UG-425":{"countryCode":"UG","subdivisionName":"Rubirizi","code":"UG-425"},"UG-412":{"countryCode":"UG","subdivisionName":"Rukungiri","code":"UG-412"},"UG-111":{"countryCode":"UG","subdivisionName":"Sembabule","code":"UG-111"},"UG-426":{"countryCode":"UG","subdivisionName":"Sheema","code":"UG-426"},"UG-215":{"countryCode":"UG","subdivisionName":"Sironko","code":"UG-215"},"UG-211":{"countryCode":"UG","subdivisionName":"Soroti","code":"UG-211"},"UG-212":{"countryCode":"UG","subdivisionName":"Tororo","code":"UG-212"},"UG-113":{"countryCode":"UG","subdivisionName":"Wakiso","code":"UG-113"},"UG-313":{"countryCode":"UG","subdivisionName":"Yumbe","code":"UG-313"},"UG-330":{"countryCode":"UG","subdivisionName":"Zombo","code":"UG-330"},"UM-95":{"countryCode":"UM","subdivisionName":"Palmyra Atoll","code":"UM-95"},"US-AL":{"countryCode":"US","subdivisionName":"Alabama","code":"US-AL"},"US-AK":{"countryCode":"US","subdivisionName":"Alaska","code":"US-AK"},"US-AZ":{"countryCode":"US","subdivisionName":"Arizona","code":"US-AZ"},"US-AR":{"countryCode":"US","subdivisionName":"Arkansas","code":"US-AR"},"US-CA":{"countryCode":"US","subdivisionName":"California","code":"US-CA"},"US-CO":{"countryCode":"US","subdivisionName":"Colorado","code":"US-CO"},"US-CT":{"countryCode":"US","subdivisionName":"Connecticut","code":"US-CT"},"US-DE":{"countryCode":"US","subdivisionName":"Delaware","code":"US-DE"},"US-DC":{"countryCode":"US","subdivisionName":"District of Columbia","code":"US-DC"},"US-FL":{"countryCode":"US","subdivisionName":"Florida","code":"US-FL"},"US-GA":{"countryCode":"US","subdivisionName":"Georgia","code":"US-GA"},"US-HI":{"countryCode":"US","subdivisionName":"Hawaii","code":"US-HI"},"US-ID":{"countryCode":"US","subdivisionName":"Idaho","code":"US-ID"},"US-IL":{"countryCode":"US","subdivisionName":"Illinois","code":"US-IL"},"US-IN":{"countryCode":"US","subdivisionName":"Indiana","code":"US-IN"},"US-IA":{"countryCode":"US","subdivisionName":"Iowa","code":"US-IA"},"US-KS":{"countryCode":"US","subdivisionName":"Kansas","code":"US-KS"},"US-KY":{"countryCode":"US","subdivisionName":"Kentucky","code":"US-KY"},"US-LA":{"countryCode":"US","subdivisionName":"Louisiana","code":"US-LA"},"US-ME":{"countryCode":"US","subdivisionName":"Maine","code":"US-ME"},"US-MD":{"countryCode":"US","subdivisionName":"Maryland","code":"US-MD"},"US-MA":{"countryCode":"US","subdivisionName":"Massachusetts","code":"US-MA"},"US-MI":{"countryCode":"US","subdivisionName":"Michigan","code":"US-MI"},"US-MN":{"countryCode":"US","subdivisionName":"Minnesota","code":"US-MN"},"US-MS":{"countryCode":"US","subdivisionName":"Mississippi","code":"US-MS"},"US-MO":{"countryCode":"US","subdivisionName":"Missouri","code":"US-MO"},"US-MT":{"countryCode":"US","subdivisionName":"Montana","code":"US-MT"},"US-NE":{"countryCode":"US","subdivisionName":"Nebraska","code":"US-NE"},"US-NV":{"countryCode":"US","subdivisionName":"Nevada","code":"US-NV"},"US-NH":{"countryCode":"US","subdivisionName":"New Hampshire","code":"US-NH"},"US-NJ":{"countryCode":"US","subdivisionName":"New Jersey","code":"US-NJ"},"US-NM":{"countryCode":"US","subdivisionName":"New Mexico","code":"US-NM"},"US-NY":{"countryCode":"US","subdivisionName":"New York","code":"US-NY"},"US-NC":{"countryCode":"US","subdivisionName":"North Carolina","code":"US-NC"},"US-ND":{"countryCode":"US","subdivisionName":"North Dakota","code":"US-ND"},"US-OH":{"countryCode":"US","subdivisionName":"Ohio","code":"US-OH"},"US-OK":{"countryCode":"US","subdivisionName":"Oklahoma","code":"US-OK"},"US-OR":{"countryCode":"US","subdivisionName":"Oregon","code":"US-OR"},"US-PA":{"countryCode":"US","subdivisionName":"Pennsylvania","code":"US-PA"},"US-RI":{"countryCode":"US","subdivisionName":"Rhode Island","code":"US-RI"},"US-SC":{"countryCode":"US","subdivisionName":"South Carolina","code":"US-SC"},"US-SD":{"countryCode":"US","subdivisionName":"South Dakota","code":"US-SD"},"US-TN":{"countryCode":"US","subdivisionName":"Tennessee","code":"US-TN"},"US-TX":{"countryCode":"US","subdivisionName":"Texas","code":"US-TX"},"US-UT":{"countryCode":"US","subdivisionName":"Utah","code":"US-UT"},"US-VT":{"countryCode":"US","subdivisionName":"Vermont","code":"US-VT"},"US-VA":{"countryCode":"US","subdivisionName":"Virginia","code":"US-VA"},"US-WA":{"countryCode":"US","subdivisionName":"Washington","code":"US-WA"},"US-WV":{"countryCode":"US","subdivisionName":"West Virginia","code":"US-WV"},"US-WI":{"countryCode":"US","subdivisionName":"Wisconsin","code":"US-WI"},"US-WY":{"countryCode":"US","subdivisionName":"Wyoming","code":"US-WY"},"UY-AR":{"countryCode":"UY","subdivisionName":"Artigas","code":"UY-AR"},"UY-CA":{"countryCode":"UY","subdivisionName":"Canelones","code":"UY-CA"},"UY-CL":{"countryCode":"UY","subdivisionName":"Cerro Largo","code":"UY-CL"},"UY-CO":{"countryCode":"UY","subdivisionName":"Colonia","code":"UY-CO"},"UY-DU":{"countryCode":"UY","subdivisionName":"Durazno","code":"UY-DU"},"UY-FS":{"countryCode":"UY","subdivisionName":"Flores","code":"UY-FS"},"UY-FD":{"countryCode":"UY","subdivisionName":"Florida","code":"UY-FD"},"UY-LA":{"countryCode":"UY","subdivisionName":"Lavalleja","code":"UY-LA"},"UY-MA":{"countryCode":"UY","subdivisionName":"Maldonado","code":"UY-MA"},"UY-MO":{"countryCode":"UY","subdivisionName":"Montevideo","code":"UY-MO"},"UY-PA":{"countryCode":"UY","subdivisionName":"Paysandu","code":"UY-PA"},"UY-RN":{"countryCode":"UY","subdivisionName":"Rio Negro","code":"UY-RN"},"UY-RV":{"countryCode":"UY","subdivisionName":"Rivera","code":"UY-RV"},"UY-RO":{"countryCode":"UY","subdivisionName":"Rocha","code":"UY-RO"},"UY-SA":{"countryCode":"UY","subdivisionName":"Salto","code":"UY-SA"},"UY-SJ":{"countryCode":"UY","subdivisionName":"San Jose","code":"UY-SJ"},"UY-SO":{"countryCode":"UY","subdivisionName":"Soriano","code":"UY-SO"},"UY-TA":{"countryCode":"UY","subdivisionName":"Tacuarembo","code":"UY-TA"},"UY-TT":{"countryCode":"UY","subdivisionName":"Treinta y Tres","code":"UY-TT"},"UZ-AN":{"countryCode":"UZ","subdivisionName":"Andijon","code":"UZ-AN"},"UZ-BU":{"countryCode":"UZ","subdivisionName":"Buxoro","code":"UZ-BU"},"UZ-FA":{"countryCode":"UZ","subdivisionName":"Farg'ona","code":"UZ-FA"},"UZ-JI":{"countryCode":"UZ","subdivisionName":"Jizzax","code":"UZ-JI"},"UZ-NG":{"countryCode":"UZ","subdivisionName":"Namangan","code":"UZ-NG"},"UZ-NW":{"countryCode":"UZ","subdivisionName":"Navoiy","code":"UZ-NW"},"UZ-QA":{"countryCode":"UZ","subdivisionName":"Qashqadaryo","code":"UZ-QA"},"UZ-QR":{"countryCode":"UZ","subdivisionName":"Qoraqalpog'iston Respublikasi","code":"UZ-QR"},"UZ-SA":{"countryCode":"UZ","subdivisionName":"Samarqand","code":"UZ-SA"},"UZ-SI":{"countryCode":"UZ","subdivisionName":"Sirdaryo","code":"UZ-SI"},"UZ-SU":{"countryCode":"UZ","subdivisionName":"Surxondaryo","code":"UZ-SU"},"UZ-TK":{"countryCode":"UZ","subdivisionName":"Toshkent","code":"UZ-TK"},"UZ-XO":{"countryCode":"UZ","subdivisionName":"Xorazm","code":"UZ-XO"},"VC-01":{"countryCode":"VC","subdivisionName":"Charlotte","code":"VC-01"},"VC-06":{"countryCode":"VC","subdivisionName":"Grenadines","code":"VC-06"},"VC-04":{"countryCode":"VC","subdivisionName":"Saint George","code":"VC-04"},"VC-05":{"countryCode":"VC","subdivisionName":"Saint Patrick","code":"VC-05"},"VE-Z":{"countryCode":"VE","subdivisionName":"Amazonas","code":"VE-Z"},"VE-B":{"countryCode":"VE","subdivisionName":"Anzoategui","code":"VE-B"},"VE-C":{"countryCode":"VE","subdivisionName":"Apure","code":"VE-C"},"VE-D":{"countryCode":"VE","subdivisionName":"Aragua","code":"VE-D"},"VE-E":{"countryCode":"VE","subdivisionName":"Barinas","code":"VE-E"},"VE-F":{"countryCode":"VE","subdivisionName":"Bolivar","code":"VE-F"},"VE-G":{"countryCode":"VE","subdivisionName":"Carabobo","code":"VE-G"},"VE-H":{"countryCode":"VE","subdivisionName":"Cojedes","code":"VE-H"},"VE-Y":{"countryCode":"VE","subdivisionName":"Delta Amacuro","code":"VE-Y"},"VE-W":{"countryCode":"VE","subdivisionName":"Dependencias Federales","code":"VE-W"},"VE-A":{"countryCode":"VE","subdivisionName":"Distrito Capital","code":"VE-A"},"VE-I":{"countryCode":"VE","subdivisionName":"Falcon","code":"VE-I"},"VE-J":{"countryCode":"VE","subdivisionName":"Guarico","code":"VE-J"},"VE-X":{"countryCode":"VE","subdivisionName":"La Guaira","code":"VE-X"},"VE-K":{"countryCode":"VE","subdivisionName":"Lara","code":"VE-K"},"VE-L":{"countryCode":"VE","subdivisionName":"Merida","code":"VE-L"},"VE-M":{"countryCode":"VE","subdivisionName":"Miranda","code":"VE-M"},"VE-N":{"countryCode":"VE","subdivisionName":"Monagas","code":"VE-N"},"VE-O":{"countryCode":"VE","subdivisionName":"Nueva Esparta","code":"VE-O"},"VE-P":{"countryCode":"VE","subdivisionName":"Portuguesa","code":"VE-P"},"VE-R":{"countryCode":"VE","subdivisionName":"Sucre","code":"VE-R"},"VE-S":{"countryCode":"VE","subdivisionName":"Tachira","code":"VE-S"},"VE-T":{"countryCode":"VE","subdivisionName":"Trujillo","code":"VE-T"},"VE-U":{"countryCode":"VE","subdivisionName":"Yaracuy","code":"VE-U"},"VE-V":{"countryCode":"VE","subdivisionName":"Zulia","code":"VE-V"},"VN-44":{"countryCode":"VN","subdivisionName":"An Giang","code":"VN-44"},"VN-43":{"countryCode":"VN","subdivisionName":"Ba Ria - Vung Tau","code":"VN-43"},"VN-54":{"countryCode":"VN","subdivisionName":"Bac Giang","code":"VN-54"},"VN-53":{"countryCode":"VN","subdivisionName":"Bac Kan","code":"VN-53"},"VN-55":{"countryCode":"VN","subdivisionName":"Bac Lieu","code":"VN-55"},"VN-56":{"countryCode":"VN","subdivisionName":"Bac Ninh","code":"VN-56"},"VN-50":{"countryCode":"VN","subdivisionName":"Ben Tre","code":"VN-50"},"VN-31":{"countryCode":"VN","subdivisionName":"Binh Dinh","code":"VN-31"},"VN-57":{"countryCode":"VN","subdivisionName":"Binh Duong","code":"VN-57"},"VN-58":{"countryCode":"VN","subdivisionName":"Binh Phuoc","code":"VN-58"},"VN-40":{"countryCode":"VN","subdivisionName":"Binh Thuan","code":"VN-40"},"VN-59":{"countryCode":"VN","subdivisionName":"Ca Mau","code":"VN-59"},"VN-CT":{"countryCode":"VN","subdivisionName":"Can Tho","code":"VN-CT"},"VN-04":{"countryCode":"VN","subdivisionName":"Cao Bang","code":"VN-04"},"VN-DN":{"countryCode":"VN","subdivisionName":"Da Nang","code":"VN-DN"},"VN-33":{"countryCode":"VN","subdivisionName":"Dak Lak","code":"VN-33"},"VN-72":{"countryCode":"VN","subdivisionName":"Dak Nong","code":"VN-72"},"VN-71":{"countryCode":"VN","subdivisionName":"Dien Bien","code":"VN-71"},"VN-39":{"countryCode":"VN","subdivisionName":"Dong Nai","code":"VN-39"},"VN-45":{"countryCode":"VN","subdivisionName":"Dong Thap","code":"VN-45"},"VN-30":{"countryCode":"VN","subdivisionName":"Gia Lai","code":"VN-30"},"VN-03":{"countryCode":"VN","subdivisionName":"Ha Giang","code":"VN-03"},"VN-63":{"countryCode":"VN","subdivisionName":"Ha Nam","code":"VN-63"},"VN-HN":{"countryCode":"VN","subdivisionName":"Ha Noi","code":"VN-HN"},"VN-23":{"countryCode":"VN","subdivisionName":"Ha Tinh","code":"VN-23"},"VN-61":{"countryCode":"VN","subdivisionName":"Hai Duong","code":"VN-61"},"VN-HP":{"countryCode":"VN","subdivisionName":"Hai Phong","code":"VN-HP"},"VN-73":{"countryCode":"VN","subdivisionName":"Hau Giang","code":"VN-73"},"VN-SG":{"countryCode":"VN","subdivisionName":"Ho Chi Minh","code":"VN-SG"},"VN-14":{"countryCode":"VN","subdivisionName":"Hoa Binh","code":"VN-14"},"VN-66":{"countryCode":"VN","subdivisionName":"Hung Yen","code":"VN-66"},"VN-34":{"countryCode":"VN","subdivisionName":"Khanh Hoa","code":"VN-34"},"VN-47":{"countryCode":"VN","subdivisionName":"Kien Giang","code":"VN-47"},"VN-28":{"countryCode":"VN","subdivisionName":"Kon Tum","code":"VN-28"},"VN-01":{"countryCode":"VN","subdivisionName":"Lai Chau","code":"VN-01"},"VN-35":{"countryCode":"VN","subdivisionName":"Lam Dong","code":"VN-35"},"VN-09":{"countryCode":"VN","subdivisionName":"Lang Son","code":"VN-09"},"VN-02":{"countryCode":"VN","subdivisionName":"Lao Cai","code":"VN-02"},"VN-41":{"countryCode":"VN","subdivisionName":"Long An","code":"VN-41"},"VN-67":{"countryCode":"VN","subdivisionName":"Nam Dinh","code":"VN-67"},"VN-22":{"countryCode":"VN","subdivisionName":"Nghe An","code":"VN-22"},"VN-18":{"countryCode":"VN","subdivisionName":"Ninh Binh","code":"VN-18"},"VN-36":{"countryCode":"VN","subdivisionName":"Ninh Thuan","code":"VN-36"},"VN-68":{"countryCode":"VN","subdivisionName":"Phu Tho","code":"VN-68"},"VN-32":{"countryCode":"VN","subdivisionName":"Phu Yen","code":"VN-32"},"VN-24":{"countryCode":"VN","subdivisionName":"Quang Binh","code":"VN-24"},"VN-27":{"countryCode":"VN","subdivisionName":"Quang Nam","code":"VN-27"},"VN-29":{"countryCode":"VN","subdivisionName":"Quang Ngai","code":"VN-29"},"VN-13":{"countryCode":"VN","subdivisionName":"Quang Ninh","code":"VN-13"},"VN-25":{"countryCode":"VN","subdivisionName":"Quang Tri","code":"VN-25"},"VN-52":{"countryCode":"VN","subdivisionName":"Soc Trang","code":"VN-52"},"VN-05":{"countryCode":"VN","subdivisionName":"Son La","code":"VN-05"},"VN-37":{"countryCode":"VN","subdivisionName":"Tay Ninh","code":"VN-37"},"VN-20":{"countryCode":"VN","subdivisionName":"Thai Binh","code":"VN-20"},"VN-69":{"countryCode":"VN","subdivisionName":"Thai Nguyen","code":"VN-69"},"VN-21":{"countryCode":"VN","subdivisionName":"Thanh Hoa","code":"VN-21"},"VN-26":{"countryCode":"VN","subdivisionName":"Thua Thien-Hue","code":"VN-26"},"VN-46":{"countryCode":"VN","subdivisionName":"Tien Giang","code":"VN-46"},"VN-51":{"countryCode":"VN","subdivisionName":"Tra Vinh","code":"VN-51"},"VN-07":{"countryCode":"VN","subdivisionName":"Tuyen Quang","code":"VN-07"},"VN-49":{"countryCode":"VN","subdivisionName":"Vinh Long","code":"VN-49"},"VN-70":{"countryCode":"VN","subdivisionName":"Vinh Phuc","code":"VN-70"},"VN-06":{"countryCode":"VN","subdivisionName":"Yen Bai","code":"VN-06"},"VU-SAM":{"countryCode":"VU","subdivisionName":"Sanma","code":"VU-SAM"},"VU-SEE":{"countryCode":"VU","subdivisionName":"Shefa","code":"VU-SEE"},"VU-TAE":{"countryCode":"VU","subdivisionName":"Tafea","code":"VU-TAE"},"VU-TOB":{"countryCode":"VU","subdivisionName":"Torba","code":"VU-TOB"},"WF-SG":{"countryCode":"WF","subdivisionName":"Sigave","code":"WF-SG"},"WF-UV":{"countryCode":"WF","subdivisionName":"Uvea","code":"WF-UV"},"WS-AT":{"countryCode":"WS","subdivisionName":"Atua","code":"WS-AT"},"WS-FA":{"countryCode":"WS","subdivisionName":"Fa'asaleleaga","code":"WS-FA"},"WS-GI":{"countryCode":"WS","subdivisionName":"Gagaifomauga","code":"WS-GI"},"WS-TU":{"countryCode":"WS","subdivisionName":"Tuamasaga","code":"WS-TU"},"YE-AD":{"countryCode":"YE","subdivisionName":"'Adan","code":"YE-AD"},"YE-AM":{"countryCode":"YE","subdivisionName":"'Amran","code":"YE-AM"},"YE-DA":{"countryCode":"YE","subdivisionName":"Ad Dali'","code":"YE-DA"},"YE-BA":{"countryCode":"YE","subdivisionName":"Al Bayda'","code":"YE-BA"},"YE-HU":{"countryCode":"YE","subdivisionName":"Al Hudaydah","code":"YE-HU"},"YE-JA":{"countryCode":"YE","subdivisionName":"Al Jawf","code":"YE-JA"},"YE-MW":{"countryCode":"YE","subdivisionName":"Al Mahwit","code":"YE-MW"},"YE-SA":{"countryCode":"YE","subdivisionName":"Amanat al 'Asimah","code":"YE-SA"},"YE-DH":{"countryCode":"YE","subdivisionName":"Dhamar","code":"YE-DH"},"YE-HD":{"countryCode":"YE","subdivisionName":"Hadramawt","code":"YE-HD"},"YE-HJ":{"countryCode":"YE","subdivisionName":"Hajjah","code":"YE-HJ"},"YE-IB":{"countryCode":"YE","subdivisionName":"Ibb","code":"YE-IB"},"YE-LA":{"countryCode":"YE","subdivisionName":"Lahij","code":"YE-LA"},"YE-MA":{"countryCode":"YE","subdivisionName":"Ma'rib","code":"YE-MA"},"YE-RA":{"countryCode":"YE","subdivisionName":"Raymah","code":"YE-RA"},"YE-SD":{"countryCode":"YE","subdivisionName":"Sa'dah","code":"YE-SD"},"YE-SN":{"countryCode":"YE","subdivisionName":"San'a'","code":"YE-SN"},"YE-SH":{"countryCode":"YE","subdivisionName":"Shabwah","code":"YE-SH"},"YE-TA":{"countryCode":"YE","subdivisionName":"Ta'izz","code":"YE-TA"},"ZA-EC":{"countryCode":"ZA","subdivisionName":"Eastern Cape","code":"ZA-EC"},"ZA-FS":{"countryCode":"ZA","subdivisionName":"Free State","code":"ZA-FS"},"ZA-GP":{"countryCode":"ZA","subdivisionName":"Gauteng","code":"ZA-GP"},"ZA-KZN":{"countryCode":"ZA","subdivisionName":"Kwazulu-Natal","code":"ZA-KZN"},"ZA-LP":{"countryCode":"ZA","subdivisionName":"Limpopo","code":"ZA-LP"},"ZA-MP":{"countryCode":"ZA","subdivisionName":"Mpumalanga","code":"ZA-MP"},"ZA-NW":{"countryCode":"ZA","subdivisionName":"North-West","code":"ZA-NW"},"ZA-NC":{"countryCode":"ZA","subdivisionName":"Northern Cape","code":"ZA-NC"},"ZA-WC":{"countryCode":"ZA","subdivisionName":"Western Cape","code":"ZA-WC"},"ZM-02":{"countryCode":"ZM","subdivisionName":"Central","code":"ZM-02"},"ZM-08":{"countryCode":"ZM","subdivisionName":"Copperbelt","code":"ZM-08"},"ZM-03":{"countryCode":"ZM","subdivisionName":"Eastern","code":"ZM-03"},"ZM-04":{"countryCode":"ZM","subdivisionName":"Luapula","code":"ZM-04"},"ZM-09":{"countryCode":"ZM","subdivisionName":"Lusaka","code":"ZM-09"},"ZM-10":{"countryCode":"ZM","subdivisionName":"Muchinga","code":"ZM-10"},"ZM-06":{"countryCode":"ZM","subdivisionName":"North-Western","code":"ZM-06"},"ZM-05":{"countryCode":"ZM","subdivisionName":"Northern","code":"ZM-05"},"ZM-07":{"countryCode":"ZM","subdivisionName":"Southern","code":"ZM-07"},"ZM-01":{"countryCode":"ZM","subdivisionName":"Western","code":"ZM-01"},"ZW-BU":{"countryCode":"ZW","subdivisionName":"Bulawayo","code":"ZW-BU"},"ZW-HA":{"countryCode":"ZW","subdivisionName":"Harare","code":"ZW-HA"},"ZW-MA":{"countryCode":"ZW","subdivisionName":"Manicaland","code":"ZW-MA"},"ZW-MC":{"countryCode":"ZW","subdivisionName":"Mashonaland Central","code":"ZW-MC"},"ZW-ME":{"countryCode":"ZW","subdivisionName":"Mashonaland East","code":"ZW-ME"},"ZW-MW":{"countryCode":"ZW","subdivisionName":"Mashonaland West","code":"ZW-MW"},"ZW-MV":{"countryCode":"ZW","subdivisionName":"Masvingo","code":"ZW-MV"},"ZW-MN":{"countryCode":"ZW","subdivisionName":"Matabeleland North","code":"ZW-MN"},"ZW-MS":{"countryCode":"ZW","subdivisionName":"Matabeleland South","code":"ZW-MS"},"ZW-MI":{"countryCode":"ZW","subdivisionName":"Midlands","code":"ZW-MI"}} \ No newline at end of file +export const countries = { + "AD-07": { + countryCode: "AD", + subdivisionName: "Andorra la Vella", + code: "AD-07", + }, + "AD-02": { countryCode: "AD", subdivisionName: "Canillo", code: "AD-02" }, + "AD-03": { countryCode: "AD", subdivisionName: "Encamp", code: "AD-03" }, + "AD-08": { + countryCode: "AD", + subdivisionName: "Escaldes-Engordany", + code: "AD-08", + }, + "AD-04": { countryCode: "AD", subdivisionName: "La Massana", code: "AD-04" }, + "AD-05": { countryCode: "AD", subdivisionName: "Ordino", code: "AD-05" }, + "AD-06": { + countryCode: "AD", + subdivisionName: "Sant Julia de Loria", + code: "AD-06", + }, + "AE-AJ": { countryCode: "AE", subdivisionName: "'Ajman", code: "AE-AJ" }, + "AE-AZ": { countryCode: "AE", subdivisionName: "Abu Zaby", code: "AE-AZ" }, + "AE-FU": { countryCode: "AE", subdivisionName: "Al Fujayrah", code: "AE-FU" }, + "AE-SH": { + countryCode: "AE", + subdivisionName: "Ash Shariqah", + code: "AE-SH", + }, + "AE-DU": { countryCode: "AE", subdivisionName: "Dubayy", code: "AE-DU" }, + "AE-RK": { + countryCode: "AE", + subdivisionName: "Ra's al Khaymah", + code: "AE-RK", + }, + "AE-UQ": { + countryCode: "AE", + subdivisionName: "Umm al Qaywayn", + code: "AE-UQ", + }, + "AF-BDG": { countryCode: "AF", subdivisionName: "Badghis", code: "AF-BDG" }, + "AF-BGL": { countryCode: "AF", subdivisionName: "Baghlan", code: "AF-BGL" }, + "AF-BAL": { countryCode: "AF", subdivisionName: "Balkh", code: "AF-BAL" }, + "AF-BAM": { countryCode: "AF", subdivisionName: "Bamyan", code: "AF-BAM" }, + "AF-DAY": { countryCode: "AF", subdivisionName: "Daykundi", code: "AF-DAY" }, + "AF-FRA": { countryCode: "AF", subdivisionName: "Farah", code: "AF-FRA" }, + "AF-FYB": { countryCode: "AF", subdivisionName: "Faryab", code: "AF-FYB" }, + "AF-GHA": { countryCode: "AF", subdivisionName: "Ghazni", code: "AF-GHA" }, + "AF-GHO": { countryCode: "AF", subdivisionName: "Ghor", code: "AF-GHO" }, + "AF-HEL": { countryCode: "AF", subdivisionName: "Helmand", code: "AF-HEL" }, + "AF-HER": { countryCode: "AF", subdivisionName: "Herat", code: "AF-HER" }, + "AF-JOW": { countryCode: "AF", subdivisionName: "Jowzjan", code: "AF-JOW" }, + "AF-KAB": { countryCode: "AF", subdivisionName: "Kabul", code: "AF-KAB" }, + "AF-KAN": { countryCode: "AF", subdivisionName: "Kandahar", code: "AF-KAN" }, + "AF-KHO": { countryCode: "AF", subdivisionName: "Khost", code: "AF-KHO" }, + "AF-KDZ": { countryCode: "AF", subdivisionName: "Kunduz", code: "AF-KDZ" }, + "AF-LAG": { countryCode: "AF", subdivisionName: "Laghman", code: "AF-LAG" }, + "AF-LOG": { countryCode: "AF", subdivisionName: "Logar", code: "AF-LOG" }, + "AF-NAN": { countryCode: "AF", subdivisionName: "Nangarhar", code: "AF-NAN" }, + "AF-NIM": { countryCode: "AF", subdivisionName: "Nimroz", code: "AF-NIM" }, + "AF-PKA": { countryCode: "AF", subdivisionName: "Paktika", code: "AF-PKA" }, + "AF-PIA": { countryCode: "AF", subdivisionName: "Paktiya", code: "AF-PIA" }, + "AF-PAR": { countryCode: "AF", subdivisionName: "Parwan", code: "AF-PAR" }, + "AF-SAM": { countryCode: "AF", subdivisionName: "Samangan", code: "AF-SAM" }, + "AF-SAR": { countryCode: "AF", subdivisionName: "Sar-e Pul", code: "AF-SAR" }, + "AF-TAK": { countryCode: "AF", subdivisionName: "Takhar", code: "AF-TAK" }, + "AF-URU": { countryCode: "AF", subdivisionName: "Uruzgan", code: "AF-URU" }, + "AF-WAR": { countryCode: "AF", subdivisionName: "Wardak", code: "AF-WAR" }, + "AG-10": { countryCode: "AG", subdivisionName: "Barbuda", code: "AG-10" }, + "AG-11": { countryCode: "AG", subdivisionName: "Redonda", code: "AG-11" }, + "AG-03": { + countryCode: "AG", + subdivisionName: "Saint George", + code: "AG-03", + }, + "AG-04": { countryCode: "AG", subdivisionName: "Saint John", code: "AG-04" }, + "AG-05": { countryCode: "AG", subdivisionName: "Saint Mary", code: "AG-05" }, + "AG-07": { countryCode: "AG", subdivisionName: "Saint Peter", code: "AG-07" }, + "AG-08": { + countryCode: "AG", + subdivisionName: "Saint Philip", + code: "AG-08", + }, + "-": { countryCode: "YT", subdivisionName: "Tsingoni", code: "-" }, + "AL-01": { countryCode: "AL", subdivisionName: "Berat", code: "AL-01" }, + "AL-09": { countryCode: "AL", subdivisionName: "Diber", code: "AL-09" }, + "AL-02": { countryCode: "AL", subdivisionName: "Durres", code: "AL-02" }, + "AL-03": { countryCode: "AL", subdivisionName: "Elbasan", code: "AL-03" }, + "AL-04": { countryCode: "AL", subdivisionName: "Fier", code: "AL-04" }, + "AL-05": { countryCode: "AL", subdivisionName: "Gjirokaster", code: "AL-05" }, + "AL-06": { countryCode: "AL", subdivisionName: "Korce", code: "AL-06" }, + "AL-07": { countryCode: "AL", subdivisionName: "Kukes", code: "AL-07" }, + "AL-08": { countryCode: "AL", subdivisionName: "Lezhe", code: "AL-08" }, + "AL-10": { countryCode: "AL", subdivisionName: "Shkoder", code: "AL-10" }, + "AL-11": { countryCode: "AL", subdivisionName: "Tirane", code: "AL-11" }, + "AL-12": { countryCode: "AL", subdivisionName: "Vlore", code: "AL-12" }, + "AM-AG": { countryCode: "AM", subdivisionName: "Aragacotn", code: "AM-AG" }, + "AM-AR": { countryCode: "AM", subdivisionName: "Ararat", code: "AM-AR" }, + "AM-AV": { countryCode: "AM", subdivisionName: "Armavir", code: "AM-AV" }, + "AM-ER": { countryCode: "AM", subdivisionName: "Erevan", code: "AM-ER" }, + "AM-GR": { + countryCode: "AM", + subdivisionName: "Gegark'unik'", + code: "AM-GR", + }, + "AM-KT": { countryCode: "AM", subdivisionName: "Kotayk'", code: "AM-KT" }, + "AM-LO": { countryCode: "AM", subdivisionName: "Lori", code: "AM-LO" }, + "AM-SH": { countryCode: "AM", subdivisionName: "Sirak", code: "AM-SH" }, + "AM-SU": { countryCode: "AM", subdivisionName: "Syunik'", code: "AM-SU" }, + "AM-TV": { countryCode: "AM", subdivisionName: "Tavus", code: "AM-TV" }, + "AM-VD": { countryCode: "AM", subdivisionName: "Vayoc Jor", code: "AM-VD" }, + "AO-BGO": { countryCode: "AO", subdivisionName: "Bengo", code: "AO-BGO" }, + "AO-BGU": { countryCode: "AO", subdivisionName: "Benguela", code: "AO-BGU" }, + "AO-BIE": { countryCode: "AO", subdivisionName: "Bie", code: "AO-BIE" }, + "AO-CAB": { countryCode: "AO", subdivisionName: "Cabinda", code: "AO-CAB" }, + "AO-CCU": { + countryCode: "AO", + subdivisionName: "Cuando Cubango", + code: "AO-CCU", + }, + "AO-CNO": { + countryCode: "AO", + subdivisionName: "Cuanza-Norte", + code: "AO-CNO", + }, + "AO-CUS": { + countryCode: "AO", + subdivisionName: "Cuanza-Sul", + code: "AO-CUS", + }, + "AO-CNN": { countryCode: "AO", subdivisionName: "Cunene", code: "AO-CNN" }, + "AO-HUA": { countryCode: "AO", subdivisionName: "Huambo", code: "AO-HUA" }, + "AO-HUI": { countryCode: "AO", subdivisionName: "Huila", code: "AO-HUI" }, + "AO-LUA": { countryCode: "AO", subdivisionName: "Luanda", code: "AO-LUA" }, + "AO-LNO": { + countryCode: "AO", + subdivisionName: "Lunda-Norte", + code: "AO-LNO", + }, + "AO-LSU": { countryCode: "AO", subdivisionName: "Lunda-Sul", code: "AO-LSU" }, + "AO-MAL": { countryCode: "AO", subdivisionName: "Malange", code: "AO-MAL" }, + "AO-MOX": { countryCode: "AO", subdivisionName: "Moxico", code: "AO-MOX" }, + "AO-NAM": { countryCode: "AO", subdivisionName: "Namibe", code: "AO-NAM" }, + "AO-UIG": { countryCode: "AO", subdivisionName: "Uige", code: "AO-UIG" }, + "AO-ZAI": { countryCode: "AO", subdivisionName: "Zaire", code: "AO-ZAI" }, + "AR-B": { countryCode: "AR", subdivisionName: "Buenos Aires", code: "AR-B" }, + "AR-K": { countryCode: "AR", subdivisionName: "Catamarca", code: "AR-K" }, + "AR-H": { countryCode: "AR", subdivisionName: "Chaco", code: "AR-H" }, + "AR-U": { countryCode: "AR", subdivisionName: "Chubut", code: "AR-U" }, + "AR-C": { + countryCode: "AR", + subdivisionName: "Ciudad Autonoma de Buenos Aires", + code: "AR-C", + }, + "AR-X": { countryCode: "AR", subdivisionName: "Cordoba", code: "AR-X" }, + "AR-W": { countryCode: "AR", subdivisionName: "Corrientes", code: "AR-W" }, + "AR-E": { countryCode: "AR", subdivisionName: "Entre Rios", code: "AR-E" }, + "AR-P": { countryCode: "AR", subdivisionName: "Formosa", code: "AR-P" }, + "AR-Y": { countryCode: "AR", subdivisionName: "Jujuy", code: "AR-Y" }, + "AR-L": { countryCode: "AR", subdivisionName: "La Pampa", code: "AR-L" }, + "AR-F": { countryCode: "AR", subdivisionName: "La Rioja", code: "AR-F" }, + "AR-M": { countryCode: "AR", subdivisionName: "Mendoza", code: "AR-M" }, + "AR-N": { countryCode: "AR", subdivisionName: "Misiones", code: "AR-N" }, + "AR-Q": { countryCode: "AR", subdivisionName: "Neuquen", code: "AR-Q" }, + "AR-R": { countryCode: "AR", subdivisionName: "Rio Negro", code: "AR-R" }, + "AR-A": { countryCode: "AR", subdivisionName: "Salta", code: "AR-A" }, + "AR-J": { countryCode: "AR", subdivisionName: "San Juan", code: "AR-J" }, + "AR-D": { countryCode: "AR", subdivisionName: "San Luis", code: "AR-D" }, + "AR-Z": { countryCode: "AR", subdivisionName: "Santa Cruz", code: "AR-Z" }, + "AR-S": { countryCode: "AR", subdivisionName: "Santa Fe", code: "AR-S" }, + "AR-G": { + countryCode: "AR", + subdivisionName: "Santiago del Estero", + code: "AR-G", + }, + "AR-V": { + countryCode: "AR", + subdivisionName: "Tierra del Fuego", + code: "AR-V", + }, + "AR-T": { countryCode: "AR", subdivisionName: "Tucuman", code: "AR-T" }, + "AT-1": { countryCode: "AT", subdivisionName: "Burgenland", code: "AT-1" }, + "AT-2": { countryCode: "AT", subdivisionName: "Karnten", code: "AT-2" }, + "AT-3": { + countryCode: "AT", + subdivisionName: "Niederosterreich", + code: "AT-3", + }, + "AT-4": { + countryCode: "AT", + subdivisionName: "Oberosterreich", + code: "AT-4", + }, + "AT-5": { countryCode: "AT", subdivisionName: "Salzburg", code: "AT-5" }, + "AT-6": { countryCode: "AT", subdivisionName: "Steiermark", code: "AT-6" }, + "AT-7": { countryCode: "AT", subdivisionName: "Tirol", code: "AT-7" }, + "AT-8": { countryCode: "AT", subdivisionName: "Vorarlberg", code: "AT-8" }, + "AT-9": { countryCode: "AT", subdivisionName: "Wien", code: "AT-9" }, + "AU-ACT": { + countryCode: "AU", + subdivisionName: "Australian Capital Territory", + code: "AU-ACT", + }, + "AU-NSW": { + countryCode: "AU", + subdivisionName: "New South Wales", + code: "AU-NSW", + }, + "AU-NT": { + countryCode: "AU", + subdivisionName: "Northern Territory", + code: "AU-NT", + }, + "AU-QLD": { + countryCode: "AU", + subdivisionName: "Queensland", + code: "AU-QLD", + }, + "AU-SA": { + countryCode: "AU", + subdivisionName: "South Australia", + code: "AU-SA", + }, + "AU-TAS": { countryCode: "AU", subdivisionName: "Tasmania", code: "AU-TAS" }, + "AU-VIC": { countryCode: "AU", subdivisionName: "Victoria", code: "AU-VIC" }, + "AU-WA": { + countryCode: "AU", + subdivisionName: "Western Australia", + code: "AU-WA", + }, + "AZ-ABS": { countryCode: "AZ", subdivisionName: "Abseron", code: "AZ-ABS" }, + "AZ-AGC": { countryCode: "AZ", subdivisionName: "Agcabadi", code: "AZ-AGC" }, + "AZ-AGS": { countryCode: "AZ", subdivisionName: "Agdas", code: "AZ-AGS" }, + "AZ-AGA": { countryCode: "AZ", subdivisionName: "Agstafa", code: "AZ-AGA" }, + "AZ-AGU": { countryCode: "AZ", subdivisionName: "Agsu", code: "AZ-AGU" }, + "AZ-AST": { countryCode: "AZ", subdivisionName: "Astara", code: "AZ-AST" }, + "AZ-BA": { countryCode: "AZ", subdivisionName: "Baki", code: "AZ-BA" }, + "AZ-BAL": { countryCode: "AZ", subdivisionName: "Balakan", code: "AZ-BAL" }, + "AZ-BAR": { countryCode: "AZ", subdivisionName: "Barda", code: "AZ-BAR" }, + "AZ-BEY": { countryCode: "AZ", subdivisionName: "Beylaqan", code: "AZ-BEY" }, + "AZ-BIL": { countryCode: "AZ", subdivisionName: "Bilasuvar", code: "AZ-BIL" }, + "AZ-CAL": { countryCode: "AZ", subdivisionName: "Calilabad", code: "AZ-CAL" }, + "AZ-DAS": { countryCode: "AZ", subdivisionName: "Daskasan", code: "AZ-DAS" }, + "AZ-FUZ": { countryCode: "AZ", subdivisionName: "Fuzuli", code: "AZ-FUZ" }, + "AZ-GAD": { countryCode: "AZ", subdivisionName: "Gadabay", code: "AZ-GAD" }, + "AZ-GA": { countryCode: "AZ", subdivisionName: "Ganca", code: "AZ-GA" }, + "AZ-GOY": { countryCode: "AZ", subdivisionName: "Goycay", code: "AZ-GOY" }, + "AZ-GYG": { countryCode: "AZ", subdivisionName: "Goygol", code: "AZ-GYG" }, + "AZ-IMI": { countryCode: "AZ", subdivisionName: "Imisli", code: "AZ-IMI" }, + "AZ-ISM": { countryCode: "AZ", subdivisionName: "Ismayilli", code: "AZ-ISM" }, + "AZ-KUR": { countryCode: "AZ", subdivisionName: "Kurdamir", code: "AZ-KUR" }, + "AZ-LA": { countryCode: "AZ", subdivisionName: "Lankaran", code: "AZ-LA" }, + "AZ-MAS": { countryCode: "AZ", subdivisionName: "Masalli", code: "AZ-MAS" }, + "AZ-MI": { countryCode: "AZ", subdivisionName: "Mingacevir", code: "AZ-MI" }, + "AZ-NA": { countryCode: "AZ", subdivisionName: "Naftalan", code: "AZ-NA" }, + "AZ-NX": { countryCode: "AZ", subdivisionName: "Naxcivan", code: "AZ-NX" }, + "AZ-NEF": { countryCode: "AZ", subdivisionName: "Neftcala", code: "AZ-NEF" }, + "AZ-OGU": { countryCode: "AZ", subdivisionName: "Oguz", code: "AZ-OGU" }, + "AZ-QAB": { countryCode: "AZ", subdivisionName: "Qabala", code: "AZ-QAB" }, + "AZ-QAX": { countryCode: "AZ", subdivisionName: "Qax", code: "AZ-QAX" }, + "AZ-QAZ": { countryCode: "AZ", subdivisionName: "Qazax", code: "AZ-QAZ" }, + "AZ-QBA": { countryCode: "AZ", subdivisionName: "Quba", code: "AZ-QBA" }, + "AZ-QUS": { countryCode: "AZ", subdivisionName: "Qusar", code: "AZ-QUS" }, + "AZ-SAT": { countryCode: "AZ", subdivisionName: "Saatli", code: "AZ-SAT" }, + "AZ-SAB": { countryCode: "AZ", subdivisionName: "Sabirabad", code: "AZ-SAB" }, + "AZ-SAK": { countryCode: "AZ", subdivisionName: "Saki", code: "AZ-SAK" }, + "AZ-SAL": { countryCode: "AZ", subdivisionName: "Salyan", code: "AZ-SAL" }, + "AZ-SMI": { countryCode: "AZ", subdivisionName: "Samaxi", code: "AZ-SMI" }, + "AZ-SKR": { countryCode: "AZ", subdivisionName: "Samkir", code: "AZ-SKR" }, + "AZ-SMX": { countryCode: "AZ", subdivisionName: "Samux", code: "AZ-SMX" }, + "AZ-SR": { countryCode: "AZ", subdivisionName: "Sirvan", code: "AZ-SR" }, + "AZ-SIY": { countryCode: "AZ", subdivisionName: "Siyazan", code: "AZ-SIY" }, + "AZ-SM": { countryCode: "AZ", subdivisionName: "Sumqayit", code: "AZ-SM" }, + "AZ-TAR": { countryCode: "AZ", subdivisionName: "Tartar", code: "AZ-TAR" }, + "AZ-TOV": { countryCode: "AZ", subdivisionName: "Tovuz", code: "AZ-TOV" }, + "AZ-UCA": { countryCode: "AZ", subdivisionName: "Ucar", code: "AZ-UCA" }, + "AZ-XAC": { countryCode: "AZ", subdivisionName: "Xacmaz", code: "AZ-XAC" }, + "AZ-XIZ": { countryCode: "AZ", subdivisionName: "Xizi", code: "AZ-XIZ" }, + "AZ-YAR": { countryCode: "AZ", subdivisionName: "Yardimli", code: "AZ-YAR" }, + "AZ-YEV": { countryCode: "AZ", subdivisionName: "Yevlax", code: "AZ-YEV" }, + "AZ-ZAQ": { countryCode: "AZ", subdivisionName: "Zaqatala", code: "AZ-ZAQ" }, + "AZ-ZAR": { countryCode: "AZ", subdivisionName: "Zardab", code: "AZ-ZAR" }, + "BA-BRC": { + countryCode: "BA", + subdivisionName: "Brcko distrikt", + code: "BA-BRC", + }, + "BA-BIH": { + countryCode: "BA", + subdivisionName: "Federacija Bosne i Hercegovine", + code: "BA-BIH", + }, + "BA-SRP": { + countryCode: "BA", + subdivisionName: "Republika Srpska", + code: "BA-SRP", + }, + "BB-01": { + countryCode: "BB", + subdivisionName: "Christ Church", + code: "BB-01", + }, + "BB-02": { + countryCode: "BB", + subdivisionName: "Saint Andrew", + code: "BB-02", + }, + "BB-03": { + countryCode: "BB", + subdivisionName: "Saint George", + code: "BB-03", + }, + "BB-04": { countryCode: "BB", subdivisionName: "Saint James", code: "BB-04" }, + "BB-05": { countryCode: "BB", subdivisionName: "Saint John", code: "BB-05" }, + "BB-07": { countryCode: "BB", subdivisionName: "Saint Lucy", code: "BB-07" }, + "BB-08": { + countryCode: "BB", + subdivisionName: "Saint Michael", + code: "BB-08", + }, + "BB-09": { countryCode: "BB", subdivisionName: "Saint Peter", code: "BB-09" }, + "BB-10": { + countryCode: "BB", + subdivisionName: "Saint Philip", + code: "BB-10", + }, + "BB-11": { + countryCode: "BB", + subdivisionName: "Saint Thomas", + code: "BB-11", + }, + "BD-A": { countryCode: "BD", subdivisionName: "Barishal", code: "BD-A" }, + "BD-B": { countryCode: "BD", subdivisionName: "Chattogram", code: "BD-B" }, + "BD-C": { countryCode: "BD", subdivisionName: "Dhaka", code: "BD-C" }, + "BD-D": { countryCode: "BD", subdivisionName: "Khulna", code: "BD-D" }, + "BD-E": { countryCode: "BD", subdivisionName: "Rajshahi", code: "BD-E" }, + "BD-F": { countryCode: "BD", subdivisionName: "Rangpur", code: "BD-F" }, + "BD-G": { countryCode: "BD", subdivisionName: "Sylhet", code: "BD-G" }, + "BE-VAN": { countryCode: "BE", subdivisionName: "Antwerpen", code: "BE-VAN" }, + "BE-WBR": { + countryCode: "BE", + subdivisionName: "Brabant wallon", + code: "BE-WBR", + }, + "BE-BRU": { + countryCode: "BE", + subdivisionName: "Brussels Hoofdstedelijk Gewest", + code: "BE-BRU", + }, + "BE-WHT": { countryCode: "BE", subdivisionName: "Hainaut", code: "BE-WHT" }, + "BE-WLG": { countryCode: "BE", subdivisionName: "Liege", code: "BE-WLG" }, + "BE-VLI": { countryCode: "BE", subdivisionName: "Limburg", code: "BE-VLI" }, + "BE-WLX": { + countryCode: "BE", + subdivisionName: "Luxembourg", + code: "BE-WLX", + }, + "BE-WNA": { countryCode: "BE", subdivisionName: "Namur", code: "BE-WNA" }, + "BE-VOV": { + countryCode: "BE", + subdivisionName: "Oost-Vlaanderen", + code: "BE-VOV", + }, + "BE-VBR": { + countryCode: "BE", + subdivisionName: "Vlaams-Brabant", + code: "BE-VBR", + }, + "BE-VWV": { + countryCode: "BE", + subdivisionName: "West-Vlaanderen", + code: "BE-VWV", + }, + "BF-BAL": { countryCode: "BF", subdivisionName: "Bale", code: "BF-BAL" }, + "BF-BAM": { countryCode: "BF", subdivisionName: "Bam", code: "BF-BAM" }, + "BF-BAN": { countryCode: "BF", subdivisionName: "Banwa", code: "BF-BAN" }, + "BF-BAZ": { countryCode: "BF", subdivisionName: "Bazega", code: "BF-BAZ" }, + "BF-BGR": { + countryCode: "BF", + subdivisionName: "Bougouriba", + code: "BF-BGR", + }, + "BF-BLG": { countryCode: "BF", subdivisionName: "Boulgou", code: "BF-BLG" }, + "BF-BLK": { + countryCode: "BF", + subdivisionName: "Boulkiemde", + code: "BF-BLK", + }, + "BF-COM": { countryCode: "BF", subdivisionName: "Comoe", code: "BF-COM" }, + "BF-GAN": { + countryCode: "BF", + subdivisionName: "Ganzourgou", + code: "BF-GAN", + }, + "BF-GNA": { countryCode: "BF", subdivisionName: "Gnagna", code: "BF-GNA" }, + "BF-GOU": { countryCode: "BF", subdivisionName: "Gourma", code: "BF-GOU" }, + "BF-HOU": { countryCode: "BF", subdivisionName: "Houet", code: "BF-HOU" }, + "BF-IOB": { countryCode: "BF", subdivisionName: "Ioba", code: "BF-IOB" }, + "BF-KAD": { countryCode: "BF", subdivisionName: "Kadiogo", code: "BF-KAD" }, + "BF-KEN": { + countryCode: "BF", + subdivisionName: "Kenedougou", + code: "BF-KEN", + }, + "BF-KMP": { countryCode: "BF", subdivisionName: "Kompienga", code: "BF-KMP" }, + "BF-KOS": { countryCode: "BF", subdivisionName: "Kossi", code: "BF-KOS" }, + "BF-KOT": { + countryCode: "BF", + subdivisionName: "Kouritenga", + code: "BF-KOT", + }, + "BF-KOW": { countryCode: "BF", subdivisionName: "Kourweogo", code: "BF-KOW" }, + "BF-LER": { countryCode: "BF", subdivisionName: "Leraba", code: "BF-LER" }, + "BF-LOR": { countryCode: "BF", subdivisionName: "Loroum", code: "BF-LOR" }, + "BF-MOU": { countryCode: "BF", subdivisionName: "Mouhoun", code: "BF-MOU" }, + "BF-NAO": { countryCode: "BF", subdivisionName: "Nahouri", code: "BF-NAO" }, + "BF-NAM": { + countryCode: "BF", + subdivisionName: "Namentenga", + code: "BF-NAM", + }, + "BF-NAY": { countryCode: "BF", subdivisionName: "Nayala", code: "BF-NAY" }, + "BF-OUB": { + countryCode: "BF", + subdivisionName: "Oubritenga", + code: "BF-OUB", + }, + "BF-OUD": { countryCode: "BF", subdivisionName: "Oudalan", code: "BF-OUD" }, + "BF-PAS": { countryCode: "BF", subdivisionName: "Passore", code: "BF-PAS" }, + "BF-SMT": { + countryCode: "BF", + subdivisionName: "Sanmatenga", + code: "BF-SMT", + }, + "BF-SEN": { countryCode: "BF", subdivisionName: "Seno", code: "BF-SEN" }, + "BF-SIS": { countryCode: "BF", subdivisionName: "Sissili", code: "BF-SIS" }, + "BF-SOM": { countryCode: "BF", subdivisionName: "Soum", code: "BF-SOM" }, + "BF-SOR": { countryCode: "BF", subdivisionName: "Sourou", code: "BF-SOR" }, + "BF-TAP": { countryCode: "BF", subdivisionName: "Tapoa", code: "BF-TAP" }, + "BF-TUI": { countryCode: "BF", subdivisionName: "Tuy", code: "BF-TUI" }, + "BF-YAT": { countryCode: "BF", subdivisionName: "Yatenga", code: "BF-YAT" }, + "BF-ZIR": { countryCode: "BF", subdivisionName: "Ziro", code: "BF-ZIR" }, + "BF-ZON": { countryCode: "BF", subdivisionName: "Zondoma", code: "BF-ZON" }, + "BF-ZOU": { + countryCode: "BF", + subdivisionName: "Zoundweogo", + code: "BF-ZOU", + }, + "BG-01": { countryCode: "BG", subdivisionName: "Blagoevgrad", code: "BG-01" }, + "BG-02": { countryCode: "BG", subdivisionName: "Burgas", code: "BG-02" }, + "BG-08": { countryCode: "BG", subdivisionName: "Dobrich", code: "BG-08" }, + "BG-07": { countryCode: "BG", subdivisionName: "Gabrovo", code: "BG-07" }, + "BG-26": { countryCode: "BG", subdivisionName: "Haskovo", code: "BG-26" }, + "BG-09": { countryCode: "BG", subdivisionName: "Kardzhali", code: "BG-09" }, + "BG-10": { countryCode: "BG", subdivisionName: "Kyustendil", code: "BG-10" }, + "BG-11": { countryCode: "BG", subdivisionName: "Lovech", code: "BG-11" }, + "BG-12": { countryCode: "BG", subdivisionName: "Montana", code: "BG-12" }, + "BG-13": { countryCode: "BG", subdivisionName: "Pazardzhik", code: "BG-13" }, + "BG-14": { countryCode: "BG", subdivisionName: "Pernik", code: "BG-14" }, + "BG-15": { countryCode: "BG", subdivisionName: "Pleven", code: "BG-15" }, + "BG-16": { countryCode: "BG", subdivisionName: "Plovdiv", code: "BG-16" }, + "BG-17": { countryCode: "BG", subdivisionName: "Razgrad", code: "BG-17" }, + "BG-18": { countryCode: "BG", subdivisionName: "Ruse", code: "BG-18" }, + "BG-27": { countryCode: "BG", subdivisionName: "Shumen", code: "BG-27" }, + "BG-19": { countryCode: "BG", subdivisionName: "Silistra", code: "BG-19" }, + "BG-20": { countryCode: "BG", subdivisionName: "Sliven", code: "BG-20" }, + "BG-21": { countryCode: "BG", subdivisionName: "Smolyan", code: "BG-21" }, + "BG-23": { countryCode: "BG", subdivisionName: "Sofia", code: "BG-23" }, + "BG-22": { + countryCode: "BG", + subdivisionName: "Sofia (stolitsa)", + code: "BG-22", + }, + "BG-24": { + countryCode: "BG", + subdivisionName: "Stara Zagora", + code: "BG-24", + }, + "BG-25": { countryCode: "BG", subdivisionName: "Targovishte", code: "BG-25" }, + "BG-03": { countryCode: "BG", subdivisionName: "Varna", code: "BG-03" }, + "BG-04": { + countryCode: "BG", + subdivisionName: "Veliko Tarnovo", + code: "BG-04", + }, + "BG-05": { countryCode: "BG", subdivisionName: "Vidin", code: "BG-05" }, + "BG-06": { countryCode: "BG", subdivisionName: "Vratsa", code: "BG-06" }, + "BG-28": { countryCode: "BG", subdivisionName: "Yambol", code: "BG-28" }, + "BH-13": { countryCode: "BH", subdivisionName: "Al 'Asimah", code: "BH-13" }, + "BH-14": { + countryCode: "BH", + subdivisionName: "Al Janubiyah", + code: "BH-14", + }, + "BH-15": { countryCode: "BH", subdivisionName: "Al Muharraq", code: "BH-15" }, + "BH-17": { + countryCode: "BH", + subdivisionName: "Ash Shamaliyah", + code: "BH-17", + }, + "BI-BM": { + countryCode: "BI", + subdivisionName: "Bujumbura Mairie", + code: "BI-BM", + }, + "BI-BR": { countryCode: "BI", subdivisionName: "Bururi", code: "BI-BR" }, + "BI-CI": { countryCode: "BI", subdivisionName: "Cibitoke", code: "BI-CI" }, + "BI-GI": { countryCode: "BI", subdivisionName: "Gitega", code: "BI-GI" }, + "BI-KI": { countryCode: "BI", subdivisionName: "Kirundo", code: "BI-KI" }, + "BI-MW": { countryCode: "BI", subdivisionName: "Mwaro", code: "BI-MW" }, + "BI-NG": { countryCode: "BI", subdivisionName: "Ngozi", code: "BI-NG" }, + "BI-RM": { countryCode: "BI", subdivisionName: "Rumonge", code: "BI-RM" }, + "BI-RT": { countryCode: "BI", subdivisionName: "Rutana", code: "BI-RT" }, + "BI-RY": { countryCode: "BI", subdivisionName: "Ruyigi", code: "BI-RY" }, + "BJ-AK": { countryCode: "BJ", subdivisionName: "Atacora", code: "BJ-AK" }, + "BJ-AQ": { countryCode: "BJ", subdivisionName: "Atlantique", code: "BJ-AQ" }, + "BJ-BO": { countryCode: "BJ", subdivisionName: "Borgou", code: "BJ-BO" }, + "BJ-CO": { countryCode: "BJ", subdivisionName: "Collines", code: "BJ-CO" }, + "BJ-DO": { countryCode: "BJ", subdivisionName: "Donga", code: "BJ-DO" }, + "BJ-LI": { countryCode: "BJ", subdivisionName: "Littoral", code: "BJ-LI" }, + "BJ-MO": { countryCode: "BJ", subdivisionName: "Mono", code: "BJ-MO" }, + "BJ-OU": { countryCode: "BJ", subdivisionName: "Oueme", code: "BJ-OU" }, + "BJ-PL": { countryCode: "BJ", subdivisionName: "Plateau", code: "BJ-PL" }, + "BJ-ZO": { countryCode: "BJ", subdivisionName: "Zou", code: "BJ-ZO" }, + "BN-BE": { countryCode: "BN", subdivisionName: "Belait", code: "BN-BE" }, + "BN-BM": { + countryCode: "BN", + subdivisionName: "Brunei-Muara", + code: "BN-BM", + }, + "BN-TE": { countryCode: "BN", subdivisionName: "Temburong", code: "BN-TE" }, + "BN-TU": { countryCode: "BN", subdivisionName: "Tutong", code: "BN-TU" }, + "BO-H": { countryCode: "BO", subdivisionName: "Chuquisaca", code: "BO-H" }, + "BO-C": { countryCode: "BO", subdivisionName: "Cochabamba", code: "BO-C" }, + "BO-B": { countryCode: "BO", subdivisionName: "El Beni", code: "BO-B" }, + "BO-L": { countryCode: "BO", subdivisionName: "La Paz", code: "BO-L" }, + "BO-O": { countryCode: "BO", subdivisionName: "Oruro", code: "BO-O" }, + "BO-N": { countryCode: "BO", subdivisionName: "Pando", code: "BO-N" }, + "BO-P": { countryCode: "BO", subdivisionName: "Potosi", code: "BO-P" }, + "BO-S": { countryCode: "BO", subdivisionName: "Santa Cruz", code: "BO-S" }, + "BO-T": { countryCode: "BO", subdivisionName: "Tarija", code: "BO-T" }, + "BQ-BO": { countryCode: "BQ", subdivisionName: "Bonaire", code: "BQ-BO" }, + "BQ-SA": { countryCode: "BQ", subdivisionName: "Saba", code: "BQ-SA" }, + "BQ-SE": { + countryCode: "BQ", + subdivisionName: "Sint Eustatius", + code: "BQ-SE", + }, + "BR-AC": { countryCode: "BR", subdivisionName: "Acre", code: "BR-AC" }, + "BR-AL": { countryCode: "BR", subdivisionName: "Alagoas", code: "BR-AL" }, + "BR-AP": { countryCode: "BR", subdivisionName: "Amapa", code: "BR-AP" }, + "BR-AM": { countryCode: "BR", subdivisionName: "Amazonas", code: "BR-AM" }, + "BR-BA": { countryCode: "BR", subdivisionName: "Bahia", code: "BR-BA" }, + "BR-CE": { countryCode: "BR", subdivisionName: "Ceara", code: "BR-CE" }, + "BR-DF": { + countryCode: "BR", + subdivisionName: "Distrito Federal", + code: "BR-DF", + }, + "BR-ES": { + countryCode: "BR", + subdivisionName: "Espirito Santo", + code: "BR-ES", + }, + "BR-GO": { countryCode: "BR", subdivisionName: "Goias", code: "BR-GO" }, + "BR-MA": { countryCode: "BR", subdivisionName: "Maranhao", code: "BR-MA" }, + "BR-MT": { countryCode: "BR", subdivisionName: "Mato Grosso", code: "BR-MT" }, + "BR-MS": { + countryCode: "BR", + subdivisionName: "Mato Grosso do Sul", + code: "BR-MS", + }, + "BR-MG": { + countryCode: "BR", + subdivisionName: "Minas Gerais", + code: "BR-MG", + }, + "BR-PA": { countryCode: "BR", subdivisionName: "Para", code: "BR-PA" }, + "BR-PB": { countryCode: "BR", subdivisionName: "Paraiba", code: "BR-PB" }, + "BR-PR": { countryCode: "BR", subdivisionName: "Parana", code: "BR-PR" }, + "BR-PE": { countryCode: "BR", subdivisionName: "Pernambuco", code: "BR-PE" }, + "BR-PI": { countryCode: "BR", subdivisionName: "Piaui", code: "BR-PI" }, + "BR-RN": { + countryCode: "BR", + subdivisionName: "Rio Grande do Norte", + code: "BR-RN", + }, + "BR-RS": { + countryCode: "BR", + subdivisionName: "Rio Grande do Sul", + code: "BR-RS", + }, + "BR-RJ": { + countryCode: "BR", + subdivisionName: "Rio de Janeiro", + code: "BR-RJ", + }, + "BR-RO": { countryCode: "BR", subdivisionName: "Rondonia", code: "BR-RO" }, + "BR-RR": { countryCode: "BR", subdivisionName: "Roraima", code: "BR-RR" }, + "BR-SC": { + countryCode: "BR", + subdivisionName: "Santa Catarina", + code: "BR-SC", + }, + "BR-SP": { countryCode: "BR", subdivisionName: "Sao Paulo", code: "BR-SP" }, + "BR-SE": { countryCode: "BR", subdivisionName: "Sergipe", code: "BR-SE" }, + "BR-TO": { countryCode: "BR", subdivisionName: "Tocantins", code: "BR-TO" }, + "BS-BP": { countryCode: "BS", subdivisionName: "Black Point", code: "BS-BP" }, + "BS-CO": { + countryCode: "BS", + subdivisionName: "Central Abaco", + code: "BS-CO", + }, + "BS-FP": { + countryCode: "BS", + subdivisionName: "City of Freeport", + code: "BS-FP", + }, + "BS-EG": { + countryCode: "BS", + subdivisionName: "East Grand Bahama", + code: "BS-EG", + }, + "BS-HI": { + countryCode: "BS", + subdivisionName: "Harbour Island", + code: "BS-HI", + }, + "BS-LI": { countryCode: "BS", subdivisionName: "Long Island", code: "BS-LI" }, + "BS-NP": { + countryCode: "BS", + subdivisionName: "New Providence", + code: "BS-NP", + }, + "BS-NS": { + countryCode: "BS", + subdivisionName: "North Andros", + code: "BS-NS", + }, + "BS-NE": { + countryCode: "BS", + subdivisionName: "North Eleuthera", + code: "BS-NE", + }, + "BS-SS": { + countryCode: "BS", + subdivisionName: "San Salvador", + code: "BS-SS", + }, + "BS-SE": { + countryCode: "BS", + subdivisionName: "South Eleuthera", + code: "BS-SE", + }, + "BS-WG": { + countryCode: "BS", + subdivisionName: "West Grand Bahama", + code: "BS-WG", + }, + "BT-33": { countryCode: "BT", subdivisionName: "Bumthang", code: "BT-33" }, + "BT-12": { countryCode: "BT", subdivisionName: "Chhukha", code: "BT-12" }, + "BT-22": { countryCode: "BT", subdivisionName: "Dagana", code: "BT-22" }, + "BT-GA": { countryCode: "BT", subdivisionName: "Gasa", code: "BT-GA" }, + "BT-44": { countryCode: "BT", subdivisionName: "Lhuentse", code: "BT-44" }, + "BT-42": { countryCode: "BT", subdivisionName: "Monggar", code: "BT-42" }, + "BT-11": { countryCode: "BT", subdivisionName: "Paro", code: "BT-11" }, + "BT-43": { + countryCode: "BT", + subdivisionName: "Pema Gatshel", + code: "BT-43", + }, + "BT-23": { countryCode: "BT", subdivisionName: "Punakha", code: "BT-23" }, + "BT-45": { + countryCode: "BT", + subdivisionName: "Samdrup Jongkhar", + code: "BT-45", + }, + "BT-14": { countryCode: "BT", subdivisionName: "Samtse", code: "BT-14" }, + "BT-31": { countryCode: "BT", subdivisionName: "Sarpang", code: "BT-31" }, + "BT-15": { countryCode: "BT", subdivisionName: "Thimphu", code: "BT-15" }, + "BT-41": { countryCode: "BT", subdivisionName: "Trashigang", code: "BT-41" }, + "BT-32": { countryCode: "BT", subdivisionName: "Trongsa", code: "BT-32" }, + "BT-21": { countryCode: "BT", subdivisionName: "Tsirang", code: "BT-21" }, + "BT-24": { + countryCode: "BT", + subdivisionName: "Wangdue Phodrang", + code: "BT-24", + }, + "BT-34": { countryCode: "BT", subdivisionName: "Zhemgang", code: "BT-34" }, + "BW-CE": { countryCode: "BW", subdivisionName: "Central", code: "BW-CE" }, + "BW-CH": { countryCode: "BW", subdivisionName: "Chobe", code: "BW-CH" }, + "BW-GH": { countryCode: "BW", subdivisionName: "Ghanzi", code: "BW-GH" }, + "BW-KG": { countryCode: "BW", subdivisionName: "Kgalagadi", code: "BW-KG" }, + "BW-KL": { countryCode: "BW", subdivisionName: "Kgatleng", code: "BW-KL" }, + "BW-KW": { countryCode: "BW", subdivisionName: "Kweneng", code: "BW-KW" }, + "BW-NE": { countryCode: "BW", subdivisionName: "North East", code: "BW-NE" }, + "BW-NW": { countryCode: "BW", subdivisionName: "North West", code: "BW-NW" }, + "BW-SE": { countryCode: "BW", subdivisionName: "South East", code: "BW-SE" }, + "BW-SO": { countryCode: "BW", subdivisionName: "Southern", code: "BW-SO" }, + "BY-BR": { + countryCode: "BY", + subdivisionName: "Brestskaya voblasts'", + code: "BY-BR", + }, + "BY-HO": { + countryCode: "BY", + subdivisionName: "Homyel'skaya voblasts'", + code: "BY-HO", + }, + "BY-HM": { countryCode: "BY", subdivisionName: "Horad Minsk", code: "BY-HM" }, + "BY-HR": { + countryCode: "BY", + subdivisionName: "Hrodzyenskaya voblasts'", + code: "BY-HR", + }, + "BY-MA": { + countryCode: "BY", + subdivisionName: "Mahilyowskaya voblasts'", + code: "BY-MA", + }, + "BY-MI": { + countryCode: "BY", + subdivisionName: "Minskaya voblasts'", + code: "BY-MI", + }, + "BY-VI": { + countryCode: "BY", + subdivisionName: "Vitsyebskaya voblasts'", + code: "BY-VI", + }, + "BZ-BZ": { countryCode: "BZ", subdivisionName: "Belize", code: "BZ-BZ" }, + "BZ-CY": { countryCode: "BZ", subdivisionName: "Cayo", code: "BZ-CY" }, + "BZ-CZL": { countryCode: "BZ", subdivisionName: "Corozal", code: "BZ-CZL" }, + "BZ-OW": { countryCode: "BZ", subdivisionName: "Orange Walk", code: "BZ-OW" }, + "BZ-SC": { countryCode: "BZ", subdivisionName: "Stann Creek", code: "BZ-SC" }, + "BZ-TOL": { countryCode: "BZ", subdivisionName: "Toledo", code: "BZ-TOL" }, + "CA-AB": { countryCode: "CA", subdivisionName: "Alberta", code: "CA-AB" }, + "CA-BC": { + countryCode: "CA", + subdivisionName: "British Columbia", + code: "CA-BC", + }, + "CA-MB": { countryCode: "CA", subdivisionName: "Manitoba", code: "CA-MB" }, + "CA-NB": { + countryCode: "CA", + subdivisionName: "New Brunswick", + code: "CA-NB", + }, + "CA-NL": { + countryCode: "CA", + subdivisionName: "Newfoundland and Labrador", + code: "CA-NL", + }, + "CA-NT": { + countryCode: "CA", + subdivisionName: "Northwest Territories", + code: "CA-NT", + }, + "CA-NS": { countryCode: "CA", subdivisionName: "Nova Scotia", code: "CA-NS" }, + "CA-NU": { countryCode: "CA", subdivisionName: "Nunavut", code: "CA-NU" }, + "CA-ON": { countryCode: "CA", subdivisionName: "Ontario", code: "CA-ON" }, + "CA-PE": { + countryCode: "CA", + subdivisionName: "Prince Edward Island", + code: "CA-PE", + }, + "CA-QC": { countryCode: "CA", subdivisionName: "Quebec", code: "CA-QC" }, + "CA-SK": { + countryCode: "CA", + subdivisionName: "Saskatchewan", + code: "CA-SK", + }, + "CA-YT": { countryCode: "CA", subdivisionName: "Yukon", code: "CA-YT" }, + "CD-EQ": { countryCode: "CD", subdivisionName: "Equateur", code: "CD-EQ" }, + "CD-HK": { + countryCode: "CD", + subdivisionName: "Haut-Katanga", + code: "CD-HK", + }, + "CD-HL": { countryCode: "CD", subdivisionName: "Haut-Lomami", code: "CD-HL" }, + "CD-IT": { countryCode: "CD", subdivisionName: "Ituri", code: "CD-IT" }, + "CD-KS": { countryCode: "CD", subdivisionName: "Kasai", code: "CD-KS" }, + "CD-KC": { + countryCode: "CD", + subdivisionName: "Kasai Central", + code: "CD-KC", + }, + "CD-KE": { + countryCode: "CD", + subdivisionName: "Kasai Oriental", + code: "CD-KE", + }, + "CD-KN": { countryCode: "CD", subdivisionName: "Kinshasa", code: "CD-KN" }, + "CD-LU": { countryCode: "CD", subdivisionName: "Lualaba", code: "CD-LU" }, + "CD-MA": { countryCode: "CD", subdivisionName: "Maniema", code: "CD-MA" }, + "CD-NK": { countryCode: "CD", subdivisionName: "Nord-Kivu", code: "CD-NK" }, + "CD-SA": { countryCode: "CD", subdivisionName: "Sankuru", code: "CD-SA" }, + "CD-SK": { countryCode: "CD", subdivisionName: "Sud-Kivu", code: "CD-SK" }, + "CD-TA": { countryCode: "CD", subdivisionName: "Tanganyika", code: "CD-TA" }, + "CD-TO": { countryCode: "CD", subdivisionName: "Tshopo", code: "CD-TO" }, + "CF-BB": { + countryCode: "CF", + subdivisionName: "Bamingui-Bangoran", + code: "CF-BB", + }, + "CF-BGF": { countryCode: "CF", subdivisionName: "Bangui", code: "CF-BGF" }, + "CF-KB": { countryCode: "CF", subdivisionName: "Gribingui", code: "CF-KB" }, + "CF-KG": { + countryCode: "CF", + subdivisionName: "Kemo-Gribingui", + code: "CF-KG", + }, + "CF-HS": { + countryCode: "CF", + subdivisionName: "Mambere-Kadei", + code: "CF-HS", + }, + "CF-NM": { + countryCode: "CF", + subdivisionName: "Nana-Mambere", + code: "CF-NM", + }, + "CF-UK": { countryCode: "CF", subdivisionName: "Ouaka", code: "CF-UK" }, + "CF-AC": { countryCode: "CF", subdivisionName: "Ouham", code: "CF-AC" }, + "CF-OP": { countryCode: "CF", subdivisionName: "Ouham-Pende", code: "CF-OP" }, + "CF-VK": { countryCode: "CF", subdivisionName: "Vakaga", code: "CF-VK" }, + "CG-11": { countryCode: "CG", subdivisionName: "Bouenza", code: "CG-11" }, + "CG-BZV": { + countryCode: "CG", + subdivisionName: "Brazzaville", + code: "CG-BZV", + }, + "CG-8": { countryCode: "CG", subdivisionName: "Cuvette", code: "CG-8" }, + "CG-9": { countryCode: "CG", subdivisionName: "Niari", code: "CG-9" }, + "CG-14": { countryCode: "CG", subdivisionName: "Plateaux", code: "CG-14" }, + "CG-16": { + countryCode: "CG", + subdivisionName: "Pointe-Noire", + code: "CG-16", + }, + "CG-13": { countryCode: "CG", subdivisionName: "Sangha", code: "CG-13" }, + "CH-AG": { countryCode: "CH", subdivisionName: "Aargau", code: "CH-AG" }, + "CH-AR": { + countryCode: "CH", + subdivisionName: "Appenzell Ausserrhoden", + code: "CH-AR", + }, + "CH-AI": { + countryCode: "CH", + subdivisionName: "Appenzell Innerrhoden", + code: "CH-AI", + }, + "CH-BL": { + countryCode: "CH", + subdivisionName: "Basel-Landschaft", + code: "CH-BL", + }, + "CH-BS": { countryCode: "CH", subdivisionName: "Basel-Stadt", code: "CH-BS" }, + "CH-BE": { countryCode: "CH", subdivisionName: "Bern", code: "CH-BE" }, + "CH-FR": { countryCode: "CH", subdivisionName: "Fribourg", code: "CH-FR" }, + "CH-GE": { countryCode: "CH", subdivisionName: "Geneve", code: "CH-GE" }, + "CH-GL": { countryCode: "CH", subdivisionName: "Glarus", code: "CH-GL" }, + "CH-GR": { countryCode: "CH", subdivisionName: "Graubunden", code: "CH-GR" }, + "CH-JU": { countryCode: "CH", subdivisionName: "Jura", code: "CH-JU" }, + "CH-LU": { countryCode: "CH", subdivisionName: "Luzern", code: "CH-LU" }, + "CH-NE": { countryCode: "CH", subdivisionName: "Neuchatel", code: "CH-NE" }, + "CH-NW": { countryCode: "CH", subdivisionName: "Nidwalden", code: "CH-NW" }, + "CH-OW": { countryCode: "CH", subdivisionName: "Obwalden", code: "CH-OW" }, + "CH-SG": { + countryCode: "CH", + subdivisionName: "Sankt Gallen", + code: "CH-SG", + }, + "CH-SH": { + countryCode: "CH", + subdivisionName: "Schaffhausen", + code: "CH-SH", + }, + "CH-SZ": { countryCode: "CH", subdivisionName: "Schwyz", code: "CH-SZ" }, + "CH-SO": { countryCode: "CH", subdivisionName: "Solothurn", code: "CH-SO" }, + "CH-TG": { countryCode: "CH", subdivisionName: "Thurgau", code: "CH-TG" }, + "CH-TI": { countryCode: "CH", subdivisionName: "Ticino", code: "CH-TI" }, + "CH-UR": { countryCode: "CH", subdivisionName: "Uri", code: "CH-UR" }, + "CH-VS": { countryCode: "CH", subdivisionName: "Valais", code: "CH-VS" }, + "CH-VD": { countryCode: "CH", subdivisionName: "Vaud", code: "CH-VD" }, + "CH-ZG": { countryCode: "CH", subdivisionName: "Zug", code: "CH-ZG" }, + "CH-ZH": { countryCode: "CH", subdivisionName: "Zurich", code: "CH-ZH" }, + "CI-AB": { countryCode: "CI", subdivisionName: "Abidjan", code: "CI-AB" }, + "CI-BS": { + countryCode: "CI", + subdivisionName: "Bas-Sassandra", + code: "CI-BS", + }, + "CI-CM": { countryCode: "CI", subdivisionName: "Comoe", code: "CI-CM" }, + "CI-DN": { countryCode: "CI", subdivisionName: "Denguele", code: "CI-DN" }, + "CI-GD": { countryCode: "CI", subdivisionName: "Goh-Djiboua", code: "CI-GD" }, + "CI-LC": { countryCode: "CI", subdivisionName: "Lacs", code: "CI-LC" }, + "CI-LG": { countryCode: "CI", subdivisionName: "Lagunes", code: "CI-LG" }, + "CI-MG": { countryCode: "CI", subdivisionName: "Montagnes", code: "CI-MG" }, + "CI-SM": { + countryCode: "CI", + subdivisionName: "Sassandra-Marahoue", + code: "CI-SM", + }, + "CI-SV": { countryCode: "CI", subdivisionName: "Savanes", code: "CI-SV" }, + "CI-VB": { + countryCode: "CI", + subdivisionName: "Vallee du Bandama", + code: "CI-VB", + }, + "CI-WR": { countryCode: "CI", subdivisionName: "Woroba", code: "CI-WR" }, + "CI-YM": { + countryCode: "CI", + subdivisionName: "Yamoussoukro", + code: "CI-YM", + }, + "CI-ZZ": { countryCode: "CI", subdivisionName: "Zanzan", code: "CI-ZZ" }, + "CL-AI": { + countryCode: "CL", + subdivisionName: "Aisen del General Carlos Ibanez del Campo", + code: "CL-AI", + }, + "CL-AN": { countryCode: "CL", subdivisionName: "Antofagasta", code: "CL-AN" }, + "CL-AP": { + countryCode: "CL", + subdivisionName: "Arica y Parinacota", + code: "CL-AP", + }, + "CL-AT": { countryCode: "CL", subdivisionName: "Atacama", code: "CL-AT" }, + "CL-BI": { countryCode: "CL", subdivisionName: "Biobio", code: "CL-BI" }, + "CL-CO": { countryCode: "CL", subdivisionName: "Coquimbo", code: "CL-CO" }, + "CL-AR": { + countryCode: "CL", + subdivisionName: "La Araucania", + code: "CL-AR", + }, + "CL-LI": { + countryCode: "CL", + subdivisionName: "Libertador General Bernardo O'Higgins", + code: "CL-LI", + }, + "CL-LL": { countryCode: "CL", subdivisionName: "Los Lagos", code: "CL-LL" }, + "CL-LR": { countryCode: "CL", subdivisionName: "Los Rios", code: "CL-LR" }, + "CL-MA": { countryCode: "CL", subdivisionName: "Magallanes", code: "CL-MA" }, + "CL-ML": { countryCode: "CL", subdivisionName: "Maule", code: "CL-ML" }, + "CL-NB": { countryCode: "CL", subdivisionName: "Nuble", code: "CL-NB" }, + "CL-RM": { + countryCode: "CL", + subdivisionName: "Region Metropolitana de Santiago", + code: "CL-RM", + }, + "CL-TA": { countryCode: "CL", subdivisionName: "Tarapaca", code: "CL-TA" }, + "CL-VS": { countryCode: "CL", subdivisionName: "Valparaiso", code: "CL-VS" }, + "CM-AD": { countryCode: "CM", subdivisionName: "Adamaoua", code: "CM-AD" }, + "CM-CE": { countryCode: "CM", subdivisionName: "Centre", code: "CM-CE" }, + "CM-ES": { countryCode: "CM", subdivisionName: "Est", code: "CM-ES" }, + "CM-EN": { + countryCode: "CM", + subdivisionName: "Extreme-Nord", + code: "CM-EN", + }, + "CM-LT": { countryCode: "CM", subdivisionName: "Littoral", code: "CM-LT" }, + "CM-NO": { countryCode: "CM", subdivisionName: "Nord", code: "CM-NO" }, + "CM-NW": { countryCode: "CM", subdivisionName: "Nord-Ouest", code: "CM-NW" }, + "CM-OU": { countryCode: "CM", subdivisionName: "Ouest", code: "CM-OU" }, + "CM-SU": { countryCode: "CM", subdivisionName: "Sud", code: "CM-SU" }, + "CM-SW": { countryCode: "CM", subdivisionName: "Sud-Ouest", code: "CM-SW" }, + "CN-AH": { countryCode: "CN", subdivisionName: "Anhui", code: "CN-AH" }, + "CN-BJ": { countryCode: "CN", subdivisionName: "Beijing", code: "CN-BJ" }, + "CN-CQ": { countryCode: "CN", subdivisionName: "Chongqing", code: "CN-CQ" }, + "CN-FJ": { countryCode: "CN", subdivisionName: "Fujian", code: "CN-FJ" }, + "CN-GS": { countryCode: "CN", subdivisionName: "Gansu", code: "CN-GS" }, + "CN-GD": { countryCode: "CN", subdivisionName: "Guangdong", code: "CN-GD" }, + "CN-GX": { + countryCode: "CN", + subdivisionName: "Guangxi Zhuangzu", + code: "CN-GX", + }, + "CN-GZ": { countryCode: "CN", subdivisionName: "Guizhou", code: "CN-GZ" }, + "CN-HI": { countryCode: "CN", subdivisionName: "Hainan", code: "CN-HI" }, + "CN-HE": { countryCode: "CN", subdivisionName: "Hebei", code: "CN-HE" }, + "CN-HL": { + countryCode: "CN", + subdivisionName: "Heilongjiang", + code: "CN-HL", + }, + "CN-HA": { countryCode: "CN", subdivisionName: "Henan", code: "CN-HA" }, + "CN-HB": { countryCode: "CN", subdivisionName: "Hubei", code: "CN-HB" }, + "CN-HN": { countryCode: "CN", subdivisionName: "Hunan", code: "CN-HN" }, + "CN-JS": { countryCode: "CN", subdivisionName: "Jiangsu", code: "CN-JS" }, + "CN-JX": { countryCode: "CN", subdivisionName: "Jiangxi", code: "CN-JX" }, + "CN-JL": { countryCode: "CN", subdivisionName: "Jilin", code: "CN-JL" }, + "CN-LN": { countryCode: "CN", subdivisionName: "Liaoning", code: "CN-LN" }, + "CN-NM": { countryCode: "CN", subdivisionName: "Nei Mongol", code: "CN-NM" }, + "CN-NX": { + countryCode: "CN", + subdivisionName: "Ningxia Huizu", + code: "CN-NX", + }, + "CN-QH": { countryCode: "CN", subdivisionName: "Qinghai", code: "CN-QH" }, + "CN-SN": { countryCode: "CN", subdivisionName: "Shaanxi", code: "CN-SN" }, + "CN-SD": { countryCode: "CN", subdivisionName: "Shandong", code: "CN-SD" }, + "CN-SH": { countryCode: "CN", subdivisionName: "Shanghai", code: "CN-SH" }, + "CN-SX": { countryCode: "CN", subdivisionName: "Shanxi", code: "CN-SX" }, + "CN-SC": { countryCode: "CN", subdivisionName: "Sichuan", code: "CN-SC" }, + "CN-TJ": { countryCode: "CN", subdivisionName: "Tianjin", code: "CN-TJ" }, + "CN-XJ": { + countryCode: "CN", + subdivisionName: "Xinjiang Uygur", + code: "CN-XJ", + }, + "CN-XZ": { countryCode: "CN", subdivisionName: "Xizang", code: "CN-XZ" }, + "CN-YN": { countryCode: "CN", subdivisionName: "Yunnan", code: "CN-YN" }, + "CN-ZJ": { countryCode: "CN", subdivisionName: "Zhejiang", code: "CN-ZJ" }, + "CO-AMA": { countryCode: "CO", subdivisionName: "Amazonas", code: "CO-AMA" }, + "CO-ANT": { countryCode: "CO", subdivisionName: "Antioquia", code: "CO-ANT" }, + "CO-ARA": { countryCode: "CO", subdivisionName: "Arauca", code: "CO-ARA" }, + "CO-ATL": { countryCode: "CO", subdivisionName: "Atlantico", code: "CO-ATL" }, + "CO-BOL": { countryCode: "CO", subdivisionName: "Bolivar", code: "CO-BOL" }, + "CO-BOY": { countryCode: "CO", subdivisionName: "Boyaca", code: "CO-BOY" }, + "CO-CAL": { countryCode: "CO", subdivisionName: "Caldas", code: "CO-CAL" }, + "CO-CAQ": { countryCode: "CO", subdivisionName: "Caqueta", code: "CO-CAQ" }, + "CO-CAS": { countryCode: "CO", subdivisionName: "Casanare", code: "CO-CAS" }, + "CO-CAU": { countryCode: "CO", subdivisionName: "Cauca", code: "CO-CAU" }, + "CO-CES": { countryCode: "CO", subdivisionName: "Cesar", code: "CO-CES" }, + "CO-CHO": { countryCode: "CO", subdivisionName: "Choco", code: "CO-CHO" }, + "CO-COR": { countryCode: "CO", subdivisionName: "Cordoba", code: "CO-COR" }, + "CO-CUN": { + countryCode: "CO", + subdivisionName: "Cundinamarca", + code: "CO-CUN", + }, + "CO-DC": { + countryCode: "CO", + subdivisionName: "Distrito Capital de Bogota", + code: "CO-DC", + }, + "CO-GUA": { countryCode: "CO", subdivisionName: "Guainia", code: "CO-GUA" }, + "CO-GUV": { countryCode: "CO", subdivisionName: "Guaviare", code: "CO-GUV" }, + "CO-HUI": { countryCode: "CO", subdivisionName: "Huila", code: "CO-HUI" }, + "CO-LAG": { + countryCode: "CO", + subdivisionName: "La Guajira", + code: "CO-LAG", + }, + "CO-MAG": { countryCode: "CO", subdivisionName: "Magdalena", code: "CO-MAG" }, + "CO-MET": { countryCode: "CO", subdivisionName: "Meta", code: "CO-MET" }, + "CO-NAR": { countryCode: "CO", subdivisionName: "Narino", code: "CO-NAR" }, + "CO-NSA": { + countryCode: "CO", + subdivisionName: "Norte de Santander", + code: "CO-NSA", + }, + "CO-PUT": { countryCode: "CO", subdivisionName: "Putumayo", code: "CO-PUT" }, + "CO-QUI": { countryCode: "CO", subdivisionName: "Quindio", code: "CO-QUI" }, + "CO-RIS": { countryCode: "CO", subdivisionName: "Risaralda", code: "CO-RIS" }, + "CO-SAP": { + countryCode: "CO", + subdivisionName: "San Andres, Providencia y Santa Catalina", + code: "CO-SAP", + }, + "CO-SAN": { countryCode: "CO", subdivisionName: "Santander", code: "CO-SAN" }, + "CO-SUC": { countryCode: "CO", subdivisionName: "Sucre", code: "CO-SUC" }, + "CO-TOL": { countryCode: "CO", subdivisionName: "Tolima", code: "CO-TOL" }, + "CO-VAC": { + countryCode: "CO", + subdivisionName: "Valle del Cauca", + code: "CO-VAC", + }, + "CO-VID": { countryCode: "CO", subdivisionName: "Vichada", code: "CO-VID" }, + "CR-A": { countryCode: "CR", subdivisionName: "Alajuela", code: "CR-A" }, + "CR-C": { countryCode: "CR", subdivisionName: "Cartago", code: "CR-C" }, + "CR-G": { countryCode: "CR", subdivisionName: "Guanacaste", code: "CR-G" }, + "CR-H": { countryCode: "CR", subdivisionName: "Heredia", code: "CR-H" }, + "CR-L": { countryCode: "CR", subdivisionName: "Limon", code: "CR-L" }, + "CR-P": { countryCode: "CR", subdivisionName: "Puntarenas", code: "CR-P" }, + "CR-SJ": { countryCode: "CR", subdivisionName: "San Jose", code: "CR-SJ" }, + "CU-15": { countryCode: "CU", subdivisionName: "Artemisa", code: "CU-15" }, + "CU-09": { countryCode: "CU", subdivisionName: "Camaguey", code: "CU-09" }, + "CU-08": { + countryCode: "CU", + subdivisionName: "Ciego de Avila", + code: "CU-08", + }, + "CU-06": { countryCode: "CU", subdivisionName: "Cienfuegos", code: "CU-06" }, + "CU-12": { countryCode: "CU", subdivisionName: "Granma", code: "CU-12" }, + "CU-14": { countryCode: "CU", subdivisionName: "Guantanamo", code: "CU-14" }, + "CU-11": { countryCode: "CU", subdivisionName: "Holguin", code: "CU-11" }, + "CU-99": { + countryCode: "CU", + subdivisionName: "Isla de la Juventud", + code: "CU-99", + }, + "CU-03": { countryCode: "CU", subdivisionName: "La Habana", code: "CU-03" }, + "CU-10": { countryCode: "CU", subdivisionName: "Las Tunas", code: "CU-10" }, + "CU-04": { countryCode: "CU", subdivisionName: "Matanzas", code: "CU-04" }, + "CU-16": { countryCode: "CU", subdivisionName: "Mayabeque", code: "CU-16" }, + "CU-01": { + countryCode: "CU", + subdivisionName: "Pinar del Rio", + code: "CU-01", + }, + "CU-07": { + countryCode: "CU", + subdivisionName: "Sancti Spiritus", + code: "CU-07", + }, + "CU-13": { + countryCode: "CU", + subdivisionName: "Santiago de Cuba", + code: "CU-13", + }, + "CU-05": { countryCode: "CU", subdivisionName: "Villa Clara", code: "CU-05" }, + "CV-BV": { countryCode: "CV", subdivisionName: "Boa Vista", code: "CV-BV" }, + "CV-BR": { countryCode: "CV", subdivisionName: "Brava", code: "CV-BR" }, + "CV-MO": { countryCode: "CV", subdivisionName: "Mosteiros", code: "CV-MO" }, + "CV-PN": { countryCode: "CV", subdivisionName: "Porto Novo", code: "CV-PN" }, + "CV-PR": { countryCode: "CV", subdivisionName: "Praia", code: "CV-PR" }, + "CV-RS": { + countryCode: "CV", + subdivisionName: "Ribeira Grande de Santiago", + code: "CV-RS", + }, + "CV-SL": { countryCode: "CV", subdivisionName: "Sal", code: "CV-SL" }, + "CV-SV": { countryCode: "CV", subdivisionName: "Sao Vicente", code: "CV-SV" }, + "CV-TA": { countryCode: "CV", subdivisionName: "Tarrafal", code: "CV-TA" }, + "CY-04": { countryCode: "CY", subdivisionName: "Ammochostos", code: "CY-04" }, + "CY-06": { countryCode: "CY", subdivisionName: "Keryneia", code: "CY-06" }, + "CY-03": { countryCode: "CY", subdivisionName: "Larnaka", code: "CY-03" }, + "CY-01": { countryCode: "CY", subdivisionName: "Lefkosia", code: "CY-01" }, + "CY-02": { countryCode: "CY", subdivisionName: "Lemesos", code: "CY-02" }, + "CY-05": { countryCode: "CY", subdivisionName: "Pafos", code: "CY-05" }, + "CZ-31": { + countryCode: "CZ", + subdivisionName: "Jihocesky kraj", + code: "CZ-31", + }, + "CZ-64": { + countryCode: "CZ", + subdivisionName: "Jihomoravsky kraj", + code: "CZ-64", + }, + "CZ-41": { + countryCode: "CZ", + subdivisionName: "Karlovarsky kraj", + code: "CZ-41", + }, + "CZ-63": { + countryCode: "CZ", + subdivisionName: "Kraj Vysocina", + code: "CZ-63", + }, + "CZ-52": { + countryCode: "CZ", + subdivisionName: "Kralovehradecky kraj", + code: "CZ-52", + }, + "CZ-51": { + countryCode: "CZ", + subdivisionName: "Liberecky kraj", + code: "CZ-51", + }, + "CZ-80": { + countryCode: "CZ", + subdivisionName: "Moravskoslezsky kraj", + code: "CZ-80", + }, + "CZ-71": { + countryCode: "CZ", + subdivisionName: "Olomoucky kraj", + code: "CZ-71", + }, + "CZ-53": { + countryCode: "CZ", + subdivisionName: "Pardubicky kraj", + code: "CZ-53", + }, + "CZ-32": { + countryCode: "CZ", + subdivisionName: "Plzensky kraj", + code: "CZ-32", + }, + "CZ-10": { + countryCode: "CZ", + subdivisionName: "Praha, Hlavni mesto", + code: "CZ-10", + }, + "CZ-20": { + countryCode: "CZ", + subdivisionName: "Stredocesky kraj", + code: "CZ-20", + }, + "CZ-42": { + countryCode: "CZ", + subdivisionName: "Ustecky kraj", + code: "CZ-42", + }, + "CZ-72": { + countryCode: "CZ", + subdivisionName: "Zlinsky kraj", + code: "CZ-72", + }, + "DE-BW": { + countryCode: "DE", + subdivisionName: "Baden-Wurttemberg", + code: "DE-BW", + }, + "DE-BY": { countryCode: "DE", subdivisionName: "Bayern", code: "DE-BY" }, + "DE-BE": { countryCode: "DE", subdivisionName: "Berlin", code: "DE-BE" }, + "DE-BB": { countryCode: "DE", subdivisionName: "Brandenburg", code: "DE-BB" }, + "DE-HB": { countryCode: "DE", subdivisionName: "Bremen", code: "DE-HB" }, + "DE-HH": { countryCode: "DE", subdivisionName: "Hamburg", code: "DE-HH" }, + "DE-HE": { countryCode: "DE", subdivisionName: "Hessen", code: "DE-HE" }, + "DE-MV": { + countryCode: "DE", + subdivisionName: "Mecklenburg-Vorpommern", + code: "DE-MV", + }, + "DE-NI": { + countryCode: "DE", + subdivisionName: "Niedersachsen", + code: "DE-NI", + }, + "DE-NW": { + countryCode: "DE", + subdivisionName: "Nordrhein-Westfalen", + code: "DE-NW", + }, + "DE-RP": { + countryCode: "DE", + subdivisionName: "Rheinland-Pfalz", + code: "DE-RP", + }, + "DE-SL": { countryCode: "DE", subdivisionName: "Saarland", code: "DE-SL" }, + "DE-SN": { countryCode: "DE", subdivisionName: "Sachsen", code: "DE-SN" }, + "DE-ST": { + countryCode: "DE", + subdivisionName: "Sachsen-Anhalt", + code: "DE-ST", + }, + "DE-SH": { + countryCode: "DE", + subdivisionName: "Schleswig-Holstein", + code: "DE-SH", + }, + "DE-TH": { countryCode: "DE", subdivisionName: "Thuringen", code: "DE-TH" }, + "DJ-AR": { countryCode: "DJ", subdivisionName: "Arta", code: "DJ-AR" }, + "DJ-DI": { countryCode: "DJ", subdivisionName: "Dikhil", code: "DJ-DI" }, + "DJ-DJ": { countryCode: "DJ", subdivisionName: "Djibouti", code: "DJ-DJ" }, + "DK-84": { countryCode: "DK", subdivisionName: "Hovedstaden", code: "DK-84" }, + "DK-82": { countryCode: "DK", subdivisionName: "Midtjylland", code: "DK-82" }, + "DK-81": { countryCode: "DK", subdivisionName: "Nordjylland", code: "DK-81" }, + "DK-85": { countryCode: "DK", subdivisionName: "Sjaelland", code: "DK-85" }, + "DK-83": { countryCode: "DK", subdivisionName: "Syddanmark", code: "DK-83" }, + "DM-02": { + countryCode: "DM", + subdivisionName: "Saint Andrew", + code: "DM-02", + }, + "DM-04": { + countryCode: "DM", + subdivisionName: "Saint George", + code: "DM-04", + }, + "DM-05": { countryCode: "DM", subdivisionName: "Saint John", code: "DM-05" }, + "DM-06": { + countryCode: "DM", + subdivisionName: "Saint Joseph", + code: "DM-06", + }, + "DM-07": { countryCode: "DM", subdivisionName: "Saint Luke", code: "DM-07" }, + "DM-09": { + countryCode: "DM", + subdivisionName: "Saint Patrick", + code: "DM-09", + }, + "DM-10": { countryCode: "DM", subdivisionName: "Saint Paul", code: "DM-10" }, + "DO-02": { countryCode: "DO", subdivisionName: "Azua", code: "DO-02" }, + "DO-03": { countryCode: "DO", subdivisionName: "Baoruco", code: "DO-03" }, + "DO-04": { countryCode: "DO", subdivisionName: "Barahona", code: "DO-04" }, + "DO-05": { countryCode: "DO", subdivisionName: "Dajabon", code: "DO-05" }, + "DO-01": { + countryCode: "DO", + subdivisionName: "Distrito Nacional (Santo Domingo)", + code: "DO-01", + }, + "DO-06": { countryCode: "DO", subdivisionName: "Duarte", code: "DO-06" }, + "DO-08": { countryCode: "DO", subdivisionName: "El Seibo", code: "DO-08" }, + "DO-07": { countryCode: "DO", subdivisionName: "Elias Pina", code: "DO-07" }, + "DO-09": { countryCode: "DO", subdivisionName: "Espaillat", code: "DO-09" }, + "DO-30": { countryCode: "DO", subdivisionName: "Hato Mayor", code: "DO-30" }, + "DO-19": { + countryCode: "DO", + subdivisionName: "Hermanas Mirabal", + code: "DO-19", + }, + "DO-10": { + countryCode: "DO", + subdivisionName: "Independencia", + code: "DO-10", + }, + "DO-11": { + countryCode: "DO", + subdivisionName: "La Altagracia", + code: "DO-11", + }, + "DO-12": { countryCode: "DO", subdivisionName: "La Romana", code: "DO-12" }, + "DO-13": { countryCode: "DO", subdivisionName: "La Vega", code: "DO-13" }, + "DO-14": { + countryCode: "DO", + subdivisionName: "Maria Trinidad Sanchez", + code: "DO-14", + }, + "DO-28": { + countryCode: "DO", + subdivisionName: "Monsenor Nouel", + code: "DO-28", + }, + "DO-15": { + countryCode: "DO", + subdivisionName: "Monte Cristi", + code: "DO-15", + }, + "DO-29": { countryCode: "DO", subdivisionName: "Monte Plata", code: "DO-29" }, + "DO-16": { countryCode: "DO", subdivisionName: "Pedernales", code: "DO-16" }, + "DO-17": { countryCode: "DO", subdivisionName: "Peravia", code: "DO-17" }, + "DO-18": { + countryCode: "DO", + subdivisionName: "Puerto Plata", + code: "DO-18", + }, + "DO-20": { countryCode: "DO", subdivisionName: "Samana", code: "DO-20" }, + "DO-21": { + countryCode: "DO", + subdivisionName: "San Cristobal", + code: "DO-21", + }, + "DO-31": { + countryCode: "DO", + subdivisionName: "San Jose de Ocoa", + code: "DO-31", + }, + "DO-22": { countryCode: "DO", subdivisionName: "San Juan", code: "DO-22" }, + "DO-23": { + countryCode: "DO", + subdivisionName: "San Pedro de Macoris", + code: "DO-23", + }, + "DO-24": { + countryCode: "DO", + subdivisionName: "Sanchez Ramirez", + code: "DO-24", + }, + "DO-25": { countryCode: "DO", subdivisionName: "Santiago", code: "DO-25" }, + "DO-26": { + countryCode: "DO", + subdivisionName: "Santiago Rodriguez", + code: "DO-26", + }, + "DO-27": { countryCode: "DO", subdivisionName: "Valverde", code: "DO-27" }, + "DZ-01": { countryCode: "DZ", subdivisionName: "Adrar", code: "DZ-01" }, + "DZ-44": { countryCode: "DZ", subdivisionName: "Ain Defla", code: "DZ-44" }, + "DZ-46": { + countryCode: "DZ", + subdivisionName: "Ain Temouchent", + code: "DZ-46", + }, + "DZ-16": { countryCode: "DZ", subdivisionName: "Alger", code: "DZ-16" }, + "DZ-23": { countryCode: "DZ", subdivisionName: "Annaba", code: "DZ-23" }, + "DZ-05": { countryCode: "DZ", subdivisionName: "Batna", code: "DZ-05" }, + "DZ-08": { countryCode: "DZ", subdivisionName: "Bechar", code: "DZ-08" }, + "DZ-06": { countryCode: "DZ", subdivisionName: "Bejaia", code: "DZ-06" }, + "DZ-07": { countryCode: "DZ", subdivisionName: "Biskra", code: "DZ-07" }, + "DZ-09": { countryCode: "DZ", subdivisionName: "Blida", code: "DZ-09" }, + "DZ-34": { + countryCode: "DZ", + subdivisionName: "Bordj Bou Arreridj", + code: "DZ-34", + }, + "DZ-10": { countryCode: "DZ", subdivisionName: "Bouira", code: "DZ-10" }, + "DZ-35": { countryCode: "DZ", subdivisionName: "Boumerdes", code: "DZ-35" }, + "DZ-02": { countryCode: "DZ", subdivisionName: "Chlef", code: "DZ-02" }, + "DZ-25": { countryCode: "DZ", subdivisionName: "Constantine", code: "DZ-25" }, + "DZ-56": { countryCode: "DZ", subdivisionName: "Djanet", code: "DZ-56" }, + "DZ-17": { countryCode: "DZ", subdivisionName: "Djelfa", code: "DZ-17" }, + "DZ-32": { countryCode: "DZ", subdivisionName: "El Bayadh", code: "DZ-32" }, + "DZ-57": { countryCode: "DZ", subdivisionName: "El Meghaier", code: "DZ-57" }, + "DZ-39": { countryCode: "DZ", subdivisionName: "El Oued", code: "DZ-39" }, + "DZ-36": { countryCode: "DZ", subdivisionName: "El Tarf", code: "DZ-36" }, + "DZ-47": { countryCode: "DZ", subdivisionName: "Ghardaia", code: "DZ-47" }, + "DZ-24": { countryCode: "DZ", subdivisionName: "Guelma", code: "DZ-24" }, + "DZ-33": { countryCode: "DZ", subdivisionName: "Illizi", code: "DZ-33" }, + "DZ-53": { countryCode: "DZ", subdivisionName: "In Salah", code: "DZ-53" }, + "DZ-18": { countryCode: "DZ", subdivisionName: "Jijel", code: "DZ-18" }, + "DZ-40": { countryCode: "DZ", subdivisionName: "Khenchela", code: "DZ-40" }, + "DZ-03": { countryCode: "DZ", subdivisionName: "Laghouat", code: "DZ-03" }, + "DZ-28": { countryCode: "DZ", subdivisionName: "M'sila", code: "DZ-28" }, + "DZ-29": { countryCode: "DZ", subdivisionName: "Mascara", code: "DZ-29" }, + "DZ-26": { countryCode: "DZ", subdivisionName: "Medea", code: "DZ-26" }, + "DZ-43": { countryCode: "DZ", subdivisionName: "Mila", code: "DZ-43" }, + "DZ-27": { countryCode: "DZ", subdivisionName: "Mostaganem", code: "DZ-27" }, + "DZ-45": { countryCode: "DZ", subdivisionName: "Naama", code: "DZ-45" }, + "DZ-31": { countryCode: "DZ", subdivisionName: "Oran", code: "DZ-31" }, + "DZ-30": { countryCode: "DZ", subdivisionName: "Ouargla", code: "DZ-30" }, + "DZ-51": { + countryCode: "DZ", + subdivisionName: "Ouled Djellal", + code: "DZ-51", + }, + "DZ-04": { + countryCode: "DZ", + subdivisionName: "Oum el Bouaghi", + code: "DZ-04", + }, + "DZ-48": { countryCode: "DZ", subdivisionName: "Relizane", code: "DZ-48" }, + "DZ-20": { countryCode: "DZ", subdivisionName: "Saida", code: "DZ-20" }, + "DZ-19": { countryCode: "DZ", subdivisionName: "Setif", code: "DZ-19" }, + "DZ-22": { + countryCode: "DZ", + subdivisionName: "Sidi Bel Abbes", + code: "DZ-22", + }, + "DZ-21": { countryCode: "DZ", subdivisionName: "Skikda", code: "DZ-21" }, + "DZ-41": { countryCode: "DZ", subdivisionName: "Souk Ahras", code: "DZ-41" }, + "DZ-11": { countryCode: "DZ", subdivisionName: "Tamanrasset", code: "DZ-11" }, + "DZ-12": { countryCode: "DZ", subdivisionName: "Tebessa", code: "DZ-12" }, + "DZ-14": { countryCode: "DZ", subdivisionName: "Tiaret", code: "DZ-14" }, + "DZ-49": { countryCode: "DZ", subdivisionName: "Timimoun", code: "DZ-49" }, + "DZ-37": { countryCode: "DZ", subdivisionName: "Tindouf", code: "DZ-37" }, + "DZ-42": { countryCode: "DZ", subdivisionName: "Tipaza", code: "DZ-42" }, + "DZ-38": { countryCode: "DZ", subdivisionName: "Tissemsilt", code: "DZ-38" }, + "DZ-15": { countryCode: "DZ", subdivisionName: "Tizi Ouzou", code: "DZ-15" }, + "DZ-13": { countryCode: "DZ", subdivisionName: "Tlemcen", code: "DZ-13" }, + "DZ-55": { countryCode: "DZ", subdivisionName: "Touggourt", code: "DZ-55" }, + "EC-A": { countryCode: "EC", subdivisionName: "Azuay", code: "EC-A" }, + "EC-B": { countryCode: "EC", subdivisionName: "Bolivar", code: "EC-B" }, + "EC-F": { countryCode: "EC", subdivisionName: "Canar", code: "EC-F" }, + "EC-C": { countryCode: "EC", subdivisionName: "Carchi", code: "EC-C" }, + "EC-H": { countryCode: "EC", subdivisionName: "Chimborazo", code: "EC-H" }, + "EC-X": { countryCode: "EC", subdivisionName: "Cotopaxi", code: "EC-X" }, + "EC-O": { countryCode: "EC", subdivisionName: "El Oro", code: "EC-O" }, + "EC-E": { countryCode: "EC", subdivisionName: "Esmeraldas", code: "EC-E" }, + "EC-W": { countryCode: "EC", subdivisionName: "Galapagos", code: "EC-W" }, + "EC-G": { countryCode: "EC", subdivisionName: "Guayas", code: "EC-G" }, + "EC-I": { countryCode: "EC", subdivisionName: "Imbabura", code: "EC-I" }, + "EC-L": { countryCode: "EC", subdivisionName: "Loja", code: "EC-L" }, + "EC-R": { countryCode: "EC", subdivisionName: "Los Rios", code: "EC-R" }, + "EC-M": { countryCode: "EC", subdivisionName: "Manabi", code: "EC-M" }, + "EC-S": { + countryCode: "EC", + subdivisionName: "Morona Santiago", + code: "EC-S", + }, + "EC-N": { countryCode: "EC", subdivisionName: "Napo", code: "EC-N" }, + "EC-D": { countryCode: "EC", subdivisionName: "Orellana", code: "EC-D" }, + "EC-Y": { countryCode: "EC", subdivisionName: "Pastaza", code: "EC-Y" }, + "EC-P": { countryCode: "EC", subdivisionName: "Pichincha", code: "EC-P" }, + "EC-SE": { countryCode: "EC", subdivisionName: "Santa Elena", code: "EC-SE" }, + "EC-SD": { + countryCode: "EC", + subdivisionName: "Santo Domingo de los Tsachilas", + code: "EC-SD", + }, + "EC-U": { countryCode: "EC", subdivisionName: "Sucumbios", code: "EC-U" }, + "EC-T": { countryCode: "EC", subdivisionName: "Tungurahua", code: "EC-T" }, + "EC-Z": { + countryCode: "EC", + subdivisionName: "Zamora Chinchipe", + code: "EC-Z", + }, + "EE-37": { countryCode: "EE", subdivisionName: "Harjumaa", code: "EE-37" }, + "EE-39": { countryCode: "EE", subdivisionName: "Hiiumaa", code: "EE-39" }, + "EE-45": { countryCode: "EE", subdivisionName: "Ida-Virumaa", code: "EE-45" }, + "EE-52": { countryCode: "EE", subdivisionName: "Jarvamaa", code: "EE-52" }, + "EE-50": { countryCode: "EE", subdivisionName: "Jogevamaa", code: "EE-50" }, + "EE-60": { + countryCode: "EE", + subdivisionName: "Laane-Virumaa", + code: "EE-60", + }, + "EE-56": { countryCode: "EE", subdivisionName: "Laanemaa", code: "EE-56" }, + "EE-68": { countryCode: "EE", subdivisionName: "Parnumaa", code: "EE-68" }, + "EE-64": { countryCode: "EE", subdivisionName: "Polvamaa", code: "EE-64" }, + "EE-71": { countryCode: "EE", subdivisionName: "Raplamaa", code: "EE-71" }, + "EE-74": { countryCode: "EE", subdivisionName: "Saaremaa", code: "EE-74" }, + "EE-79": { countryCode: "EE", subdivisionName: "Tartumaa", code: "EE-79" }, + "EE-81": { countryCode: "EE", subdivisionName: "Valgamaa", code: "EE-81" }, + "EE-84": { countryCode: "EE", subdivisionName: "Viljandimaa", code: "EE-84" }, + "EE-87": { countryCode: "EE", subdivisionName: "Vorumaa", code: "EE-87" }, + "EG-DK": { + countryCode: "EG", + subdivisionName: "Ad Daqahliyah", + code: "EG-DK", + }, + "EG-BA": { + countryCode: "EG", + subdivisionName: "Al Bahr al Ahmar", + code: "EG-BA", + }, + "EG-BH": { countryCode: "EG", subdivisionName: "Al Buhayrah", code: "EG-BH" }, + "EG-FYM": { countryCode: "EG", subdivisionName: "Al Fayyum", code: "EG-FYM" }, + "EG-GH": { + countryCode: "EG", + subdivisionName: "Al Gharbiyah", + code: "EG-GH", + }, + "EG-ALX": { + countryCode: "EG", + subdivisionName: "Al Iskandariyah", + code: "EG-ALX", + }, + "EG-IS": { + countryCode: "EG", + subdivisionName: "Al Isma'iliyah", + code: "EG-IS", + }, + "EG-GZ": { countryCode: "EG", subdivisionName: "Al Jizah", code: "EG-GZ" }, + "EG-MNF": { + countryCode: "EG", + subdivisionName: "Al Minufiyah", + code: "EG-MNF", + }, + "EG-MN": { countryCode: "EG", subdivisionName: "Al Minya", code: "EG-MN" }, + "EG-C": { countryCode: "EG", subdivisionName: "Al Qahirah", code: "EG-C" }, + "EG-KB": { + countryCode: "EG", + subdivisionName: "Al Qalyubiyah", + code: "EG-KB", + }, + "EG-LX": { countryCode: "EG", subdivisionName: "Al Uqsur", code: "EG-LX" }, + "EG-WAD": { + countryCode: "EG", + subdivisionName: "Al Wadi al Jadid", + code: "EG-WAD", + }, + "EG-SUZ": { countryCode: "EG", subdivisionName: "As Suways", code: "EG-SUZ" }, + "EG-SHR": { + countryCode: "EG", + subdivisionName: "Ash Sharqiyah", + code: "EG-SHR", + }, + "EG-ASN": { countryCode: "EG", subdivisionName: "Aswan", code: "EG-ASN" }, + "EG-AST": { countryCode: "EG", subdivisionName: "Asyut", code: "EG-AST" }, + "EG-BNS": { + countryCode: "EG", + subdivisionName: "Bani Suwayf", + code: "EG-BNS", + }, + "EG-PTS": { countryCode: "EG", subdivisionName: "Bur Sa'id", code: "EG-PTS" }, + "EG-DT": { countryCode: "EG", subdivisionName: "Dumyat", code: "EG-DT" }, + "EG-JS": { countryCode: "EG", subdivisionName: "Janub Sina'", code: "EG-JS" }, + "EG-KFS": { + countryCode: "EG", + subdivisionName: "Kafr ash Shaykh", + code: "EG-KFS", + }, + "EG-MT": { countryCode: "EG", subdivisionName: "Matruh", code: "EG-MT" }, + "EG-KN": { countryCode: "EG", subdivisionName: "Qina", code: "EG-KN" }, + "EG-SIN": { + countryCode: "EG", + subdivisionName: "Shamal Sina'", + code: "EG-SIN", + }, + "EG-SHG": { countryCode: "EG", subdivisionName: "Suhaj", code: "EG-SHG" }, + "ER-MA": { countryCode: "ER", subdivisionName: "Al Awsat", code: "ER-MA" }, + "ER-GB": { countryCode: "ER", subdivisionName: "Qash-Barkah", code: "ER-GB" }, + "ES-AN": { countryCode: "ES", subdivisionName: "Andalucia", code: "ES-AN" }, + "ES-AR": { countryCode: "ES", subdivisionName: "Aragon", code: "ES-AR" }, + "ES-AS": { + countryCode: "ES", + subdivisionName: "Asturias, Principado de", + code: "ES-AS", + }, + "ES-CN": { countryCode: "ES", subdivisionName: "Canarias", code: "ES-CN" }, + "ES-CB": { countryCode: "ES", subdivisionName: "Cantabria", code: "ES-CB" }, + "ES-CL": { + countryCode: "ES", + subdivisionName: "Castilla y Leon", + code: "ES-CL", + }, + "ES-CM": { + countryCode: "ES", + subdivisionName: "Castilla-La Mancha", + code: "ES-CM", + }, + "ES-CT": { countryCode: "ES", subdivisionName: "Catalunya", code: "ES-CT" }, + "ES-CE": { countryCode: "ES", subdivisionName: "Ceuta", code: "ES-CE" }, + "ES-EX": { countryCode: "ES", subdivisionName: "Extremadura", code: "ES-EX" }, + "ES-GA": { countryCode: "ES", subdivisionName: "Galicia", code: "ES-GA" }, + "ES-IB": { + countryCode: "ES", + subdivisionName: "Illes Balears", + code: "ES-IB", + }, + "ES-RI": { countryCode: "ES", subdivisionName: "La Rioja", code: "ES-RI" }, + "ES-MD": { + countryCode: "ES", + subdivisionName: "Madrid, Comunidad de", + code: "ES-MD", + }, + "ES-ML": { countryCode: "ES", subdivisionName: "Melilla", code: "ES-ML" }, + "ES-MC": { + countryCode: "ES", + subdivisionName: "Murcia, Region de", + code: "ES-MC", + }, + "ES-NC": { + countryCode: "ES", + subdivisionName: "Navarra, Comunidad Foral de", + code: "ES-NC", + }, + "ES-PV": { countryCode: "ES", subdivisionName: "Pais Vasco", code: "ES-PV" }, + "ES-VC": { + countryCode: "ES", + subdivisionName: "Valenciana, Comunidad", + code: "ES-VC", + }, + "ET-AA": { countryCode: "ET", subdivisionName: "Addis Ababa", code: "ET-AA" }, + "ET-AF": { countryCode: "ET", subdivisionName: "Afar", code: "ET-AF" }, + "ET-AM": { countryCode: "ET", subdivisionName: "Amara", code: "ET-AM" }, + "ET-BE": { + countryCode: "ET", + subdivisionName: "Benshangul-Gumaz", + code: "ET-BE", + }, + "ET-DD": { countryCode: "ET", subdivisionName: "Dire Dawa", code: "ET-DD" }, + "ET-HA": { + countryCode: "ET", + subdivisionName: "Harari People", + code: "ET-HA", + }, + "ET-OR": { countryCode: "ET", subdivisionName: "Oromia", code: "ET-OR" }, + "ET-SO": { countryCode: "ET", subdivisionName: "Somali", code: "ET-SO" }, + "ET-SN": { + countryCode: "ET", + subdivisionName: "Southern Nations, Nationalities and Peoples", + code: "ET-SN", + }, + "ET-TI": { countryCode: "ET", subdivisionName: "Tigrai", code: "ET-TI" }, + "FI-02": { + countryCode: "FI", + subdivisionName: "Etela-Karjala", + code: "FI-02", + }, + "FI-03": { + countryCode: "FI", + subdivisionName: "Etela-Pohjanmaa", + code: "FI-03", + }, + "FI-04": { countryCode: "FI", subdivisionName: "Etela-Savo", code: "FI-04" }, + "FI-05": { countryCode: "FI", subdivisionName: "Kainuu", code: "FI-05" }, + "FI-06": { countryCode: "FI", subdivisionName: "Kanta-Hame", code: "FI-06" }, + "FI-07": { + countryCode: "FI", + subdivisionName: "Keski-Pohjanmaa", + code: "FI-07", + }, + "FI-08": { countryCode: "FI", subdivisionName: "Keski-Suomi", code: "FI-08" }, + "FI-09": { countryCode: "FI", subdivisionName: "Kymenlaakso", code: "FI-09" }, + "FI-10": { countryCode: "FI", subdivisionName: "Lappi", code: "FI-10" }, + "FI-16": { countryCode: "FI", subdivisionName: "Paijat-Hame", code: "FI-16" }, + "FI-11": { countryCode: "FI", subdivisionName: "Pirkanmaa", code: "FI-11" }, + "FI-12": { countryCode: "FI", subdivisionName: "Pohjanmaa", code: "FI-12" }, + "FI-13": { + countryCode: "FI", + subdivisionName: "Pohjois-Karjala", + code: "FI-13", + }, + "FI-14": { + countryCode: "FI", + subdivisionName: "Pohjois-Pohjanmaa", + code: "FI-14", + }, + "FI-15": { + countryCode: "FI", + subdivisionName: "Pohjois-Savo", + code: "FI-15", + }, + "FI-17": { countryCode: "FI", subdivisionName: "Satakunta", code: "FI-17" }, + "FI-18": { countryCode: "FI", subdivisionName: "Uusimaa", code: "FI-18" }, + "FI-19": { + countryCode: "FI", + subdivisionName: "Varsinais-Suomi", + code: "FI-19", + }, + "FJ-C": { countryCode: "FJ", subdivisionName: "Central", code: "FJ-C" }, + "FJ-E": { countryCode: "FJ", subdivisionName: "Eastern", code: "FJ-E" }, + "FJ-N": { countryCode: "FJ", subdivisionName: "Northern", code: "FJ-N" }, + "FJ-R": { countryCode: "FJ", subdivisionName: "Rotuma", code: "FJ-R" }, + "FJ-W": { countryCode: "FJ", subdivisionName: "Western", code: "FJ-W" }, + "FM-TRK": { countryCode: "FM", subdivisionName: "Chuuk", code: "FM-TRK" }, + "FM-KSA": { countryCode: "FM", subdivisionName: "Kosrae", code: "FM-KSA" }, + "FM-PNI": { countryCode: "FM", subdivisionName: "Pohnpei", code: "FM-PNI" }, + "FM-YAP": { countryCode: "FM", subdivisionName: "Yap", code: "FM-YAP" }, + "FR-ARA": { + countryCode: "FR", + subdivisionName: "Auvergne-Rhone-Alpes", + code: "FR-ARA", + }, + "FR-BFC": { + countryCode: "FR", + subdivisionName: "Bourgogne-Franche-Comte", + code: "FR-BFC", + }, + "FR-BRE": { countryCode: "FR", subdivisionName: "Bretagne", code: "FR-BRE" }, + "FR-CVL": { + countryCode: "FR", + subdivisionName: "Centre-Val de Loire", + code: "FR-CVL", + }, + "FR-20R": { countryCode: "FR", subdivisionName: "Corse", code: "FR-20R" }, + "FR-GES": { countryCode: "FR", subdivisionName: "Grand-Est", code: "FR-GES" }, + "FR-HDF": { + countryCode: "FR", + subdivisionName: "Hauts-de-France", + code: "FR-HDF", + }, + "FR-IDF": { + countryCode: "FR", + subdivisionName: "Ile-de-France", + code: "FR-IDF", + }, + "FR-NOR": { countryCode: "FR", subdivisionName: "Normandie", code: "FR-NOR" }, + "FR-NAQ": { + countryCode: "FR", + subdivisionName: "Nouvelle-Aquitaine", + code: "FR-NAQ", + }, + "FR-OCC": { countryCode: "FR", subdivisionName: "Occitanie", code: "FR-OCC" }, + "FR-PDL": { + countryCode: "FR", + subdivisionName: "Pays-de-la-Loire", + code: "FR-PDL", + }, + "FR-PAC": { + countryCode: "FR", + subdivisionName: "Provence-Alpes-Cote-d'Azur", + code: "FR-PAC", + }, + "GA-1": { countryCode: "GA", subdivisionName: "Estuaire", code: "GA-1" }, + "GA-2": { countryCode: "GA", subdivisionName: "Haut-Ogooue", code: "GA-2" }, + "GA-3": { countryCode: "GA", subdivisionName: "Moyen-Ogooue", code: "GA-3" }, + "GA-4": { countryCode: "GA", subdivisionName: "Ngounie", code: "GA-4" }, + "GA-5": { countryCode: "GA", subdivisionName: "Nyanga", code: "GA-5" }, + "GA-8": { + countryCode: "GA", + subdivisionName: "Ogooue-Maritime", + code: "GA-8", + }, + "GA-9": { countryCode: "GA", subdivisionName: "Woleu-Ntem", code: "GA-9" }, + "GB-ENG": { countryCode: "GB", subdivisionName: "England", code: "GB-ENG" }, + "GB-NIR": { + countryCode: "GB", + subdivisionName: "Northern Ireland", + code: "GB-NIR", + }, + "GB-SCT": { countryCode: "GB", subdivisionName: "Scotland", code: "GB-SCT" }, + "GB-WLS": { countryCode: "GB", subdivisionName: "Wales", code: "GB-WLS" }, + "GD-01": { + countryCode: "GD", + subdivisionName: "Saint Andrew", + code: "GD-01", + }, + "GD-02": { countryCode: "GD", subdivisionName: "Saint David", code: "GD-02" }, + "GD-03": { + countryCode: "GD", + subdivisionName: "Saint George", + code: "GD-03", + }, + "GD-04": { countryCode: "GD", subdivisionName: "Saint John", code: "GD-04" }, + "GD-05": { countryCode: "GD", subdivisionName: "Saint Mark", code: "GD-05" }, + "GD-06": { + countryCode: "GD", + subdivisionName: "Saint Patrick", + code: "GD-06", + }, + "GD-10": { + countryCode: "GD", + subdivisionName: "Southern Grenadine Islands", + code: "GD-10", + }, + "GE-AB": { countryCode: "GE", subdivisionName: "Abkhazia", code: "GE-AB" }, + "GE-AJ": { countryCode: "GE", subdivisionName: "Ajaria", code: "GE-AJ" }, + "GE-GU": { countryCode: "GE", subdivisionName: "Guria", code: "GE-GU" }, + "GE-IM": { countryCode: "GE", subdivisionName: "Imereti", code: "GE-IM" }, + "GE-KA": { countryCode: "GE", subdivisionName: "K'akheti", code: "GE-KA" }, + "GE-KK": { + countryCode: "GE", + subdivisionName: "Kvemo Kartli", + code: "GE-KK", + }, + "GE-MM": { + countryCode: "GE", + subdivisionName: "Mtskheta-Mtianeti", + code: "GE-MM", + }, + "GE-RL": { + countryCode: "GE", + subdivisionName: "Rach'a-Lechkhumi-Kvemo Svaneti", + code: "GE-RL", + }, + "GE-SZ": { + countryCode: "GE", + subdivisionName: "Samegrelo-Zemo Svaneti", + code: "GE-SZ", + }, + "GE-SJ": { + countryCode: "GE", + subdivisionName: "Samtskhe-Javakheti", + code: "GE-SJ", + }, + "GE-SK": { + countryCode: "GE", + subdivisionName: "Shida Kartli", + code: "GE-SK", + }, + "GE-TB": { countryCode: "GE", subdivisionName: "Tbilisi", code: "GE-TB" }, + "GH-AF": { countryCode: "GH", subdivisionName: "Ahafo", code: "GH-AF" }, + "GH-AH": { countryCode: "GH", subdivisionName: "Ashanti", code: "GH-AH" }, + "GH-BO": { countryCode: "GH", subdivisionName: "Bono", code: "GH-BO" }, + "GH-BE": { countryCode: "GH", subdivisionName: "Bono East", code: "GH-BE" }, + "GH-CP": { countryCode: "GH", subdivisionName: "Central", code: "GH-CP" }, + "GH-EP": { countryCode: "GH", subdivisionName: "Eastern", code: "GH-EP" }, + "GH-AA": { + countryCode: "GH", + subdivisionName: "Greater Accra", + code: "GH-AA", + }, + "GH-NP": { countryCode: "GH", subdivisionName: "Northern", code: "GH-NP" }, + "GH-UE": { countryCode: "GH", subdivisionName: "Upper East", code: "GH-UE" }, + "GH-UW": { countryCode: "GH", subdivisionName: "Upper West", code: "GH-UW" }, + "GH-TV": { countryCode: "GH", subdivisionName: "Volta", code: "GH-TV" }, + "GH-WP": { countryCode: "GH", subdivisionName: "Western", code: "GH-WP" }, + "GL-AV": { + countryCode: "GL", + subdivisionName: "Avannaata Kommunia", + code: "GL-AV", + }, + "GL-KU": { + countryCode: "GL", + subdivisionName: "Kommune Kujalleq", + code: "GL-KU", + }, + "GL-QT": { + countryCode: "GL", + subdivisionName: "Kommune Qeqertalik", + code: "GL-QT", + }, + "GL-SM": { + countryCode: "GL", + subdivisionName: "Kommuneqarfik Sermersooq", + code: "GL-SM", + }, + "GL-QE": { + countryCode: "GL", + subdivisionName: "Qeqqata Kommunia", + code: "GL-QE", + }, + "GM-B": { countryCode: "GM", subdivisionName: "Banjul", code: "GM-B" }, + "GM-M": { countryCode: "GM", subdivisionName: "Central River", code: "GM-M" }, + "GM-L": { countryCode: "GM", subdivisionName: "Lower River", code: "GM-L" }, + "GM-N": { countryCode: "GM", subdivisionName: "North Bank", code: "GM-N" }, + "GM-U": { countryCode: "GM", subdivisionName: "Upper River", code: "GM-U" }, + "GM-W": { countryCode: "GM", subdivisionName: "Western", code: "GM-W" }, + "GN-BF": { countryCode: "GN", subdivisionName: "Boffa", code: "GN-BF" }, + "GN-BK": { countryCode: "GN", subdivisionName: "Boke", code: "GN-BK" }, + "GN-C": { countryCode: "GN", subdivisionName: "Conakry", code: "GN-C" }, + "GN-DB": { countryCode: "GN", subdivisionName: "Dabola", code: "GN-DB" }, + "GN-DL": { countryCode: "GN", subdivisionName: "Dalaba", code: "GN-DL" }, + "GN-DI": { countryCode: "GN", subdivisionName: "Dinguiraye", code: "GN-DI" }, + "GN-FR": { countryCode: "GN", subdivisionName: "Fria", code: "GN-FR" }, + "GN-KA": { countryCode: "GN", subdivisionName: "Kankan", code: "GN-KA" }, + "GN-KO": { countryCode: "GN", subdivisionName: "Kouroussa", code: "GN-KO" }, + "GN-LA": { countryCode: "GN", subdivisionName: "Labe", code: "GN-LA" }, + "GN-SI": { countryCode: "GN", subdivisionName: "Siguiri", code: "GN-SI" }, + "GQ-AN": { countryCode: "GQ", subdivisionName: "Annobon", code: "GQ-AN" }, + "GQ-BN": { countryCode: "GQ", subdivisionName: "Bioko Norte", code: "GQ-BN" }, + "GQ-CS": { countryCode: "GQ", subdivisionName: "Centro Sur", code: "GQ-CS" }, + "GQ-KN": { countryCode: "GQ", subdivisionName: "Kie-Ntem", code: "GQ-KN" }, + "GQ-LI": { countryCode: "GQ", subdivisionName: "Litoral", code: "GQ-LI" }, + "GQ-WN": { countryCode: "GQ", subdivisionName: "Wele-Nzas", code: "GQ-WN" }, + "GR-A": { + countryCode: "GR", + subdivisionName: "Anatoliki Makedonia kai Thraki", + code: "GR-A", + }, + "GR-I": { countryCode: "GR", subdivisionName: "Attiki", code: "GR-I" }, + "GR-G": { countryCode: "GR", subdivisionName: "Dytiki Ellada", code: "GR-G" }, + "GR-C": { + countryCode: "GR", + subdivisionName: "Dytiki Makedonia", + code: "GR-C", + }, + "GR-F": { countryCode: "GR", subdivisionName: "Ionia Nisia", code: "GR-F" }, + "GR-D": { countryCode: "GR", subdivisionName: "Ipeiros", code: "GR-D" }, + "GR-B": { + countryCode: "GR", + subdivisionName: "Kentriki Makedonia", + code: "GR-B", + }, + "GR-M": { countryCode: "GR", subdivisionName: "Kriti", code: "GR-M" }, + "GR-L": { countryCode: "GR", subdivisionName: "Notio Aigaio", code: "GR-L" }, + "GR-J": { countryCode: "GR", subdivisionName: "Peloponnisos", code: "GR-J" }, + "GR-H": { countryCode: "GR", subdivisionName: "Sterea Ellada", code: "GR-H" }, + "GR-E": { countryCode: "GR", subdivisionName: "Thessalia", code: "GR-E" }, + "GR-K": { countryCode: "GR", subdivisionName: "Voreio Aigaio", code: "GR-K" }, + "GT-16": { + countryCode: "GT", + subdivisionName: "Alta Verapaz", + code: "GT-16", + }, + "GT-15": { + countryCode: "GT", + subdivisionName: "Baja Verapaz", + code: "GT-15", + }, + "GT-04": { + countryCode: "GT", + subdivisionName: "Chimaltenango", + code: "GT-04", + }, + "GT-20": { countryCode: "GT", subdivisionName: "Chiquimula", code: "GT-20" }, + "GT-02": { countryCode: "GT", subdivisionName: "El Progreso", code: "GT-02" }, + "GT-05": { countryCode: "GT", subdivisionName: "Escuintla", code: "GT-05" }, + "GT-01": { countryCode: "GT", subdivisionName: "Guatemala", code: "GT-01" }, + "GT-13": { + countryCode: "GT", + subdivisionName: "Huehuetenango", + code: "GT-13", + }, + "GT-18": { countryCode: "GT", subdivisionName: "Izabal", code: "GT-18" }, + "GT-21": { countryCode: "GT", subdivisionName: "Jalapa", code: "GT-21" }, + "GT-22": { countryCode: "GT", subdivisionName: "Jutiapa", code: "GT-22" }, + "GT-17": { countryCode: "GT", subdivisionName: "Peten", code: "GT-17" }, + "GT-09": { + countryCode: "GT", + subdivisionName: "Quetzaltenango", + code: "GT-09", + }, + "GT-14": { countryCode: "GT", subdivisionName: "Quiche", code: "GT-14" }, + "GT-11": { countryCode: "GT", subdivisionName: "Retalhuleu", code: "GT-11" }, + "GT-03": { + countryCode: "GT", + subdivisionName: "Sacatepequez", + code: "GT-03", + }, + "GT-12": { countryCode: "GT", subdivisionName: "San Marcos", code: "GT-12" }, + "GT-06": { countryCode: "GT", subdivisionName: "Santa Rosa", code: "GT-06" }, + "GT-07": { countryCode: "GT", subdivisionName: "Solola", code: "GT-07" }, + "GT-10": { + countryCode: "GT", + subdivisionName: "Suchitepequez", + code: "GT-10", + }, + "GT-08": { countryCode: "GT", subdivisionName: "Totonicapan", code: "GT-08" }, + "GT-19": { countryCode: "GT", subdivisionName: "Zacapa", code: "GT-19" }, + "GW-BA": { countryCode: "GW", subdivisionName: "Bafata", code: "GW-BA" }, + "GW-BS": { countryCode: "GW", subdivisionName: "Bissau", code: "GW-BS" }, + "GW-CA": { countryCode: "GW", subdivisionName: "Cacheu", code: "GW-CA" }, + "GW-GA": { countryCode: "GW", subdivisionName: "Gabu", code: "GW-GA" }, + "GW-OI": { countryCode: "GW", subdivisionName: "Oio", code: "GW-OI" }, + "GY-BA": { + countryCode: "GY", + subdivisionName: "Barima-Waini", + code: "GY-BA", + }, + "GY-CU": { + countryCode: "GY", + subdivisionName: "Cuyuni-Mazaruni", + code: "GY-CU", + }, + "GY-DE": { + countryCode: "GY", + subdivisionName: "Demerara-Mahaica", + code: "GY-DE", + }, + "GY-EB": { + countryCode: "GY", + subdivisionName: "East Berbice-Corentyne", + code: "GY-EB", + }, + "GY-ES": { + countryCode: "GY", + subdivisionName: "Essequibo Islands-West Demerara", + code: "GY-ES", + }, + "GY-MA": { + countryCode: "GY", + subdivisionName: "Mahaica-Berbice", + code: "GY-MA", + }, + "GY-PM": { + countryCode: "GY", + subdivisionName: "Pomeroon-Supenaam", + code: "GY-PM", + }, + "GY-PT": { + countryCode: "GY", + subdivisionName: "Potaro-Siparuni", + code: "GY-PT", + }, + "GY-UD": { + countryCode: "GY", + subdivisionName: "Upper Demerara-Berbice", + code: "GY-UD", + }, + "HN-AT": { countryCode: "HN", subdivisionName: "Atlantida", code: "HN-AT" }, + "HN-CH": { countryCode: "HN", subdivisionName: "Choluteca", code: "HN-CH" }, + "HN-CL": { countryCode: "HN", subdivisionName: "Colon", code: "HN-CL" }, + "HN-CM": { countryCode: "HN", subdivisionName: "Comayagua", code: "HN-CM" }, + "HN-CP": { countryCode: "HN", subdivisionName: "Copan", code: "HN-CP" }, + "HN-CR": { countryCode: "HN", subdivisionName: "Cortes", code: "HN-CR" }, + "HN-EP": { countryCode: "HN", subdivisionName: "El Paraiso", code: "HN-EP" }, + "HN-FM": { + countryCode: "HN", + subdivisionName: "Francisco Morazan", + code: "HN-FM", + }, + "HN-GD": { + countryCode: "HN", + subdivisionName: "Gracias a Dios", + code: "HN-GD", + }, + "HN-IN": { countryCode: "HN", subdivisionName: "Intibuca", code: "HN-IN" }, + "HN-IB": { + countryCode: "HN", + subdivisionName: "Islas de la Bahia", + code: "HN-IB", + }, + "HN-LP": { countryCode: "HN", subdivisionName: "La Paz", code: "HN-LP" }, + "HN-LE": { countryCode: "HN", subdivisionName: "Lempira", code: "HN-LE" }, + "HN-OC": { countryCode: "HN", subdivisionName: "Ocotepeque", code: "HN-OC" }, + "HN-OL": { countryCode: "HN", subdivisionName: "Olancho", code: "HN-OL" }, + "HN-SB": { + countryCode: "HN", + subdivisionName: "Santa Barbara", + code: "HN-SB", + }, + "HN-VA": { countryCode: "HN", subdivisionName: "Valle", code: "HN-VA" }, + "HN-YO": { countryCode: "HN", subdivisionName: "Yoro", code: "HN-YO" }, + "HR-07": { + countryCode: "HR", + subdivisionName: "Bjelovarsko-bilogorska zupanija", + code: "HR-07", + }, + "HR-12": { + countryCode: "HR", + subdivisionName: "Brodsko-posavska zupanija", + code: "HR-12", + }, + "HR-19": { + countryCode: "HR", + subdivisionName: "Dubrovacko-neretvanska zupanija", + code: "HR-19", + }, + "HR-21": { countryCode: "HR", subdivisionName: "Grad Zagreb", code: "HR-21" }, + "HR-18": { + countryCode: "HR", + subdivisionName: "Istarska zupanija", + code: "HR-18", + }, + "HR-04": { + countryCode: "HR", + subdivisionName: "Karlovacka zupanija", + code: "HR-04", + }, + "HR-06": { + countryCode: "HR", + subdivisionName: "Koprivnicko-krizevacka zupanija", + code: "HR-06", + }, + "HR-02": { + countryCode: "HR", + subdivisionName: "Krapinsko-zagorska zupanija", + code: "HR-02", + }, + "HR-09": { + countryCode: "HR", + subdivisionName: "Licko-senjska zupanija", + code: "HR-09", + }, + "HR-20": { + countryCode: "HR", + subdivisionName: "Medimurska zupanija", + code: "HR-20", + }, + "HR-14": { + countryCode: "HR", + subdivisionName: "Osjecko-baranjska zupanija", + code: "HR-14", + }, + "HR-11": { + countryCode: "HR", + subdivisionName: "Pozesko-slavonska zupanija", + code: "HR-11", + }, + "HR-08": { + countryCode: "HR", + subdivisionName: "Primorsko-goranska zupanija", + code: "HR-08", + }, + "HR-15": { + countryCode: "HR", + subdivisionName: "Sibensko-kninska zupanija", + code: "HR-15", + }, + "HR-03": { + countryCode: "HR", + subdivisionName: "Sisacko-moslavacka zupanija", + code: "HR-03", + }, + "HR-17": { + countryCode: "HR", + subdivisionName: "Splitsko-dalmatinska zupanija", + code: "HR-17", + }, + "HR-05": { + countryCode: "HR", + subdivisionName: "Varazdinska zupanija", + code: "HR-05", + }, + "HR-10": { + countryCode: "HR", + subdivisionName: "Viroviticko-podravska zupanija", + code: "HR-10", + }, + "HR-16": { + countryCode: "HR", + subdivisionName: "Vukovarsko-srijemska zupanija", + code: "HR-16", + }, + "HR-13": { + countryCode: "HR", + subdivisionName: "Zadarska zupanija", + code: "HR-13", + }, + "HR-01": { + countryCode: "HR", + subdivisionName: "Zagrebacka zupanija", + code: "HR-01", + }, + "HT-AR": { countryCode: "HT", subdivisionName: "Artibonite", code: "HT-AR" }, + "HT-CE": { countryCode: "HT", subdivisionName: "Centre", code: "HT-CE" }, + "HT-GA": { countryCode: "HT", subdivisionName: "Grande'Anse", code: "HT-GA" }, + "HT-NI": { countryCode: "HT", subdivisionName: "Nippes", code: "HT-NI" }, + "HT-ND": { countryCode: "HT", subdivisionName: "Nord", code: "HT-ND" }, + "HT-NO": { countryCode: "HT", subdivisionName: "Nord-Ouest", code: "HT-NO" }, + "HT-OU": { countryCode: "HT", subdivisionName: "Ouest", code: "HT-OU" }, + "HT-SD": { countryCode: "HT", subdivisionName: "Sud", code: "HT-SD" }, + "HT-SE": { countryCode: "HT", subdivisionName: "Sud-Est", code: "HT-SE" }, + "HU-BK": { countryCode: "HU", subdivisionName: "Bacs-Kiskun", code: "HU-BK" }, + "HU-BA": { countryCode: "HU", subdivisionName: "Baranya", code: "HU-BA" }, + "HU-BE": { countryCode: "HU", subdivisionName: "Bekes", code: "HU-BE" }, + "HU-BZ": { + countryCode: "HU", + subdivisionName: "Borsod-Abauj-Zemplen", + code: "HU-BZ", + }, + "HU-BU": { countryCode: "HU", subdivisionName: "Budapest", code: "HU-BU" }, + "HU-CS": { + countryCode: "HU", + subdivisionName: "Csongrad-Csanad", + code: "HU-CS", + }, + "HU-FE": { countryCode: "HU", subdivisionName: "Fejer", code: "HU-FE" }, + "HU-GS": { + countryCode: "HU", + subdivisionName: "Gyor-Moson-Sopron", + code: "HU-GS", + }, + "HU-HB": { countryCode: "HU", subdivisionName: "Hajdu-Bihar", code: "HU-HB" }, + "HU-HE": { countryCode: "HU", subdivisionName: "Heves", code: "HU-HE" }, + "HU-JN": { + countryCode: "HU", + subdivisionName: "Jasz-Nagykun-Szolnok", + code: "HU-JN", + }, + "HU-KE": { + countryCode: "HU", + subdivisionName: "Komarom-Esztergom", + code: "HU-KE", + }, + "HU-NO": { countryCode: "HU", subdivisionName: "Nograd", code: "HU-NO" }, + "HU-PE": { countryCode: "HU", subdivisionName: "Pest", code: "HU-PE" }, + "HU-SO": { countryCode: "HU", subdivisionName: "Somogy", code: "HU-SO" }, + "HU-SZ": { + countryCode: "HU", + subdivisionName: "Szabolcs-Szatmar-Bereg", + code: "HU-SZ", + }, + "HU-TO": { countryCode: "HU", subdivisionName: "Tolna", code: "HU-TO" }, + "HU-VA": { countryCode: "HU", subdivisionName: "Vas", code: "HU-VA" }, + "HU-VE": { countryCode: "HU", subdivisionName: "Veszprem", code: "HU-VE" }, + "HU-ZA": { countryCode: "HU", subdivisionName: "Zala", code: "HU-ZA" }, + "ID-AC": { countryCode: "ID", subdivisionName: "Aceh", code: "ID-AC" }, + "ID-BA": { countryCode: "ID", subdivisionName: "Bali", code: "ID-BA" }, + "ID-BT": { countryCode: "ID", subdivisionName: "Banten", code: "ID-BT" }, + "ID-BE": { countryCode: "ID", subdivisionName: "Bengkulu", code: "ID-BE" }, + "ID-GO": { countryCode: "ID", subdivisionName: "Gorontalo", code: "ID-GO" }, + "ID-JK": { + countryCode: "ID", + subdivisionName: "Jakarta Raya", + code: "ID-JK", + }, + "ID-JA": { countryCode: "ID", subdivisionName: "Jambi", code: "ID-JA" }, + "ID-JB": { countryCode: "ID", subdivisionName: "Jawa Barat", code: "ID-JB" }, + "ID-JT": { countryCode: "ID", subdivisionName: "Jawa Tengah", code: "ID-JT" }, + "ID-JI": { countryCode: "ID", subdivisionName: "Jawa Timur", code: "ID-JI" }, + "ID-KB": { + countryCode: "ID", + subdivisionName: "Kalimantan Barat", + code: "ID-KB", + }, + "ID-KS": { + countryCode: "ID", + subdivisionName: "Kalimantan Selatan", + code: "ID-KS", + }, + "ID-KT": { + countryCode: "ID", + subdivisionName: "Kalimantan Tengah", + code: "ID-KT", + }, + "ID-KI": { + countryCode: "ID", + subdivisionName: "Kalimantan Timur", + code: "ID-KI", + }, + "ID-KU": { + countryCode: "ID", + subdivisionName: "Kalimantan Utara", + code: "ID-KU", + }, + "ID-BB": { + countryCode: "ID", + subdivisionName: "Kepulauan Bangka Belitung", + code: "ID-BB", + }, + "ID-KR": { + countryCode: "ID", + subdivisionName: "Kepulauan Riau", + code: "ID-KR", + }, + "ID-LA": { countryCode: "ID", subdivisionName: "Lampung", code: "ID-LA" }, + "ID-ML": { countryCode: "ID", subdivisionName: "Maluku", code: "ID-ML" }, + "ID-MU": { + countryCode: "ID", + subdivisionName: "Maluku Utara", + code: "ID-MU", + }, + "ID-NB": { + countryCode: "ID", + subdivisionName: "Nusa Tenggara Barat", + code: "ID-NB", + }, + "ID-NT": { + countryCode: "ID", + subdivisionName: "Nusa Tenggara Timur", + code: "ID-NT", + }, + "ID-PP": { countryCode: "ID", subdivisionName: "Papua", code: "ID-PP" }, + "ID-PB": { countryCode: "ID", subdivisionName: "Papua Barat", code: "ID-PB" }, + "ID-PE": { + countryCode: "ID", + subdivisionName: "Papua Pengunungan", + code: "ID-PE", + }, + "ID-PS": { + countryCode: "ID", + subdivisionName: "Papua Selatan", + code: "ID-PS", + }, + "ID-PT": { + countryCode: "ID", + subdivisionName: "Papua Tengah", + code: "ID-PT", + }, + "ID-RI": { countryCode: "ID", subdivisionName: "Riau", code: "ID-RI" }, + "ID-SR": { + countryCode: "ID", + subdivisionName: "Sulawesi Barat", + code: "ID-SR", + }, + "ID-SN": { + countryCode: "ID", + subdivisionName: "Sulawesi Selatan", + code: "ID-SN", + }, + "ID-ST": { + countryCode: "ID", + subdivisionName: "Sulawesi Tengah", + code: "ID-ST", + }, + "ID-SG": { + countryCode: "ID", + subdivisionName: "Sulawesi Tenggara", + code: "ID-SG", + }, + "ID-SA": { + countryCode: "ID", + subdivisionName: "Sulawesi Utara", + code: "ID-SA", + }, + "ID-SB": { + countryCode: "ID", + subdivisionName: "Sumatera Barat", + code: "ID-SB", + }, + "ID-SS": { + countryCode: "ID", + subdivisionName: "Sumatera Selatan", + code: "ID-SS", + }, + "ID-SU": { + countryCode: "ID", + subdivisionName: "Sumatera Utara", + code: "ID-SU", + }, + "ID-YO": { countryCode: "ID", subdivisionName: "Yogyakarta", code: "ID-YO" }, + "IE-CW": { countryCode: "IE", subdivisionName: "Carlow", code: "IE-CW" }, + "IE-CN": { countryCode: "IE", subdivisionName: "Cavan", code: "IE-CN" }, + "IE-CE": { countryCode: "IE", subdivisionName: "Clare", code: "IE-CE" }, + "IE-CO": { countryCode: "IE", subdivisionName: "Cork", code: "IE-CO" }, + "IE-DL": { countryCode: "IE", subdivisionName: "Donegal", code: "IE-DL" }, + "IE-D": { countryCode: "IE", subdivisionName: "Dublin", code: "IE-D" }, + "IE-G": { countryCode: "IE", subdivisionName: "Galway", code: "IE-G" }, + "IE-KY": { countryCode: "IE", subdivisionName: "Kerry", code: "IE-KY" }, + "IE-KE": { countryCode: "IE", subdivisionName: "Kildare", code: "IE-KE" }, + "IE-KK": { countryCode: "IE", subdivisionName: "Kilkenny", code: "IE-KK" }, + "IE-LS": { countryCode: "IE", subdivisionName: "Laois", code: "IE-LS" }, + "IE-LM": { countryCode: "IE", subdivisionName: "Leitrim", code: "IE-LM" }, + "IE-LK": { countryCode: "IE", subdivisionName: "Limerick", code: "IE-LK" }, + "IE-LD": { countryCode: "IE", subdivisionName: "Longford", code: "IE-LD" }, + "IE-LH": { countryCode: "IE", subdivisionName: "Louth", code: "IE-LH" }, + "IE-MO": { countryCode: "IE", subdivisionName: "Mayo", code: "IE-MO" }, + "IE-MH": { countryCode: "IE", subdivisionName: "Meath", code: "IE-MH" }, + "IE-MN": { countryCode: "IE", subdivisionName: "Monaghan", code: "IE-MN" }, + "IE-OY": { countryCode: "IE", subdivisionName: "Offaly", code: "IE-OY" }, + "IE-RN": { countryCode: "IE", subdivisionName: "Roscommon", code: "IE-RN" }, + "IE-SO": { countryCode: "IE", subdivisionName: "Sligo", code: "IE-SO" }, + "IE-TA": { countryCode: "IE", subdivisionName: "Tipperary", code: "IE-TA" }, + "IE-WD": { countryCode: "IE", subdivisionName: "Waterford", code: "IE-WD" }, + "IE-WH": { countryCode: "IE", subdivisionName: "Westmeath", code: "IE-WH" }, + "IE-WX": { countryCode: "IE", subdivisionName: "Wexford", code: "IE-WX" }, + "IE-WW": { countryCode: "IE", subdivisionName: "Wicklow", code: "IE-WW" }, + "IL-D": { countryCode: "IL", subdivisionName: "HaDarom", code: "IL-D" }, + "IL-M": { countryCode: "IL", subdivisionName: "HaMerkaz", code: "IL-M" }, + "IL-Z": { countryCode: "IL", subdivisionName: "HaTsafon", code: "IL-Z" }, + "IL-HA": { countryCode: "IL", subdivisionName: "Hefa", code: "IL-HA" }, + "IL-TA": { countryCode: "IL", subdivisionName: "Tel Aviv", code: "IL-TA" }, + "IL-JM": { + countryCode: "IL", + subdivisionName: "Yerushalayim", + code: "IL-JM", + }, + "IN-AN": { + countryCode: "IN", + subdivisionName: "Andaman and Nicobar Islands", + code: "IN-AN", + }, + "IN-AP": { + countryCode: "IN", + subdivisionName: "Andhra Pradesh", + code: "IN-AP", + }, + "IN-AR": { + countryCode: "IN", + subdivisionName: "Arunachal Pradesh", + code: "IN-AR", + }, + "IN-AS": { countryCode: "IN", subdivisionName: "Assam", code: "IN-AS" }, + "IN-BR": { countryCode: "IN", subdivisionName: "Bihar", code: "IN-BR" }, + "IN-CH": { countryCode: "IN", subdivisionName: "Chandigarh", code: "IN-CH" }, + "IN-CT": { + countryCode: "IN", + subdivisionName: "Chhattisgarh", + code: "IN-CT", + }, + "IN-DN": { + countryCode: "IN", + subdivisionName: "Dadra and Nagar Haveli", + code: "IN-DN", + }, + "IN-DH": { + countryCode: "IN", + subdivisionName: "Dadra and Nagar Haveli and Daman and Diu", + code: "IN-DH", + }, + "IN-DL": { countryCode: "IN", subdivisionName: "Delhi", code: "IN-DL" }, + "IN-GA": { countryCode: "IN", subdivisionName: "Goa", code: "IN-GA" }, + "IN-GJ": { countryCode: "IN", subdivisionName: "Gujarat", code: "IN-GJ" }, + "IN-HR": { countryCode: "IN", subdivisionName: "Haryana", code: "IN-HR" }, + "IN-HP": { + countryCode: "IN", + subdivisionName: "Himachal Pradesh", + code: "IN-HP", + }, + "IN-JK": { + countryCode: "IN", + subdivisionName: "Jammu and Kashmir", + code: "IN-JK", + }, + "IN-JH": { countryCode: "IN", subdivisionName: "Jharkhand", code: "IN-JH" }, + "IN-KA": { countryCode: "IN", subdivisionName: "Karnataka", code: "IN-KA" }, + "IN-KL": { countryCode: "IN", subdivisionName: "Kerala", code: "IN-KL" }, + "IN-LD": { countryCode: "IN", subdivisionName: "Lakshadweep", code: "IN-LD" }, + "IN-MP": { + countryCode: "IN", + subdivisionName: "Madhya Pradesh", + code: "IN-MP", + }, + "IN-MH": { countryCode: "IN", subdivisionName: "Maharashtra", code: "IN-MH" }, + "IN-MN": { countryCode: "IN", subdivisionName: "Manipur", code: "IN-MN" }, + "IN-ML": { countryCode: "IN", subdivisionName: "Meghalaya", code: "IN-ML" }, + "IN-MZ": { countryCode: "IN", subdivisionName: "Mizoram", code: "IN-MZ" }, + "IN-NL": { countryCode: "IN", subdivisionName: "Nagaland", code: "IN-NL" }, + "IN-OR": { countryCode: "IN", subdivisionName: "Odisha", code: "IN-OR" }, + "IN-PY": { countryCode: "IN", subdivisionName: "Puducherry", code: "IN-PY" }, + "IN-PB": { countryCode: "IN", subdivisionName: "Punjab", code: "IN-PB" }, + "IN-RJ": { countryCode: "IN", subdivisionName: "Rajasthan", code: "IN-RJ" }, + "IN-SK": { countryCode: "IN", subdivisionName: "Sikkim", code: "IN-SK" }, + "IN-TN": { countryCode: "IN", subdivisionName: "Tamil Nadu", code: "IN-TN" }, + "IN-TG": { countryCode: "IN", subdivisionName: "Telangana", code: "IN-TG" }, + "IN-TR": { countryCode: "IN", subdivisionName: "Tripura", code: "IN-TR" }, + "IN-UP": { + countryCode: "IN", + subdivisionName: "Uttar Pradesh", + code: "IN-UP", + }, + "IN-UT": { countryCode: "IN", subdivisionName: "Uttarakhand", code: "IN-UT" }, + "IN-WB": { countryCode: "IN", subdivisionName: "West Bengal", code: "IN-WB" }, + "IQ-AN": { countryCode: "IQ", subdivisionName: "Al Anbar", code: "IQ-AN" }, + "IQ-BA": { countryCode: "IQ", subdivisionName: "Al Basrah", code: "IQ-BA" }, + "IQ-MU": { countryCode: "IQ", subdivisionName: "Al Muthanna", code: "IQ-MU" }, + "IQ-QA": { + countryCode: "IQ", + subdivisionName: "Al Qadisiyah", + code: "IQ-QA", + }, + "IQ-NA": { countryCode: "IQ", subdivisionName: "An Najaf", code: "IQ-NA" }, + "IQ-AR": { countryCode: "IQ", subdivisionName: "Arbil", code: "IQ-AR" }, + "IQ-SU": { + countryCode: "IQ", + subdivisionName: "As Sulaymaniyah", + code: "IQ-SU", + }, + "IQ-BB": { countryCode: "IQ", subdivisionName: "Babil", code: "IQ-BB" }, + "IQ-BG": { countryCode: "IQ", subdivisionName: "Baghdad", code: "IQ-BG" }, + "IQ-DA": { countryCode: "IQ", subdivisionName: "Dahuk", code: "IQ-DA" }, + "IQ-DQ": { countryCode: "IQ", subdivisionName: "Dhi Qar", code: "IQ-DQ" }, + "IQ-DI": { countryCode: "IQ", subdivisionName: "Diyala", code: "IQ-DI" }, + "IQ-KA": { countryCode: "IQ", subdivisionName: "Karbala'", code: "IQ-KA" }, + "IQ-KI": { countryCode: "IQ", subdivisionName: "Kirkuk", code: "IQ-KI" }, + "IQ-MA": { countryCode: "IQ", subdivisionName: "Maysan", code: "IQ-MA" }, + "IQ-NI": { countryCode: "IQ", subdivisionName: "Ninawa", code: "IQ-NI" }, + "IQ-SD": { + countryCode: "IQ", + subdivisionName: "Salah ad Din", + code: "IQ-SD", + }, + "IQ-WA": { countryCode: "IQ", subdivisionName: "Wasit", code: "IQ-WA" }, + "IR-30": { countryCode: "IR", subdivisionName: "Alborz", code: "IR-30" }, + "IR-24": { countryCode: "IR", subdivisionName: "Ardabil", code: "IR-24" }, + "IR-04": { + countryCode: "IR", + subdivisionName: "Azarbayjan-e Gharbi", + code: "IR-04", + }, + "IR-03": { + countryCode: "IR", + subdivisionName: "Azarbayjan-e Sharqi", + code: "IR-03", + }, + "IR-18": { countryCode: "IR", subdivisionName: "Bushehr", code: "IR-18" }, + "IR-14": { + countryCode: "IR", + subdivisionName: "Chahar Mahal va Bakhtiari", + code: "IR-14", + }, + "IR-10": { countryCode: "IR", subdivisionName: "Esfahan", code: "IR-10" }, + "IR-07": { countryCode: "IR", subdivisionName: "Fars", code: "IR-07" }, + "IR-01": { countryCode: "IR", subdivisionName: "Gilan", code: "IR-01" }, + "IR-27": { countryCode: "IR", subdivisionName: "Golestan", code: "IR-27" }, + "IR-13": { countryCode: "IR", subdivisionName: "Hamadan", code: "IR-13" }, + "IR-22": { countryCode: "IR", subdivisionName: "Hormozgan", code: "IR-22" }, + "IR-16": { countryCode: "IR", subdivisionName: "Ilam", code: "IR-16" }, + "IR-08": { countryCode: "IR", subdivisionName: "Kerman", code: "IR-08" }, + "IR-05": { countryCode: "IR", subdivisionName: "Kermanshah", code: "IR-05" }, + "IR-29": { + countryCode: "IR", + subdivisionName: "Khorasan-e Jonubi", + code: "IR-29", + }, + "IR-09": { + countryCode: "IR", + subdivisionName: "Khorasan-e Razavi", + code: "IR-09", + }, + "IR-28": { + countryCode: "IR", + subdivisionName: "Khorasan-e Shomali", + code: "IR-28", + }, + "IR-06": { countryCode: "IR", subdivisionName: "Khuzestan", code: "IR-06" }, + "IR-17": { + countryCode: "IR", + subdivisionName: "Kohgiluyeh va Bowyer Ahmad", + code: "IR-17", + }, + "IR-12": { countryCode: "IR", subdivisionName: "Kordestan", code: "IR-12" }, + "IR-15": { countryCode: "IR", subdivisionName: "Lorestan", code: "IR-15" }, + "IR-00": { countryCode: "IR", subdivisionName: "Markazi", code: "IR-00" }, + "IR-02": { countryCode: "IR", subdivisionName: "Mazandaran", code: "IR-02" }, + "IR-26": { countryCode: "IR", subdivisionName: "Qazvin", code: "IR-26" }, + "IR-25": { countryCode: "IR", subdivisionName: "Qom", code: "IR-25" }, + "IR-20": { countryCode: "IR", subdivisionName: "Semnan", code: "IR-20" }, + "IR-11": { + countryCode: "IR", + subdivisionName: "Sistan va Baluchestan", + code: "IR-11", + }, + "IR-23": { countryCode: "IR", subdivisionName: "Tehran", code: "IR-23" }, + "IR-21": { countryCode: "IR", subdivisionName: "Yazd", code: "IR-21" }, + "IR-19": { countryCode: "IR", subdivisionName: "Zanjan", code: "IR-19" }, + "IS-7": { countryCode: "IS", subdivisionName: "Austurland", code: "IS-7" }, + "IS-1": { + countryCode: "IS", + subdivisionName: "Hofudborgarsvaedi", + code: "IS-1", + }, + "IS-6": { + countryCode: "IS", + subdivisionName: "Nordurland eystra", + code: "IS-6", + }, + "IS-5": { + countryCode: "IS", + subdivisionName: "Nordurland vestra", + code: "IS-5", + }, + "IS-8": { countryCode: "IS", subdivisionName: "Sudurland", code: "IS-8" }, + "IS-2": { countryCode: "IS", subdivisionName: "Sudurnes", code: "IS-2" }, + "IS-4": { countryCode: "IS", subdivisionName: "Vestfirdir", code: "IS-4" }, + "IS-3": { countryCode: "IS", subdivisionName: "Vesturland", code: "IS-3" }, + "IT-65": { countryCode: "IT", subdivisionName: "Abruzzo", code: "IT-65" }, + "IT-77": { countryCode: "IT", subdivisionName: "Basilicata", code: "IT-77" }, + "IT-78": { countryCode: "IT", subdivisionName: "Calabria", code: "IT-78" }, + "IT-72": { countryCode: "IT", subdivisionName: "Campania", code: "IT-72" }, + "IT-45": { + countryCode: "IT", + subdivisionName: "Emilia-Romagna", + code: "IT-45", + }, + "IT-36": { + countryCode: "IT", + subdivisionName: "Friuli-Venezia Giulia", + code: "IT-36", + }, + "IT-62": { countryCode: "IT", subdivisionName: "Lazio", code: "IT-62" }, + "IT-42": { countryCode: "IT", subdivisionName: "Liguria", code: "IT-42" }, + "IT-25": { countryCode: "IT", subdivisionName: "Lombardia", code: "IT-25" }, + "IT-57": { countryCode: "IT", subdivisionName: "Marche", code: "IT-57" }, + "IT-67": { countryCode: "IT", subdivisionName: "Molise", code: "IT-67" }, + "IT-21": { countryCode: "IT", subdivisionName: "Piemonte", code: "IT-21" }, + "IT-75": { countryCode: "IT", subdivisionName: "Puglia", code: "IT-75" }, + "IT-88": { countryCode: "IT", subdivisionName: "Sardegna", code: "IT-88" }, + "IT-82": { countryCode: "IT", subdivisionName: "Sicilia", code: "IT-82" }, + "IT-52": { countryCode: "IT", subdivisionName: "Toscana", code: "IT-52" }, + "IT-32": { + countryCode: "IT", + subdivisionName: "Trentino-Alto Adige", + code: "IT-32", + }, + "IT-55": { countryCode: "IT", subdivisionName: "Umbria", code: "IT-55" }, + "IT-23": { + countryCode: "IT", + subdivisionName: "Valle d'Aosta", + code: "IT-23", + }, + "IT-34": { countryCode: "IT", subdivisionName: "Veneto", code: "IT-34" }, + "JM-13": { countryCode: "JM", subdivisionName: "Clarendon", code: "JM-13" }, + "JM-09": { countryCode: "JM", subdivisionName: "Hanover", code: "JM-09" }, + "JM-01": { countryCode: "JM", subdivisionName: "Kingston", code: "JM-01" }, + "JM-12": { countryCode: "JM", subdivisionName: "Manchester", code: "JM-12" }, + "JM-04": { countryCode: "JM", subdivisionName: "Portland", code: "JM-04" }, + "JM-02": { + countryCode: "JM", + subdivisionName: "Saint Andrew", + code: "JM-02", + }, + "JM-06": { countryCode: "JM", subdivisionName: "Saint Ann", code: "JM-06" }, + "JM-14": { + countryCode: "JM", + subdivisionName: "Saint Catherine", + code: "JM-14", + }, + "JM-11": { + countryCode: "JM", + subdivisionName: "Saint Elizabeth", + code: "JM-11", + }, + "JM-08": { countryCode: "JM", subdivisionName: "Saint James", code: "JM-08" }, + "JM-05": { countryCode: "JM", subdivisionName: "Saint Mary", code: "JM-05" }, + "JM-03": { + countryCode: "JM", + subdivisionName: "Saint Thomas", + code: "JM-03", + }, + "JM-07": { countryCode: "JM", subdivisionName: "Trelawny", code: "JM-07" }, + "JM-10": { + countryCode: "JM", + subdivisionName: "Westmoreland", + code: "JM-10", + }, + "JO-AJ": { countryCode: "JO", subdivisionName: "'Ajlun", code: "JO-AJ" }, + "JO-AQ": { countryCode: "JO", subdivisionName: "Al 'Aqabah", code: "JO-AQ" }, + "JO-AM": { countryCode: "JO", subdivisionName: "Al 'Asimah", code: "JO-AM" }, + "JO-BA": { countryCode: "JO", subdivisionName: "Al Balqa'", code: "JO-BA" }, + "JO-KA": { countryCode: "JO", subdivisionName: "Al Karak", code: "JO-KA" }, + "JO-MA": { countryCode: "JO", subdivisionName: "Al Mafraq", code: "JO-MA" }, + "JO-AT": { countryCode: "JO", subdivisionName: "At Tafilah", code: "JO-AT" }, + "JO-AZ": { countryCode: "JO", subdivisionName: "Az Zarqa'", code: "JO-AZ" }, + "JO-IR": { countryCode: "JO", subdivisionName: "Irbid", code: "JO-IR" }, + "JO-JA": { countryCode: "JO", subdivisionName: "Jarash", code: "JO-JA" }, + "JO-MN": { countryCode: "JO", subdivisionName: "Ma'an", code: "JO-MN" }, + "JO-MD": { countryCode: "JO", subdivisionName: "Madaba", code: "JO-MD" }, + "JP-23": { countryCode: "JP", subdivisionName: "Aichi", code: "JP-23" }, + "JP-05": { countryCode: "JP", subdivisionName: "Akita", code: "JP-05" }, + "JP-02": { countryCode: "JP", subdivisionName: "Aomori", code: "JP-02" }, + "JP-12": { countryCode: "JP", subdivisionName: "Chiba", code: "JP-12" }, + "JP-38": { countryCode: "JP", subdivisionName: "Ehime", code: "JP-38" }, + "JP-18": { countryCode: "JP", subdivisionName: "Fukui", code: "JP-18" }, + "JP-40": { countryCode: "JP", subdivisionName: "Fukuoka", code: "JP-40" }, + "JP-07": { countryCode: "JP", subdivisionName: "Fukushima", code: "JP-07" }, + "JP-21": { countryCode: "JP", subdivisionName: "Gifu", code: "JP-21" }, + "JP-10": { countryCode: "JP", subdivisionName: "Gunma", code: "JP-10" }, + "JP-34": { countryCode: "JP", subdivisionName: "Hiroshima", code: "JP-34" }, + "JP-01": { countryCode: "JP", subdivisionName: "Hokkaido", code: "JP-01" }, + "JP-28": { countryCode: "JP", subdivisionName: "Hyogo", code: "JP-28" }, + "JP-08": { countryCode: "JP", subdivisionName: "Ibaraki", code: "JP-08" }, + "JP-17": { countryCode: "JP", subdivisionName: "Ishikawa", code: "JP-17" }, + "JP-03": { countryCode: "JP", subdivisionName: "Iwate", code: "JP-03" }, + "JP-37": { countryCode: "JP", subdivisionName: "Kagawa", code: "JP-37" }, + "JP-46": { countryCode: "JP", subdivisionName: "Kagoshima", code: "JP-46" }, + "JP-14": { countryCode: "JP", subdivisionName: "Kanagawa", code: "JP-14" }, + "JP-39": { countryCode: "JP", subdivisionName: "Kochi", code: "JP-39" }, + "JP-43": { countryCode: "JP", subdivisionName: "Kumamoto", code: "JP-43" }, + "JP-26": { countryCode: "JP", subdivisionName: "Kyoto", code: "JP-26" }, + "JP-24": { countryCode: "JP", subdivisionName: "Mie", code: "JP-24" }, + "JP-04": { countryCode: "JP", subdivisionName: "Miyagi", code: "JP-04" }, + "JP-45": { countryCode: "JP", subdivisionName: "Miyazaki", code: "JP-45" }, + "JP-20": { countryCode: "JP", subdivisionName: "Nagano", code: "JP-20" }, + "JP-42": { countryCode: "JP", subdivisionName: "Nagasaki", code: "JP-42" }, + "JP-29": { countryCode: "JP", subdivisionName: "Nara", code: "JP-29" }, + "JP-15": { countryCode: "JP", subdivisionName: "Niigata", code: "JP-15" }, + "JP-44": { countryCode: "JP", subdivisionName: "Oita", code: "JP-44" }, + "JP-33": { countryCode: "JP", subdivisionName: "Okayama", code: "JP-33" }, + "JP-47": { countryCode: "JP", subdivisionName: "Okinawa", code: "JP-47" }, + "JP-27": { countryCode: "JP", subdivisionName: "Osaka", code: "JP-27" }, + "JP-41": { countryCode: "JP", subdivisionName: "Saga", code: "JP-41" }, + "JP-11": { countryCode: "JP", subdivisionName: "Saitama", code: "JP-11" }, + "JP-25": { countryCode: "JP", subdivisionName: "Shiga", code: "JP-25" }, + "JP-32": { countryCode: "JP", subdivisionName: "Shimane", code: "JP-32" }, + "JP-22": { countryCode: "JP", subdivisionName: "Shizuoka", code: "JP-22" }, + "JP-09": { countryCode: "JP", subdivisionName: "Tochigi", code: "JP-09" }, + "JP-36": { countryCode: "JP", subdivisionName: "Tokushima", code: "JP-36" }, + "JP-13": { countryCode: "JP", subdivisionName: "Tokyo", code: "JP-13" }, + "JP-31": { countryCode: "JP", subdivisionName: "Tottori", code: "JP-31" }, + "JP-16": { countryCode: "JP", subdivisionName: "Toyama", code: "JP-16" }, + "JP-30": { countryCode: "JP", subdivisionName: "Wakayama", code: "JP-30" }, + "JP-06": { countryCode: "JP", subdivisionName: "Yamagata", code: "JP-06" }, + "JP-35": { countryCode: "JP", subdivisionName: "Yamaguchi", code: "JP-35" }, + "JP-19": { countryCode: "JP", subdivisionName: "Yamanashi", code: "JP-19" }, + "KE-01": { countryCode: "KE", subdivisionName: "Baringo", code: "KE-01" }, + "KE-02": { countryCode: "KE", subdivisionName: "Bomet", code: "KE-02" }, + "KE-03": { countryCode: "KE", subdivisionName: "Bungoma", code: "KE-03" }, + "KE-04": { countryCode: "KE", subdivisionName: "Busia", code: "KE-04" }, + "KE-05": { + countryCode: "KE", + subdivisionName: "Elgeyo/Marakwet", + code: "KE-05", + }, + "KE-06": { countryCode: "KE", subdivisionName: "Embu", code: "KE-06" }, + "KE-07": { countryCode: "KE", subdivisionName: "Garissa", code: "KE-07" }, + "KE-08": { countryCode: "KE", subdivisionName: "Homa Bay", code: "KE-08" }, + "KE-09": { countryCode: "KE", subdivisionName: "Isiolo", code: "KE-09" }, + "KE-10": { countryCode: "KE", subdivisionName: "Kajiado", code: "KE-10" }, + "KE-11": { countryCode: "KE", subdivisionName: "Kakamega", code: "KE-11" }, + "KE-12": { countryCode: "KE", subdivisionName: "Kericho", code: "KE-12" }, + "KE-13": { countryCode: "KE", subdivisionName: "Kiambu", code: "KE-13" }, + "KE-14": { countryCode: "KE", subdivisionName: "Kilifi", code: "KE-14" }, + "KE-15": { countryCode: "KE", subdivisionName: "Kirinyaga", code: "KE-15" }, + "KE-16": { countryCode: "KE", subdivisionName: "Kisii", code: "KE-16" }, + "KE-17": { countryCode: "KE", subdivisionName: "Kisumu", code: "KE-17" }, + "KE-18": { countryCode: "KE", subdivisionName: "Kitui", code: "KE-18" }, + "KE-19": { countryCode: "KE", subdivisionName: "Kwale", code: "KE-19" }, + "KE-20": { countryCode: "KE", subdivisionName: "Laikipia", code: "KE-20" }, + "KE-21": { countryCode: "KE", subdivisionName: "Lamu", code: "KE-21" }, + "KE-22": { countryCode: "KE", subdivisionName: "Machakos", code: "KE-22" }, + "KE-23": { countryCode: "KE", subdivisionName: "Makueni", code: "KE-23" }, + "KE-24": { countryCode: "KE", subdivisionName: "Mandera", code: "KE-24" }, + "KE-25": { countryCode: "KE", subdivisionName: "Marsabit", code: "KE-25" }, + "KE-26": { countryCode: "KE", subdivisionName: "Meru", code: "KE-26" }, + "KE-27": { countryCode: "KE", subdivisionName: "Migori", code: "KE-27" }, + "KE-28": { countryCode: "KE", subdivisionName: "Mombasa", code: "KE-28" }, + "KE-29": { countryCode: "KE", subdivisionName: "Murang'a", code: "KE-29" }, + "KE-30": { + countryCode: "KE", + subdivisionName: "Nairobi City", + code: "KE-30", + }, + "KE-31": { countryCode: "KE", subdivisionName: "Nakuru", code: "KE-31" }, + "KE-32": { countryCode: "KE", subdivisionName: "Nandi", code: "KE-32" }, + "KE-33": { countryCode: "KE", subdivisionName: "Narok", code: "KE-33" }, + "KE-34": { countryCode: "KE", subdivisionName: "Nyamira", code: "KE-34" }, + "KE-35": { countryCode: "KE", subdivisionName: "Nyandarua", code: "KE-35" }, + "KE-36": { countryCode: "KE", subdivisionName: "Nyeri", code: "KE-36" }, + "KE-37": { countryCode: "KE", subdivisionName: "Samburu", code: "KE-37" }, + "KE-38": { countryCode: "KE", subdivisionName: "Siaya", code: "KE-38" }, + "KE-39": { + countryCode: "KE", + subdivisionName: "Taita/Taveta", + code: "KE-39", + }, + "KE-40": { countryCode: "KE", subdivisionName: "Tana River", code: "KE-40" }, + "KE-41": { + countryCode: "KE", + subdivisionName: "Tharaka-Nithi", + code: "KE-41", + }, + "KE-42": { countryCode: "KE", subdivisionName: "Trans Nzoia", code: "KE-42" }, + "KE-43": { countryCode: "KE", subdivisionName: "Turkana", code: "KE-43" }, + "KE-44": { countryCode: "KE", subdivisionName: "Uasin Gishu", code: "KE-44" }, + "KE-45": { countryCode: "KE", subdivisionName: "Vihiga", code: "KE-45" }, + "KE-46": { countryCode: "KE", subdivisionName: "Wajir", code: "KE-46" }, + "KE-47": { countryCode: "KE", subdivisionName: "West Pokot", code: "KE-47" }, + "KG-B": { countryCode: "KG", subdivisionName: "Batken", code: "KG-B" }, + "KG-GB": { + countryCode: "KG", + subdivisionName: "Bishkek Shaary", + code: "KG-GB", + }, + "KG-C": { countryCode: "KG", subdivisionName: "Chuy", code: "KG-C" }, + "KG-J": { countryCode: "KG", subdivisionName: "Jalal-Abad", code: "KG-J" }, + "KG-N": { countryCode: "KG", subdivisionName: "Naryn", code: "KG-N" }, + "KG-GO": { countryCode: "KG", subdivisionName: "Osh Shaary", code: "KG-GO" }, + "KG-T": { countryCode: "KG", subdivisionName: "Talas", code: "KG-T" }, + "KG-Y": { countryCode: "KG", subdivisionName: "Ysyk-Kol", code: "KG-Y" }, + "KH-2": { countryCode: "KH", subdivisionName: "Baat Dambang", code: "KH-2" }, + "KH-1": { + countryCode: "KH", + subdivisionName: "Banteay Mean Choay", + code: "KH-1", + }, + "KH-23": { countryCode: "KH", subdivisionName: "Kaeb", code: "KH-23" }, + "KH-3": { countryCode: "KH", subdivisionName: "Kampong Chaam", code: "KH-3" }, + "KH-4": { + countryCode: "KH", + subdivisionName: "Kampong Chhnang", + code: "KH-4", + }, + "KH-5": { countryCode: "KH", subdivisionName: "Kampong Spueu", code: "KH-5" }, + "KH-6": { countryCode: "KH", subdivisionName: "Kampong Thum", code: "KH-6" }, + "KH-7": { countryCode: "KH", subdivisionName: "Kampot", code: "KH-7" }, + "KH-8": { countryCode: "KH", subdivisionName: "Kandaal", code: "KH-8" }, + "KH-10": { countryCode: "KH", subdivisionName: "Kracheh", code: "KH-10" }, + "KH-11": { countryCode: "KH", subdivisionName: "Mondol Kiri", code: "KH-11" }, + "KH-24": { countryCode: "KH", subdivisionName: "Pailin", code: "KH-24" }, + "KH-12": { countryCode: "KH", subdivisionName: "Phnom Penh", code: "KH-12" }, + "KH-15": { countryCode: "KH", subdivisionName: "Pousaat", code: "KH-15" }, + "KH-18": { + countryCode: "KH", + subdivisionName: "Preah Sihanouk", + code: "KH-18", + }, + "KH-13": { + countryCode: "KH", + subdivisionName: "Preah Vihear", + code: "KH-13", + }, + "KH-14": { countryCode: "KH", subdivisionName: "Prey Veaeng", code: "KH-14" }, + "KH-17": { countryCode: "KH", subdivisionName: "Siem Reab", code: "KH-17" }, + "KH-19": { + countryCode: "KH", + subdivisionName: "Stueng Traeng", + code: "KH-19", + }, + "KH-20": { countryCode: "KH", subdivisionName: "Svaay Rieng", code: "KH-20" }, + "KH-21": { countryCode: "KH", subdivisionName: "Taakaev", code: "KH-21" }, + "KI-G": { + countryCode: "KI", + subdivisionName: "Gilbert Islands", + code: "KI-G", + }, + "KI-L": { countryCode: "KI", subdivisionName: "Line Islands", code: "KI-L" }, + "KM-G": { countryCode: "KM", subdivisionName: "Grande Comore", code: "KM-G" }, + "KM-M": { countryCode: "KM", subdivisionName: "Moheli", code: "KM-M" }, + "KN-01": { + countryCode: "KN", + subdivisionName: "Christ Church Nichola Town", + code: "KN-01", + }, + "KN-02": { + countryCode: "KN", + subdivisionName: "Saint Anne Sandy Point", + code: "KN-02", + }, + "KN-03": { + countryCode: "KN", + subdivisionName: "Saint George Basseterre", + code: "KN-03", + }, + "KN-05": { + countryCode: "KN", + subdivisionName: "Saint James Windward", + code: "KN-05", + }, + "KN-06": { + countryCode: "KN", + subdivisionName: "Saint John Capisterre", + code: "KN-06", + }, + "KN-07": { + countryCode: "KN", + subdivisionName: "Saint John Figtree", + code: "KN-07", + }, + "KN-08": { + countryCode: "KN", + subdivisionName: "Saint Mary Cayon", + code: "KN-08", + }, + "KN-09": { + countryCode: "KN", + subdivisionName: "Saint Paul Capisterre", + code: "KN-09", + }, + "KN-10": { + countryCode: "KN", + subdivisionName: "Saint Paul Charlestown", + code: "KN-10", + }, + "KN-11": { + countryCode: "KN", + subdivisionName: "Saint Peter Basseterre", + code: "KN-11", + }, + "KN-12": { + countryCode: "KN", + subdivisionName: "Saint Thomas Lowland", + code: "KN-12", + }, + "KN-13": { + countryCode: "KN", + subdivisionName: "Saint Thomas Middle Island", + code: "KN-13", + }, + "KN-15": { + countryCode: "KN", + subdivisionName: "Trinity Palmetto Point", + code: "KN-15", + }, + "KP-01": { countryCode: "KP", subdivisionName: "P'yongyang", code: "KP-01" }, + "KR-26": { + countryCode: "KR", + subdivisionName: "Busan-gwangyeoksi", + code: "KR-26", + }, + "KR-43": { + countryCode: "KR", + subdivisionName: "Chungcheongbuk-do", + code: "KR-43", + }, + "KR-44": { + countryCode: "KR", + subdivisionName: "Chungcheongnam-do", + code: "KR-44", + }, + "KR-27": { + countryCode: "KR", + subdivisionName: "Daegu-gwangyeoksi", + code: "KR-27", + }, + "KR-30": { + countryCode: "KR", + subdivisionName: "Daejeon-gwangyeoksi", + code: "KR-30", + }, + "KR-42": { countryCode: "KR", subdivisionName: "Gangwon-do", code: "KR-42" }, + "KR-29": { + countryCode: "KR", + subdivisionName: "Gwangju-gwangyeoksi", + code: "KR-29", + }, + "KR-41": { countryCode: "KR", subdivisionName: "Gyeonggi-do", code: "KR-41" }, + "KR-47": { + countryCode: "KR", + subdivisionName: "Gyeongsangbuk-do", + code: "KR-47", + }, + "KR-48": { + countryCode: "KR", + subdivisionName: "Gyeongsangnam-do", + code: "KR-48", + }, + "KR-28": { + countryCode: "KR", + subdivisionName: "Incheon-gwangyeoksi", + code: "KR-28", + }, + "KR-49": { + countryCode: "KR", + subdivisionName: "Jeju-teukbyeoljachido", + code: "KR-49", + }, + "KR-45": { + countryCode: "KR", + subdivisionName: "Jeollabuk-do", + code: "KR-45", + }, + "KR-46": { + countryCode: "KR", + subdivisionName: "Jeollanam-do", + code: "KR-46", + }, + "KR-11": { + countryCode: "KR", + subdivisionName: "Seoul-teukbyeolsi", + code: "KR-11", + }, + "KR-31": { + countryCode: "KR", + subdivisionName: "Ulsan-gwangyeoksi", + code: "KR-31", + }, + "KW-KU": { countryCode: "KW", subdivisionName: "Al 'Asimah", code: "KW-KU" }, + "KW-AH": { countryCode: "KW", subdivisionName: "Al Ahmadi", code: "KW-AH" }, + "KW-FA": { + countryCode: "KW", + subdivisionName: "Al Farwaniyah", + code: "KW-FA", + }, + "KW-JA": { countryCode: "KW", subdivisionName: "Al Jahra'", code: "KW-JA" }, + "KW-HA": { countryCode: "KW", subdivisionName: "Hawalli", code: "KW-HA" }, + "KW-MU": { + countryCode: "KW", + subdivisionName: "Mubarak al Kabir", + code: "KW-MU", + }, + "KZ-10": { countryCode: "KZ", subdivisionName: "Abay oblysy", code: "KZ-10" }, + "KZ-75": { countryCode: "KZ", subdivisionName: "Almaty", code: "KZ-75" }, + "KZ-19": { + countryCode: "KZ", + subdivisionName: "Almaty oblysy", + code: "KZ-19", + }, + "KZ-11": { + countryCode: "KZ", + subdivisionName: "Aqmola oblysy", + code: "KZ-11", + }, + "KZ-15": { + countryCode: "KZ", + subdivisionName: "Aqtobe oblysy", + code: "KZ-15", + }, + "KZ-71": { countryCode: "KZ", subdivisionName: "Astana", code: "KZ-71" }, + "KZ-23": { + countryCode: "KZ", + subdivisionName: "Atyrau oblysy", + code: "KZ-23", + }, + "KZ-27": { + countryCode: "KZ", + subdivisionName: "Batys Qazaqstan oblysy", + code: "KZ-27", + }, + "KZ-47": { + countryCode: "KZ", + subdivisionName: "Mangghystau oblysy", + code: "KZ-47", + }, + "KZ-55": { + countryCode: "KZ", + subdivisionName: "Pavlodar oblysy", + code: "KZ-55", + }, + "KZ-35": { + countryCode: "KZ", + subdivisionName: "Qaraghandy oblysy", + code: "KZ-35", + }, + "KZ-39": { + countryCode: "KZ", + subdivisionName: "Qostanay oblysy", + code: "KZ-39", + }, + "KZ-43": { + countryCode: "KZ", + subdivisionName: "Qyzylorda oblysy", + code: "KZ-43", + }, + "KZ-63": { + countryCode: "KZ", + subdivisionName: "Shyghys Qazaqstan oblysy", + code: "KZ-63", + }, + "KZ-79": { countryCode: "KZ", subdivisionName: "Shymkent", code: "KZ-79" }, + "KZ-59": { + countryCode: "KZ", + subdivisionName: "Soltustik Qazaqstan oblysy", + code: "KZ-59", + }, + "KZ-61": { + countryCode: "KZ", + subdivisionName: "Turkistan oblysy", + code: "KZ-61", + }, + "KZ-62": { + countryCode: "KZ", + subdivisionName: "Ulytau oblysy", + code: "KZ-62", + }, + "KZ-31": { + countryCode: "KZ", + subdivisionName: "Zhambyl oblysy", + code: "KZ-31", + }, + "KZ-33": { + countryCode: "KZ", + subdivisionName: "Zhetisu oblysy", + code: "KZ-33", + }, + "LA-AT": { countryCode: "LA", subdivisionName: "Attapu", code: "LA-AT" }, + "LA-BK": { countryCode: "LA", subdivisionName: "Bokeo", code: "LA-BK" }, + "LA-BL": { countryCode: "LA", subdivisionName: "Bolikhamxai", code: "LA-BL" }, + "LA-CH": { countryCode: "LA", subdivisionName: "Champasak", code: "LA-CH" }, + "LA-KH": { countryCode: "LA", subdivisionName: "Khammouan", code: "LA-KH" }, + "LA-LP": { + countryCode: "LA", + subdivisionName: "Louangphabang", + code: "LA-LP", + }, + "LA-OU": { countryCode: "LA", subdivisionName: "Oudomxai", code: "LA-OU" }, + "LA-PH": { countryCode: "LA", subdivisionName: "Phongsali", code: "LA-PH" }, + "LA-SV": { countryCode: "LA", subdivisionName: "Savannakhet", code: "LA-SV" }, + "LA-VI": { countryCode: "LA", subdivisionName: "Viangchan", code: "LA-VI" }, + "LA-XA": { countryCode: "LA", subdivisionName: "Xaignabouli", code: "LA-XA" }, + "LA-XE": { countryCode: "LA", subdivisionName: "Xekong", code: "LA-XE" }, + "LA-XI": { + countryCode: "LA", + subdivisionName: "Xiangkhouang", + code: "LA-XI", + }, + "LB-AK": { countryCode: "LB", subdivisionName: "Aakkar", code: "LB-AK" }, + "LB-BH": { + countryCode: "LB", + subdivisionName: "Baalbek-Hermel", + code: "LB-BH", + }, + "LB-BI": { countryCode: "LB", subdivisionName: "Beqaa", code: "LB-BI" }, + "LB-BA": { countryCode: "LB", subdivisionName: "Beyrouth", code: "LB-BA" }, + "LB-AS": { countryCode: "LB", subdivisionName: "Liban-Nord", code: "LB-AS" }, + "LB-JA": { countryCode: "LB", subdivisionName: "Liban-Sud", code: "LB-JA" }, + "LB-JL": { countryCode: "LB", subdivisionName: "Mont-Liban", code: "LB-JL" }, + "LB-NA": { countryCode: "LB", subdivisionName: "Nabatiye", code: "LB-NA" }, + "LC-01": { + countryCode: "LC", + subdivisionName: "Anse la Raye", + code: "LC-01", + }, + "LC-02": { countryCode: "LC", subdivisionName: "Castries", code: "LC-02" }, + "LC-03": { countryCode: "LC", subdivisionName: "Choiseul", code: "LC-03" }, + "LC-05": { countryCode: "LC", subdivisionName: "Dennery", code: "LC-05" }, + "LC-06": { countryCode: "LC", subdivisionName: "Gros Islet", code: "LC-06" }, + "LC-07": { countryCode: "LC", subdivisionName: "Laborie", code: "LC-07" }, + "LC-08": { countryCode: "LC", subdivisionName: "Micoud", code: "LC-08" }, + "LC-10": { countryCode: "LC", subdivisionName: "Soufriere", code: "LC-10" }, + "LC-11": { countryCode: "LC", subdivisionName: "Vieux Fort", code: "LC-11" }, + "LI-01": { countryCode: "LI", subdivisionName: "Balzers", code: "LI-01" }, + "LI-02": { countryCode: "LI", subdivisionName: "Eschen", code: "LI-02" }, + "LI-03": { countryCode: "LI", subdivisionName: "Gamprin", code: "LI-03" }, + "LI-04": { countryCode: "LI", subdivisionName: "Mauren", code: "LI-04" }, + "LI-06": { countryCode: "LI", subdivisionName: "Ruggell", code: "LI-06" }, + "LI-07": { countryCode: "LI", subdivisionName: "Schaan", code: "LI-07" }, + "LI-09": { countryCode: "LI", subdivisionName: "Triesen", code: "LI-09" }, + "LI-10": { countryCode: "LI", subdivisionName: "Triesenberg", code: "LI-10" }, + "LI-11": { countryCode: "LI", subdivisionName: "Vaduz", code: "LI-11" }, + "LK-2": { + countryCode: "LK", + subdivisionName: "Central Province", + code: "LK-2", + }, + "LK-5": { + countryCode: "LK", + subdivisionName: "Eastern Province", + code: "LK-5", + }, + "LK-7": { + countryCode: "LK", + subdivisionName: "North Central Province", + code: "LK-7", + }, + "LK-6": { + countryCode: "LK", + subdivisionName: "North Western Province", + code: "LK-6", + }, + "LK-4": { + countryCode: "LK", + subdivisionName: "Northern Province", + code: "LK-4", + }, + "LK-9": { + countryCode: "LK", + subdivisionName: "Sabaragamuwa Province", + code: "LK-9", + }, + "LK-3": { + countryCode: "LK", + subdivisionName: "Southern Province", + code: "LK-3", + }, + "LK-8": { countryCode: "LK", subdivisionName: "Uva Province", code: "LK-8" }, + "LK-1": { + countryCode: "LK", + subdivisionName: "Western Province", + code: "LK-1", + }, + "LR-BM": { countryCode: "LR", subdivisionName: "Bomi", code: "LR-BM" }, + "LR-BG": { countryCode: "LR", subdivisionName: "Bong", code: "LR-BG" }, + "LR-CM": { + countryCode: "LR", + subdivisionName: "Grand Cape Mount", + code: "LR-CM", + }, + "LR-GG": { countryCode: "LR", subdivisionName: "Grand Gedeh", code: "LR-GG" }, + "LR-MG": { countryCode: "LR", subdivisionName: "Margibi", code: "LR-MG" }, + "LR-MO": { countryCode: "LR", subdivisionName: "Montserrado", code: "LR-MO" }, + "LR-RI": { countryCode: "LR", subdivisionName: "River Cess", code: "LR-RI" }, + "LR-SI": { countryCode: "LR", subdivisionName: "Sinoe", code: "LR-SI" }, + "LS-D": { countryCode: "LS", subdivisionName: "Berea", code: "LS-D" }, + "LS-B": { countryCode: "LS", subdivisionName: "Botha-Bothe", code: "LS-B" }, + "LS-C": { countryCode: "LS", subdivisionName: "Leribe", code: "LS-C" }, + "LS-E": { countryCode: "LS", subdivisionName: "Mafeteng", code: "LS-E" }, + "LS-A": { countryCode: "LS", subdivisionName: "Maseru", code: "LS-A" }, + "LS-F": { countryCode: "LS", subdivisionName: "Mohale's Hoek", code: "LS-F" }, + "LS-J": { countryCode: "LS", subdivisionName: "Mokhotlong", code: "LS-J" }, + "LS-H": { countryCode: "LS", subdivisionName: "Qacha's Nek", code: "LS-H" }, + "LS-G": { countryCode: "LS", subdivisionName: "Quthing", code: "LS-G" }, + "LS-K": { countryCode: "LS", subdivisionName: "Thaba-Tseka", code: "LS-K" }, + "LT-AL": { + countryCode: "LT", + subdivisionName: "Alytaus apskritis", + code: "LT-AL", + }, + "LT-KU": { + countryCode: "LT", + subdivisionName: "Kauno apskritis", + code: "LT-KU", + }, + "LT-KL": { + countryCode: "LT", + subdivisionName: "Klaipedos apskritis", + code: "LT-KL", + }, + "LT-MR": { + countryCode: "LT", + subdivisionName: "Marijampoles apskritis", + code: "LT-MR", + }, + "LT-PN": { + countryCode: "LT", + subdivisionName: "Panevezio apskritis", + code: "LT-PN", + }, + "LT-SA": { + countryCode: "LT", + subdivisionName: "Siauliu apskritis", + code: "LT-SA", + }, + "LT-TA": { + countryCode: "LT", + subdivisionName: "Taurages apskritis", + code: "LT-TA", + }, + "LT-TE": { + countryCode: "LT", + subdivisionName: "Telsiu apskritis", + code: "LT-TE", + }, + "LT-UT": { + countryCode: "LT", + subdivisionName: "Utenos apskritis", + code: "LT-UT", + }, + "LT-VL": { + countryCode: "LT", + subdivisionName: "Vilniaus apskritis", + code: "LT-VL", + }, + "LU-CA": { countryCode: "LU", subdivisionName: "Capellen", code: "LU-CA" }, + "LU-CL": { countryCode: "LU", subdivisionName: "Clervaux", code: "LU-CL" }, + "LU-DI": { countryCode: "LU", subdivisionName: "Diekirch", code: "LU-DI" }, + "LU-EC": { countryCode: "LU", subdivisionName: "Echternach", code: "LU-EC" }, + "LU-ES": { + countryCode: "LU", + subdivisionName: "Esch-sur-Alzette", + code: "LU-ES", + }, + "LU-GR": { + countryCode: "LU", + subdivisionName: "Grevenmacher", + code: "LU-GR", + }, + "LU-LU": { countryCode: "LU", subdivisionName: "Luxembourg", code: "LU-LU" }, + "LU-ME": { countryCode: "LU", subdivisionName: "Mersch", code: "LU-ME" }, + "LU-RD": { countryCode: "LU", subdivisionName: "Redange", code: "LU-RD" }, + "LU-RM": { countryCode: "LU", subdivisionName: "Remich", code: "LU-RM" }, + "LU-VD": { countryCode: "LU", subdivisionName: "Vianden", code: "LU-VD" }, + "LU-WI": { countryCode: "LU", subdivisionName: "Wiltz", code: "LU-WI" }, + "LV-011": { + countryCode: "LV", + subdivisionName: "Adazu novads", + code: "LV-011", + }, + "LV-002": { + countryCode: "LV", + subdivisionName: "Aizkraukles novads", + code: "LV-002", + }, + "LV-007": { + countryCode: "LV", + subdivisionName: "Aluksnes novads", + code: "LV-007", + }, + "LV-111": { + countryCode: "LV", + subdivisionName: "Augsdaugavas novads", + code: "LV-111", + }, + "LV-015": { + countryCode: "LV", + subdivisionName: "Balvu novads", + code: "LV-015", + }, + "LV-016": { + countryCode: "LV", + subdivisionName: "Bauskas novads", + code: "LV-016", + }, + "LV-022": { + countryCode: "LV", + subdivisionName: "Cesu novads", + code: "LV-022", + }, + "LV-DGV": { + countryCode: "LV", + subdivisionName: "Daugavpils", + code: "LV-DGV", + }, + "LV-112": { + countryCode: "LV", + subdivisionName: "Dienvidkurzemes novads", + code: "LV-112", + }, + "LV-026": { + countryCode: "LV", + subdivisionName: "Dobeles novads", + code: "LV-026", + }, + "LV-033": { + countryCode: "LV", + subdivisionName: "Gulbenes novads", + code: "LV-033", + }, + "LV-042": { + countryCode: "LV", + subdivisionName: "Jekabpils novads", + code: "LV-042", + }, + "LV-JEL": { countryCode: "LV", subdivisionName: "Jelgava", code: "LV-JEL" }, + "LV-041": { + countryCode: "LV", + subdivisionName: "Jelgavas novads", + code: "LV-041", + }, + "LV-JUR": { countryCode: "LV", subdivisionName: "Jurmala", code: "LV-JUR" }, + "LV-052": { + countryCode: "LV", + subdivisionName: "Kekavas novads", + code: "LV-052", + }, + "LV-047": { + countryCode: "LV", + subdivisionName: "Kraslavas novads", + code: "LV-047", + }, + "LV-050": { + countryCode: "LV", + subdivisionName: "Kuldigas novads", + code: "LV-050", + }, + "LV-LPX": { countryCode: "LV", subdivisionName: "Liepaja", code: "LV-LPX" }, + "LV-054": { + countryCode: "LV", + subdivisionName: "Limbazu novads", + code: "LV-054", + }, + "LV-056": { + countryCode: "LV", + subdivisionName: "Livanu novads", + code: "LV-056", + }, + "LV-058": { + countryCode: "LV", + subdivisionName: "Ludzas novads", + code: "LV-058", + }, + "LV-059": { + countryCode: "LV", + subdivisionName: "Madonas novads", + code: "LV-059", + }, + "LV-062": { + countryCode: "LV", + subdivisionName: "Marupes novads", + code: "LV-062", + }, + "LV-067": { + countryCode: "LV", + subdivisionName: "Ogres novads", + code: "LV-067", + }, + "LV-068": { + countryCode: "LV", + subdivisionName: "Olaines novads", + code: "LV-068", + }, + "LV-073": { + countryCode: "LV", + subdivisionName: "Preilu novads", + code: "LV-073", + }, + "LV-077": { + countryCode: "LV", + subdivisionName: "Rezeknes novads", + code: "LV-077", + }, + "LV-RIX": { countryCode: "LV", subdivisionName: "Riga", code: "LV-RIX" }, + "LV-080": { + countryCode: "LV", + subdivisionName: "Ropazu novads", + code: "LV-080", + }, + "LV-087": { + countryCode: "LV", + subdivisionName: "Salaspils novads", + code: "LV-087", + }, + "LV-088": { + countryCode: "LV", + subdivisionName: "Saldus novads", + code: "LV-088", + }, + "LV-089": { + countryCode: "LV", + subdivisionName: "Saulkrastu novads", + code: "LV-089", + }, + "LV-091": { + countryCode: "LV", + subdivisionName: "Siguldas novads", + code: "LV-091", + }, + "LV-094": { + countryCode: "LV", + subdivisionName: "Smiltenes novads", + code: "LV-094", + }, + "LV-097": { + countryCode: "LV", + subdivisionName: "Talsu novads", + code: "LV-097", + }, + "LV-099": { + countryCode: "LV", + subdivisionName: "Tukuma novads", + code: "LV-099", + }, + "LV-101": { + countryCode: "LV", + subdivisionName: "Valkas novads", + code: "LV-101", + }, + "LV-113": { + countryCode: "LV", + subdivisionName: "Valmieras novads", + code: "LV-113", + }, + "LV-102": { + countryCode: "LV", + subdivisionName: "Varaklanu novads", + code: "LV-102", + }, + "LV-106": { + countryCode: "LV", + subdivisionName: "Ventspils novads", + code: "LV-106", + }, + "LY-BU": { countryCode: "LY", subdivisionName: "Al Butnan", code: "LY-BU" }, + "LY-JA": { + countryCode: "LY", + subdivisionName: "Al Jabal al Akhdar", + code: "LY-JA", + }, + "LY-JG": { + countryCode: "LY", + subdivisionName: "Al Jabal al Gharbi", + code: "LY-JG", + }, + "LY-JI": { countryCode: "LY", subdivisionName: "Al Jafarah", code: "LY-JI" }, + "LY-JU": { countryCode: "LY", subdivisionName: "Al Jufrah", code: "LY-JU" }, + "LY-KF": { countryCode: "LY", subdivisionName: "Al Kufrah", code: "LY-KF" }, + "LY-MJ": { countryCode: "LY", subdivisionName: "Al Marj", code: "LY-MJ" }, + "LY-MB": { countryCode: "LY", subdivisionName: "Al Marqab", code: "LY-MB" }, + "LY-WA": { countryCode: "LY", subdivisionName: "Al Wahat", code: "LY-WA" }, + "LY-NQ": { + countryCode: "LY", + subdivisionName: "An Nuqat al Khams", + code: "LY-NQ", + }, + "LY-ZA": { countryCode: "LY", subdivisionName: "Az Zawiyah", code: "LY-ZA" }, + "LY-BA": { countryCode: "LY", subdivisionName: "Banghazi", code: "LY-BA" }, + "LY-DR": { countryCode: "LY", subdivisionName: "Darnah", code: "LY-DR" }, + "LY-MI": { countryCode: "LY", subdivisionName: "Misratah", code: "LY-MI" }, + "LY-MQ": { countryCode: "LY", subdivisionName: "Murzuq", code: "LY-MQ" }, + "LY-NL": { countryCode: "LY", subdivisionName: "Nalut", code: "LY-NL" }, + "LY-SB": { countryCode: "LY", subdivisionName: "Sabha", code: "LY-SB" }, + "LY-SR": { countryCode: "LY", subdivisionName: "Surt", code: "LY-SR" }, + "LY-TB": { countryCode: "LY", subdivisionName: "Tarabulus", code: "LY-TB" }, + "LY-WS": { + countryCode: "LY", + subdivisionName: "Wadi ash Shati'", + code: "LY-WS", + }, + "MA-05": { + countryCode: "MA", + subdivisionName: "Beni-Mellal-Khenifra", + code: "MA-05", + }, + "MA-06": { + countryCode: "MA", + subdivisionName: "Casablanca-Settat", + code: "MA-06", + }, + "MA-08": { + countryCode: "MA", + subdivisionName: "Draa-Tafilalet", + code: "MA-08", + }, + "MA-03": { countryCode: "MA", subdivisionName: "Fes- Meknes", code: "MA-03" }, + "MA-10": { + countryCode: "MA", + subdivisionName: "Guelmim-Oued Noun (EH-partial)", + code: "MA-10", + }, + "MA-02": { countryCode: "MA", subdivisionName: "L'Oriental", code: "MA-02" }, + "MA-11": { + countryCode: "MA", + subdivisionName: "Laayoune-Sakia El Hamra (EH-partial)", + code: "MA-11", + }, + "MA-07": { + countryCode: "MA", + subdivisionName: "Marrakech-Safi", + code: "MA-07", + }, + "MA-04": { + countryCode: "MA", + subdivisionName: "Rabat-Sale-Kenitra", + code: "MA-04", + }, + "MA-09": { countryCode: "MA", subdivisionName: "Souss-Massa", code: "MA-09" }, + "MA-01": { + countryCode: "MA", + subdivisionName: "Tanger-Tetouan-Al Hoceima", + code: "MA-01", + }, + "MC-FO": { countryCode: "MC", subdivisionName: "Fontvieille", code: "MC-FO" }, + "MC-CO": { + countryCode: "MC", + subdivisionName: "La Condamine", + code: "MC-CO", + }, + "MC-MO": { + countryCode: "MC", + subdivisionName: "Monaco-Ville", + code: "MC-MO", + }, + "MC-MG": { countryCode: "MC", subdivisionName: "Moneghetti", code: "MC-MG" }, + "MC-MC": { countryCode: "MC", subdivisionName: "Monte-Carlo", code: "MC-MC" }, + "MC-SR": { countryCode: "MC", subdivisionName: "Saint-Roman", code: "MC-SR" }, + "MD-AN": { countryCode: "MD", subdivisionName: "Anenii Noi", code: "MD-AN" }, + "MD-BA": { countryCode: "MD", subdivisionName: "Balti", code: "MD-BA" }, + "MD-BS": { + countryCode: "MD", + subdivisionName: "Basarabeasca", + code: "MD-BS", + }, + "MD-BD": { countryCode: "MD", subdivisionName: "Bender", code: "MD-BD" }, + "MD-BR": { countryCode: "MD", subdivisionName: "Briceni", code: "MD-BR" }, + "MD-CA": { countryCode: "MD", subdivisionName: "Cahul", code: "MD-CA" }, + "MD-CL": { countryCode: "MD", subdivisionName: "Calarasi", code: "MD-CL" }, + "MD-CT": { countryCode: "MD", subdivisionName: "Cantemir", code: "MD-CT" }, + "MD-CS": { countryCode: "MD", subdivisionName: "Causeni", code: "MD-CS" }, + "MD-CU": { countryCode: "MD", subdivisionName: "Chisinau", code: "MD-CU" }, + "MD-CM": { countryCode: "MD", subdivisionName: "Cimislia", code: "MD-CM" }, + "MD-CR": { countryCode: "MD", subdivisionName: "Criuleni", code: "MD-CR" }, + "MD-DO": { countryCode: "MD", subdivisionName: "Donduseni", code: "MD-DO" }, + "MD-DR": { countryCode: "MD", subdivisionName: "Drochia", code: "MD-DR" }, + "MD-DU": { countryCode: "MD", subdivisionName: "Dubasari", code: "MD-DU" }, + "MD-ED": { countryCode: "MD", subdivisionName: "Edinet", code: "MD-ED" }, + "MD-FA": { countryCode: "MD", subdivisionName: "Falesti", code: "MD-FA" }, + "MD-FL": { countryCode: "MD", subdivisionName: "Floresti", code: "MD-FL" }, + "MD-GA": { + countryCode: "MD", + subdivisionName: "Gagauzia, Unitatea teritoriala autonoma", + code: "MD-GA", + }, + "MD-GL": { countryCode: "MD", subdivisionName: "Glodeni", code: "MD-GL" }, + "MD-HI": { countryCode: "MD", subdivisionName: "Hincesti", code: "MD-HI" }, + "MD-IA": { countryCode: "MD", subdivisionName: "Ialoveni", code: "MD-IA" }, + "MD-LE": { countryCode: "MD", subdivisionName: "Leova", code: "MD-LE" }, + "MD-NI": { countryCode: "MD", subdivisionName: "Nisporeni", code: "MD-NI" }, + "MD-OC": { countryCode: "MD", subdivisionName: "Ocnita", code: "MD-OC" }, + "MD-OR": { countryCode: "MD", subdivisionName: "Orhei", code: "MD-OR" }, + "MD-RE": { countryCode: "MD", subdivisionName: "Rezina", code: "MD-RE" }, + "MD-RI": { countryCode: "MD", subdivisionName: "Riscani", code: "MD-RI" }, + "MD-SI": { countryCode: "MD", subdivisionName: "Singerei", code: "MD-SI" }, + "MD-SD": { countryCode: "MD", subdivisionName: "Soldanesti", code: "MD-SD" }, + "MD-SO": { countryCode: "MD", subdivisionName: "Soroca", code: "MD-SO" }, + "MD-SV": { countryCode: "MD", subdivisionName: "Stefan Voda", code: "MD-SV" }, + "MD-SN": { + countryCode: "MD", + subdivisionName: "Stinga Nistrului, unitatea teritoriala din", + code: "MD-SN", + }, + "MD-ST": { countryCode: "MD", subdivisionName: "Straseni", code: "MD-ST" }, + "MD-TA": { countryCode: "MD", subdivisionName: "Taraclia", code: "MD-TA" }, + "MD-TE": { countryCode: "MD", subdivisionName: "Telenesti", code: "MD-TE" }, + "MD-UN": { countryCode: "MD", subdivisionName: "Ungheni", code: "MD-UN" }, + "ME-01": { countryCode: "ME", subdivisionName: "Andrijevica", code: "ME-01" }, + "ME-02": { countryCode: "ME", subdivisionName: "Bar", code: "ME-02" }, + "ME-03": { countryCode: "ME", subdivisionName: "Berane", code: "ME-03" }, + "ME-04": { + countryCode: "ME", + subdivisionName: "Bijelo Polje", + code: "ME-04", + }, + "ME-05": { countryCode: "ME", subdivisionName: "Budva", code: "ME-05" }, + "ME-06": { countryCode: "ME", subdivisionName: "Cetinje", code: "ME-06" }, + "ME-07": { countryCode: "ME", subdivisionName: "Danilovgrad", code: "ME-07" }, + "ME-08": { countryCode: "ME", subdivisionName: "Herceg-Novi", code: "ME-08" }, + "ME-09": { countryCode: "ME", subdivisionName: "Kolasin", code: "ME-09" }, + "ME-10": { countryCode: "ME", subdivisionName: "Kotor", code: "ME-10" }, + "ME-12": { countryCode: "ME", subdivisionName: "Niksic", code: "ME-12" }, + "ME-13": { countryCode: "ME", subdivisionName: "Plav", code: "ME-13" }, + "ME-14": { countryCode: "ME", subdivisionName: "Pljevlja", code: "ME-14" }, + "ME-15": { countryCode: "ME", subdivisionName: "Pluzine", code: "ME-15" }, + "ME-16": { countryCode: "ME", subdivisionName: "Podgorica", code: "ME-16" }, + "ME-17": { countryCode: "ME", subdivisionName: "Rozaje", code: "ME-17" }, + "ME-19": { countryCode: "ME", subdivisionName: "Tivat", code: "ME-19" }, + "ME-24": { countryCode: "ME", subdivisionName: "Tuzi", code: "ME-24" }, + "ME-20": { countryCode: "ME", subdivisionName: "Ulcinj", code: "ME-20" }, + "ME-21": { countryCode: "ME", subdivisionName: "Zabljak", code: "ME-21" }, + "MG-T": { countryCode: "MG", subdivisionName: "Antananarivo", code: "MG-T" }, + "MG-D": { countryCode: "MG", subdivisionName: "Antsiranana", code: "MG-D" }, + "MG-F": { countryCode: "MG", subdivisionName: "Fianarantsoa", code: "MG-F" }, + "MG-M": { countryCode: "MG", subdivisionName: "Mahajanga", code: "MG-M" }, + "MG-A": { countryCode: "MG", subdivisionName: "Toamasina", code: "MG-A" }, + "MG-U": { countryCode: "MG", subdivisionName: "Toliara", code: "MG-U" }, + "MH-KWA": { countryCode: "MH", subdivisionName: "Kwajalein", code: "MH-KWA" }, + "MH-MAJ": { countryCode: "MH", subdivisionName: "Majuro", code: "MH-MAJ" }, + "MK-201": { countryCode: "MK", subdivisionName: "Berovo", code: "MK-201" }, + "MK-501": { countryCode: "MK", subdivisionName: "Bitola", code: "MK-501" }, + "MK-401": { countryCode: "MK", subdivisionName: "Bogdanci", code: "MK-401" }, + "MK-601": { countryCode: "MK", subdivisionName: "Bogovinje", code: "MK-601" }, + "MK-402": { countryCode: "MK", subdivisionName: "Bosilovo", code: "MK-402" }, + "MK-602": { countryCode: "MK", subdivisionName: "Brvenica", code: "MK-602" }, + "MK-803": { countryCode: "MK", subdivisionName: "Butel", code: "MK-803" }, + "MK-109": { countryCode: "MK", subdivisionName: "Caska", code: "MK-109" }, + "MK-814": { countryCode: "MK", subdivisionName: "Centar", code: "MK-814" }, + "MK-210": { + countryCode: "MK", + subdivisionName: "Cesinovo-Oblesevo", + code: "MK-210", + }, + "MK-816": { + countryCode: "MK", + subdivisionName: "Cucer Sandevo", + code: "MK-816", + }, + "MK-303": { countryCode: "MK", subdivisionName: "Debar", code: "MK-303" }, + "MK-502": { + countryCode: "MK", + subdivisionName: "Demir Hisar", + code: "MK-502", + }, + "MK-103": { + countryCode: "MK", + subdivisionName: "Demir Kapija", + code: "MK-103", + }, + "MK-406": { countryCode: "MK", subdivisionName: "Dojran", code: "MK-406" }, + "MK-503": { countryCode: "MK", subdivisionName: "Dolneni", code: "MK-503" }, + "MK-804": { countryCode: "MK", subdivisionName: "Gazi Baba", code: "MK-804" }, + "MK-405": { countryCode: "MK", subdivisionName: "Gevgelija", code: "MK-405" }, + "MK-604": { countryCode: "MK", subdivisionName: "Gostivar", code: "MK-604" }, + "MK-102": { countryCode: "MK", subdivisionName: "Gradsko", code: "MK-102" }, + "MK-807": { countryCode: "MK", subdivisionName: "Ilinden", code: "MK-807" }, + "MK-606": { countryCode: "MK", subdivisionName: "Jegunovce", code: "MK-606" }, + "MK-205": { countryCode: "MK", subdivisionName: "Karbinci", code: "MK-205" }, + "MK-104": { countryCode: "MK", subdivisionName: "Kavadarci", code: "MK-104" }, + "MK-307": { countryCode: "MK", subdivisionName: "Kicevo", code: "MK-307" }, + "MK-809": { + countryCode: "MK", + subdivisionName: "Kisela Voda", + code: "MK-809", + }, + "MK-206": { countryCode: "MK", subdivisionName: "Kocani", code: "MK-206" }, + "MK-701": { countryCode: "MK", subdivisionName: "Kratovo", code: "MK-701" }, + "MK-702": { + countryCode: "MK", + subdivisionName: "Kriva Palanka", + code: "MK-702", + }, + "MK-504": { + countryCode: "MK", + subdivisionName: "Krivogastani", + code: "MK-504", + }, + "MK-505": { countryCode: "MK", subdivisionName: "Krusevo", code: "MK-505" }, + "MK-703": { countryCode: "MK", subdivisionName: "Kumanovo", code: "MK-703" }, + "MK-704": { countryCode: "MK", subdivisionName: "Lipkovo", code: "MK-704" }, + "MK-105": { countryCode: "MK", subdivisionName: "Lozovo", code: "MK-105" }, + "MK-207": { + countryCode: "MK", + subdivisionName: "Makedonska Kamenica", + code: "MK-207", + }, + "MK-308": { + countryCode: "MK", + subdivisionName: "Makedonski Brod", + code: "MK-308", + }, + "MK-607": { + countryCode: "MK", + subdivisionName: "Mavrovo i Rostusa", + code: "MK-607", + }, + "MK-506": { countryCode: "MK", subdivisionName: "Mogila", code: "MK-506" }, + "MK-106": { countryCode: "MK", subdivisionName: "Negotino", code: "MK-106" }, + "MK-507": { countryCode: "MK", subdivisionName: "Novaci", code: "MK-507" }, + "MK-408": { countryCode: "MK", subdivisionName: "Novo Selo", code: "MK-408" }, + "MK-310": { countryCode: "MK", subdivisionName: "Ohrid", code: "MK-310" }, + "MK-208": { countryCode: "MK", subdivisionName: "Pehcevo", code: "MK-208" }, + "MK-810": { countryCode: "MK", subdivisionName: "Petrovec", code: "MK-810" }, + "MK-311": { countryCode: "MK", subdivisionName: "Plasnica", code: "MK-311" }, + "MK-508": { countryCode: "MK", subdivisionName: "Prilep", code: "MK-508" }, + "MK-209": { countryCode: "MK", subdivisionName: "Probistip", code: "MK-209" }, + "MK-409": { countryCode: "MK", subdivisionName: "Radovis", code: "MK-409" }, + "MK-705": { countryCode: "MK", subdivisionName: "Rankovce", code: "MK-705" }, + "MK-509": { countryCode: "MK", subdivisionName: "Resen", code: "MK-509" }, + "MK-107": { countryCode: "MK", subdivisionName: "Rosoman", code: "MK-107" }, + "MK-811": { countryCode: "MK", subdivisionName: "Saraj", code: "MK-811" }, + "MK-812": { countryCode: "MK", subdivisionName: "Sopiste", code: "MK-812" }, + "MK-211": { countryCode: "MK", subdivisionName: "Stip", code: "MK-211" }, + "MK-312": { countryCode: "MK", subdivisionName: "Struga", code: "MK-312" }, + "MK-410": { countryCode: "MK", subdivisionName: "Strumica", code: "MK-410" }, + "MK-813": { + countryCode: "MK", + subdivisionName: "Studenicani", + code: "MK-813", + }, + "MK-108": { + countryCode: "MK", + subdivisionName: "Sveti Nikole", + code: "MK-108", + }, + "MK-608": { countryCode: "MK", subdivisionName: "Tearce", code: "MK-608" }, + "MK-609": { countryCode: "MK", subdivisionName: "Tetovo", code: "MK-609" }, + "MK-403": { countryCode: "MK", subdivisionName: "Valandovo", code: "MK-403" }, + "MK-404": { countryCode: "MK", subdivisionName: "Vasilevo", code: "MK-404" }, + "MK-101": { countryCode: "MK", subdivisionName: "Veles", code: "MK-101" }, + "MK-301": { countryCode: "MK", subdivisionName: "Vevcani", code: "MK-301" }, + "MK-202": { countryCode: "MK", subdivisionName: "Vinica", code: "MK-202" }, + "MK-806": { + countryCode: "MK", + subdivisionName: "Zelenikovo", + code: "MK-806", + }, + "MK-605": { countryCode: "MK", subdivisionName: "Zelino", code: "MK-605" }, + "ML-BKO": { countryCode: "ML", subdivisionName: "Bamako", code: "ML-BKO" }, + "ML-7": { countryCode: "ML", subdivisionName: "Gao", code: "ML-7" }, + "ML-1": { countryCode: "ML", subdivisionName: "Kayes", code: "ML-1" }, + "ML-8": { countryCode: "ML", subdivisionName: "Kidal", code: "ML-8" }, + "ML-2": { countryCode: "ML", subdivisionName: "Koulikoro", code: "ML-2" }, + "ML-5": { countryCode: "ML", subdivisionName: "Mopti", code: "ML-5" }, + "ML-4": { countryCode: "ML", subdivisionName: "Segou", code: "ML-4" }, + "ML-3": { countryCode: "ML", subdivisionName: "Sikasso", code: "ML-3" }, + "ML-6": { countryCode: "ML", subdivisionName: "Tombouctou", code: "ML-6" }, + "MM-07": { countryCode: "MM", subdivisionName: "Ayeyarwady", code: "MM-07" }, + "MM-02": { countryCode: "MM", subdivisionName: "Bago", code: "MM-02" }, + "MM-14": { countryCode: "MM", subdivisionName: "Chin", code: "MM-14" }, + "MM-11": { countryCode: "MM", subdivisionName: "Kachin", code: "MM-11" }, + "MM-12": { countryCode: "MM", subdivisionName: "Kayah", code: "MM-12" }, + "MM-13": { countryCode: "MM", subdivisionName: "Kayin", code: "MM-13" }, + "MM-03": { countryCode: "MM", subdivisionName: "Magway", code: "MM-03" }, + "MM-04": { countryCode: "MM", subdivisionName: "Mandalay", code: "MM-04" }, + "MM-15": { countryCode: "MM", subdivisionName: "Mon", code: "MM-15" }, + "MM-18": { countryCode: "MM", subdivisionName: "Nay Pyi Taw", code: "MM-18" }, + "MM-16": { countryCode: "MM", subdivisionName: "Rakhine", code: "MM-16" }, + "MM-01": { countryCode: "MM", subdivisionName: "Sagaing", code: "MM-01" }, + "MM-17": { countryCode: "MM", subdivisionName: "Shan", code: "MM-17" }, + "MM-05": { countryCode: "MM", subdivisionName: "Tanintharyi", code: "MM-05" }, + "MM-06": { countryCode: "MM", subdivisionName: "Yangon", code: "MM-06" }, + "MN-069": { + countryCode: "MN", + subdivisionName: "Bayanhongor", + code: "MN-069", + }, + "MN-037": { + countryCode: "MN", + subdivisionName: "Darhan uul", + code: "MN-037", + }, + "MN-061": { countryCode: "MN", subdivisionName: "Dornod", code: "MN-061" }, + "MN-065": { + countryCode: "MN", + subdivisionName: "Govi-Altay", + code: "MN-065", + }, + "MN-039": { countryCode: "MN", subdivisionName: "Hentiy", code: "MN-039" }, + "MN-043": { countryCode: "MN", subdivisionName: "Hovd", code: "MN-043" }, + "MN-053": { countryCode: "MN", subdivisionName: "Omnogovi", code: "MN-053" }, + "MN-055": { + countryCode: "MN", + subdivisionName: "Ovorhangay", + code: "MN-055", + }, + "MN-049": { countryCode: "MN", subdivisionName: "Selenge", code: "MN-049" }, + "MN-047": { countryCode: "MN", subdivisionName: "Tov", code: "MN-047" }, + "MN-1": { countryCode: "MN", subdivisionName: "Ulaanbaatar", code: "MN-1" }, + "MR-07": { countryCode: "MR", subdivisionName: "Adrar", code: "MR-07" }, + "MR-03": { countryCode: "MR", subdivisionName: "Assaba", code: "MR-03" }, + "MR-05": { countryCode: "MR", subdivisionName: "Brakna", code: "MR-05" }, + "MR-08": { + countryCode: "MR", + subdivisionName: "Dakhlet Nouadhibou", + code: "MR-08", + }, + "MR-04": { countryCode: "MR", subdivisionName: "Gorgol", code: "MR-04" }, + "MR-02": { + countryCode: "MR", + subdivisionName: "Hodh el Gharbi", + code: "MR-02", + }, + "MR-12": { countryCode: "MR", subdivisionName: "Inchiri", code: "MR-12" }, + "MR-13": { + countryCode: "MR", + subdivisionName: "Nouakchott Ouest", + code: "MR-13", + }, + "MR-09": { countryCode: "MR", subdivisionName: "Tagant", code: "MR-09" }, + "MR-11": { + countryCode: "MR", + subdivisionName: "Tiris Zemmour", + code: "MR-11", + }, + "MR-06": { countryCode: "MR", subdivisionName: "Trarza", code: "MR-06" }, + "MT-01": { countryCode: "MT", subdivisionName: "Attard", code: "MT-01" }, + "MT-02": { countryCode: "MT", subdivisionName: "Balzan", code: "MT-02" }, + "MT-03": { countryCode: "MT", subdivisionName: "Birgu", code: "MT-03" }, + "MT-04": { countryCode: "MT", subdivisionName: "Birkirkara", code: "MT-04" }, + "MT-05": { countryCode: "MT", subdivisionName: "Birzebbuga", code: "MT-05" }, + "MT-06": { countryCode: "MT", subdivisionName: "Bormla", code: "MT-06" }, + "MT-07": { countryCode: "MT", subdivisionName: "Dingli", code: "MT-07" }, + "MT-08": { countryCode: "MT", subdivisionName: "Fgura", code: "MT-08" }, + "MT-09": { countryCode: "MT", subdivisionName: "Floriana", code: "MT-09" }, + "MT-10": { countryCode: "MT", subdivisionName: "Fontana", code: "MT-10" }, + "MT-13": { countryCode: "MT", subdivisionName: "Ghajnsielem", code: "MT-13" }, + "MT-14": { countryCode: "MT", subdivisionName: "Gharb", code: "MT-14" }, + "MT-15": { countryCode: "MT", subdivisionName: "Gharghur", code: "MT-15" }, + "MT-16": { countryCode: "MT", subdivisionName: "Ghasri", code: "MT-16" }, + "MT-17": { countryCode: "MT", subdivisionName: "Ghaxaq", code: "MT-17" }, + "MT-11": { countryCode: "MT", subdivisionName: "Gudja", code: "MT-11" }, + "MT-12": { countryCode: "MT", subdivisionName: "Gzira", code: "MT-12" }, + "MT-18": { countryCode: "MT", subdivisionName: "Hamrun", code: "MT-18" }, + "MT-19": { countryCode: "MT", subdivisionName: "Iklin", code: "MT-19" }, + "MT-20": { countryCode: "MT", subdivisionName: "Isla", code: "MT-20" }, + "MT-21": { countryCode: "MT", subdivisionName: "Kalkara", code: "MT-21" }, + "MT-23": { countryCode: "MT", subdivisionName: "Kirkop", code: "MT-23" }, + "MT-24": { countryCode: "MT", subdivisionName: "Lija", code: "MT-24" }, + "MT-25": { countryCode: "MT", subdivisionName: "Luqa", code: "MT-25" }, + "MT-26": { countryCode: "MT", subdivisionName: "Marsa", code: "MT-26" }, + "MT-27": { countryCode: "MT", subdivisionName: "Marsaskala", code: "MT-27" }, + "MT-28": { countryCode: "MT", subdivisionName: "Marsaxlokk", code: "MT-28" }, + "MT-29": { countryCode: "MT", subdivisionName: "Mdina", code: "MT-29" }, + "MT-30": { countryCode: "MT", subdivisionName: "Mellieha", code: "MT-30" }, + "MT-31": { countryCode: "MT", subdivisionName: "Mgarr", code: "MT-31" }, + "MT-32": { countryCode: "MT", subdivisionName: "Mosta", code: "MT-32" }, + "MT-33": { countryCode: "MT", subdivisionName: "Mqabba", code: "MT-33" }, + "MT-34": { countryCode: "MT", subdivisionName: "Msida", code: "MT-34" }, + "MT-35": { countryCode: "MT", subdivisionName: "Mtarfa", code: "MT-35" }, + "MT-36": { countryCode: "MT", subdivisionName: "Munxar", code: "MT-36" }, + "MT-37": { countryCode: "MT", subdivisionName: "Nadur", code: "MT-37" }, + "MT-38": { countryCode: "MT", subdivisionName: "Naxxar", code: "MT-38" }, + "MT-39": { countryCode: "MT", subdivisionName: "Paola", code: "MT-39" }, + "MT-40": { countryCode: "MT", subdivisionName: "Pembroke", code: "MT-40" }, + "MT-41": { countryCode: "MT", subdivisionName: "Pieta", code: "MT-41" }, + "MT-42": { countryCode: "MT", subdivisionName: "Qala", code: "MT-42" }, + "MT-43": { countryCode: "MT", subdivisionName: "Qormi", code: "MT-43" }, + "MT-44": { countryCode: "MT", subdivisionName: "Qrendi", code: "MT-44" }, + "MT-45": { countryCode: "MT", subdivisionName: "Rabat Gozo", code: "MT-45" }, + "MT-46": { countryCode: "MT", subdivisionName: "Rabat Malta", code: "MT-46" }, + "MT-47": { countryCode: "MT", subdivisionName: "Safi", code: "MT-47" }, + "MT-49": { countryCode: "MT", subdivisionName: "Saint John", code: "MT-49" }, + "MT-48": { + countryCode: "MT", + subdivisionName: "Saint Julian's", + code: "MT-48", + }, + "MT-50": { + countryCode: "MT", + subdivisionName: "Saint Lawrence", + code: "MT-50", + }, + "MT-53": { + countryCode: "MT", + subdivisionName: "Saint Lucia's", + code: "MT-53", + }, + "MT-51": { + countryCode: "MT", + subdivisionName: "Saint Paul's Bay", + code: "MT-51", + }, + "MT-52": { countryCode: "MT", subdivisionName: "Sannat", code: "MT-52" }, + "MT-54": { + countryCode: "MT", + subdivisionName: "Santa Venera", + code: "MT-54", + }, + "MT-55": { countryCode: "MT", subdivisionName: "Siggiewi", code: "MT-55" }, + "MT-56": { countryCode: "MT", subdivisionName: "Sliema", code: "MT-56" }, + "MT-57": { countryCode: "MT", subdivisionName: "Swieqi", code: "MT-57" }, + "MT-58": { countryCode: "MT", subdivisionName: "Ta' Xbiex", code: "MT-58" }, + "MT-59": { countryCode: "MT", subdivisionName: "Tarxien", code: "MT-59" }, + "MT-60": { countryCode: "MT", subdivisionName: "Valletta", code: "MT-60" }, + "MT-61": { countryCode: "MT", subdivisionName: "Xaghra", code: "MT-61" }, + "MT-62": { countryCode: "MT", subdivisionName: "Xewkija", code: "MT-62" }, + "MT-63": { countryCode: "MT", subdivisionName: "Xghajra", code: "MT-63" }, + "MT-64": { countryCode: "MT", subdivisionName: "Zabbar", code: "MT-64" }, + "MT-65": { countryCode: "MT", subdivisionName: "Zebbug Gozo", code: "MT-65" }, + "MT-67": { countryCode: "MT", subdivisionName: "Zejtun", code: "MT-67" }, + "MT-68": { countryCode: "MT", subdivisionName: "Zurrieq", code: "MT-68" }, + "MU-BL": { countryCode: "MU", subdivisionName: "Black River", code: "MU-BL" }, + "MU-FL": { countryCode: "MU", subdivisionName: "Flacq", code: "MU-FL" }, + "MU-GP": { countryCode: "MU", subdivisionName: "Grand Port", code: "MU-GP" }, + "MU-MO": { countryCode: "MU", subdivisionName: "Moka", code: "MU-MO" }, + "MU-PA": { + countryCode: "MU", + subdivisionName: "Pamplemousses", + code: "MU-PA", + }, + "MU-PW": { + countryCode: "MU", + subdivisionName: "Plaines Wilhems", + code: "MU-PW", + }, + "MU-PL": { countryCode: "MU", subdivisionName: "Port Louis", code: "MU-PL" }, + "MU-RR": { + countryCode: "MU", + subdivisionName: "Riviere du Rempart", + code: "MU-RR", + }, + "MU-RO": { + countryCode: "MU", + subdivisionName: "Rodrigues Islands", + code: "MU-RO", + }, + "MU-SA": { countryCode: "MU", subdivisionName: "Savanne", code: "MU-SA" }, + "MV-01": { countryCode: "MV", subdivisionName: "Addu City", code: "MV-01" }, + "MV-03": { + countryCode: "MV", + subdivisionName: "Faadhippolhu", + code: "MV-03", + }, + "MV-04": { + countryCode: "MV", + subdivisionName: "Felidhu Atoll", + code: "MV-04", + }, + "MV-05": { + countryCode: "MV", + subdivisionName: "Hahdhunmathi", + code: "MV-05", + }, + "MV-MLE": { countryCode: "MV", subdivisionName: "Male", code: "MV-MLE" }, + "MV-12": { + countryCode: "MV", + subdivisionName: "Mulaku Atoll", + code: "MV-12", + }, + "MV-02": { + countryCode: "MV", + subdivisionName: "North Ari Atoll", + code: "MV-02", + }, + "MV-27": { + countryCode: "MV", + subdivisionName: "North Huvadhu Atoll", + code: "MV-27", + }, + "MV-13": { + countryCode: "MV", + subdivisionName: "North Maalhosmadulu", + code: "MV-13", + }, + "MV-07": { + countryCode: "MV", + subdivisionName: "North Thiladhunmathi", + code: "MV-07", + }, + "MV-00": { + countryCode: "MV", + subdivisionName: "South Ari Atoll", + code: "MV-00", + }, + "MV-28": { + countryCode: "MV", + subdivisionName: "South Huvadhu Atoll", + code: "MV-28", + }, + "MV-20": { + countryCode: "MV", + subdivisionName: "South Maalhosmadulu", + code: "MV-20", + }, + "MV-25": { + countryCode: "MV", + subdivisionName: "South Miladhunmadulu", + code: "MV-25", + }, + "MV-23": { + countryCode: "MV", + subdivisionName: "South Thiladhunmathi", + code: "MV-23", + }, + "MW-BA": { countryCode: "MW", subdivisionName: "Balaka", code: "MW-BA" }, + "MW-BL": { countryCode: "MW", subdivisionName: "Blantyre", code: "MW-BL" }, + "MW-CK": { countryCode: "MW", subdivisionName: "Chikwawa", code: "MW-CK" }, + "MW-CR": { countryCode: "MW", subdivisionName: "Chiradzulu", code: "MW-CR" }, + "MW-DE": { countryCode: "MW", subdivisionName: "Dedza", code: "MW-DE" }, + "MW-DO": { countryCode: "MW", subdivisionName: "Dowa", code: "MW-DO" }, + "MW-KR": { countryCode: "MW", subdivisionName: "Karonga", code: "MW-KR" }, + "MW-LI": { countryCode: "MW", subdivisionName: "Lilongwe", code: "MW-LI" }, + "MW-MH": { countryCode: "MW", subdivisionName: "Machinga", code: "MW-MH" }, + "MW-MG": { countryCode: "MW", subdivisionName: "Mangochi", code: "MW-MG" }, + "MW-MW": { countryCode: "MW", subdivisionName: "Mwanza", code: "MW-MW" }, + "MW-MZ": { countryCode: "MW", subdivisionName: "Mzimba", code: "MW-MZ" }, + "MW-NE": { countryCode: "MW", subdivisionName: "Neno", code: "MW-NE" }, + "MW-NK": { countryCode: "MW", subdivisionName: "Nkhotakota", code: "MW-NK" }, + "MW-NI": { countryCode: "MW", subdivisionName: "Ntchisi", code: "MW-NI" }, + "MW-SA": { countryCode: "MW", subdivisionName: "Salima", code: "MW-SA" }, + "MW-TH": { countryCode: "MW", subdivisionName: "Thyolo", code: "MW-TH" }, + "MW-ZO": { countryCode: "MW", subdivisionName: "Zomba", code: "MW-ZO" }, + "MX-AGU": { + countryCode: "MX", + subdivisionName: "Aguascalientes", + code: "MX-AGU", + }, + "MX-BCN": { + countryCode: "MX", + subdivisionName: "Baja California", + code: "MX-BCN", + }, + "MX-BCS": { + countryCode: "MX", + subdivisionName: "Baja California Sur", + code: "MX-BCS", + }, + "MX-CAM": { countryCode: "MX", subdivisionName: "Campeche", code: "MX-CAM" }, + "MX-CHP": { countryCode: "MX", subdivisionName: "Chiapas", code: "MX-CHP" }, + "MX-CHH": { countryCode: "MX", subdivisionName: "Chihuahua", code: "MX-CHH" }, + "MX-CMX": { + countryCode: "MX", + subdivisionName: "Ciudad de Mexico", + code: "MX-CMX", + }, + "MX-COA": { + countryCode: "MX", + subdivisionName: "Coahuila de Zaragoza", + code: "MX-COA", + }, + "MX-COL": { countryCode: "MX", subdivisionName: "Colima", code: "MX-COL" }, + "MX-DUR": { countryCode: "MX", subdivisionName: "Durango", code: "MX-DUR" }, + "MX-GUA": { + countryCode: "MX", + subdivisionName: "Guanajuato", + code: "MX-GUA", + }, + "MX-GRO": { countryCode: "MX", subdivisionName: "Guerrero", code: "MX-GRO" }, + "MX-HID": { countryCode: "MX", subdivisionName: "Hidalgo", code: "MX-HID" }, + "MX-JAL": { countryCode: "MX", subdivisionName: "Jalisco", code: "MX-JAL" }, + "MX-MEX": { countryCode: "MX", subdivisionName: "Mexico", code: "MX-MEX" }, + "MX-MIC": { + countryCode: "MX", + subdivisionName: "Michoacan de Ocampo", + code: "MX-MIC", + }, + "MX-MOR": { countryCode: "MX", subdivisionName: "Morelos", code: "MX-MOR" }, + "MX-NAY": { countryCode: "MX", subdivisionName: "Nayarit", code: "MX-NAY" }, + "MX-NLE": { + countryCode: "MX", + subdivisionName: "Nuevo Leon", + code: "MX-NLE", + }, + "MX-OAX": { countryCode: "MX", subdivisionName: "Oaxaca", code: "MX-OAX" }, + "MX-PUE": { countryCode: "MX", subdivisionName: "Puebla", code: "MX-PUE" }, + "MX-QUE": { countryCode: "MX", subdivisionName: "Queretaro", code: "MX-QUE" }, + "MX-ROO": { + countryCode: "MX", + subdivisionName: "Quintana Roo", + code: "MX-ROO", + }, + "MX-SLP": { + countryCode: "MX", + subdivisionName: "San Luis Potosi", + code: "MX-SLP", + }, + "MX-SIN": { countryCode: "MX", subdivisionName: "Sinaloa", code: "MX-SIN" }, + "MX-SON": { countryCode: "MX", subdivisionName: "Sonora", code: "MX-SON" }, + "MX-TAB": { countryCode: "MX", subdivisionName: "Tabasco", code: "MX-TAB" }, + "MX-TAM": { + countryCode: "MX", + subdivisionName: "Tamaulipas", + code: "MX-TAM", + }, + "MX-TLA": { countryCode: "MX", subdivisionName: "Tlaxcala", code: "MX-TLA" }, + "MX-VER": { + countryCode: "MX", + subdivisionName: "Veracruz de Ignacio de la Llave", + code: "MX-VER", + }, + "MX-YUC": { countryCode: "MX", subdivisionName: "Yucatan", code: "MX-YUC" }, + "MX-ZAC": { countryCode: "MX", subdivisionName: "Zacatecas", code: "MX-ZAC" }, + "MY-01": { countryCode: "MY", subdivisionName: "Johor", code: "MY-01" }, + "MY-02": { countryCode: "MY", subdivisionName: "Kedah", code: "MY-02" }, + "MY-03": { countryCode: "MY", subdivisionName: "Kelantan", code: "MY-03" }, + "MY-04": { countryCode: "MY", subdivisionName: "Melaka", code: "MY-04" }, + "MY-05": { + countryCode: "MY", + subdivisionName: "Negeri Sembilan", + code: "MY-05", + }, + "MY-06": { countryCode: "MY", subdivisionName: "Pahang", code: "MY-06" }, + "MY-08": { countryCode: "MY", subdivisionName: "Perak", code: "MY-08" }, + "MY-09": { countryCode: "MY", subdivisionName: "Perlis", code: "MY-09" }, + "MY-07": { + countryCode: "MY", + subdivisionName: "Pulau Pinang", + code: "MY-07", + }, + "MY-12": { countryCode: "MY", subdivisionName: "Sabah", code: "MY-12" }, + "MY-13": { countryCode: "MY", subdivisionName: "Sarawak", code: "MY-13" }, + "MY-10": { countryCode: "MY", subdivisionName: "Selangor", code: "MY-10" }, + "MY-11": { countryCode: "MY", subdivisionName: "Terengganu", code: "MY-11" }, + "MY-14": { + countryCode: "MY", + subdivisionName: "Wilayah Persekutuan Kuala Lumpur", + code: "MY-14", + }, + "MY-15": { + countryCode: "MY", + subdivisionName: "Wilayah Persekutuan Labuan", + code: "MY-15", + }, + "MY-16": { + countryCode: "MY", + subdivisionName: "Wilayah Persekutuan Putrajaya", + code: "MY-16", + }, + "MZ-P": { countryCode: "MZ", subdivisionName: "Cabo Delgado", code: "MZ-P" }, + "MZ-G": { countryCode: "MZ", subdivisionName: "Gaza", code: "MZ-G" }, + "MZ-I": { countryCode: "MZ", subdivisionName: "Inhambane", code: "MZ-I" }, + "MZ-B": { countryCode: "MZ", subdivisionName: "Manica", code: "MZ-B" }, + "MZ-L": { countryCode: "MZ", subdivisionName: "Maputo", code: "MZ-L" }, + "MZ-N": { countryCode: "MZ", subdivisionName: "Nampula", code: "MZ-N" }, + "MZ-A": { countryCode: "MZ", subdivisionName: "Niassa", code: "MZ-A" }, + "MZ-S": { countryCode: "MZ", subdivisionName: "Sofala", code: "MZ-S" }, + "MZ-T": { countryCode: "MZ", subdivisionName: "Tete", code: "MZ-T" }, + "MZ-Q": { countryCode: "MZ", subdivisionName: "Zambezia", code: "MZ-Q" }, + "NA-ER": { countryCode: "NA", subdivisionName: "Erongo", code: "NA-ER" }, + "NA-HA": { countryCode: "NA", subdivisionName: "Hardap", code: "NA-HA" }, + "NA-KA": { countryCode: "NA", subdivisionName: "Karas", code: "NA-KA" }, + "NA-KE": { + countryCode: "NA", + subdivisionName: "Kavango East", + code: "NA-KE", + }, + "NA-KW": { + countryCode: "NA", + subdivisionName: "Kavango West", + code: "NA-KW", + }, + "NA-KH": { countryCode: "NA", subdivisionName: "Khomas", code: "NA-KH" }, + "NA-KU": { countryCode: "NA", subdivisionName: "Kunene", code: "NA-KU" }, + "NA-OW": { countryCode: "NA", subdivisionName: "Ohangwena", code: "NA-OW" }, + "NA-OH": { countryCode: "NA", subdivisionName: "Omaheke", code: "NA-OH" }, + "NA-OS": { countryCode: "NA", subdivisionName: "Omusati", code: "NA-OS" }, + "NA-ON": { countryCode: "NA", subdivisionName: "Oshana", code: "NA-ON" }, + "NA-OT": { countryCode: "NA", subdivisionName: "Oshikoto", code: "NA-OT" }, + "NA-OD": { + countryCode: "NA", + subdivisionName: "Otjozondjupa", + code: "NA-OD", + }, + "NA-CA": { countryCode: "NA", subdivisionName: "Zambezi", code: "NA-CA" }, + "NE-1": { countryCode: "NE", subdivisionName: "Agadez", code: "NE-1" }, + "NE-2": { countryCode: "NE", subdivisionName: "Diffa", code: "NE-2" }, + "NE-3": { countryCode: "NE", subdivisionName: "Dosso", code: "NE-3" }, + "NE-4": { countryCode: "NE", subdivisionName: "Maradi", code: "NE-4" }, + "NE-8": { countryCode: "NE", subdivisionName: "Niamey", code: "NE-8" }, + "NE-5": { countryCode: "NE", subdivisionName: "Tahoua", code: "NE-5" }, + "NE-6": { countryCode: "NE", subdivisionName: "Tillaberi", code: "NE-6" }, + "NE-7": { countryCode: "NE", subdivisionName: "Zinder", code: "NE-7" }, + "NG-AB": { countryCode: "NG", subdivisionName: "Abia", code: "NG-AB" }, + "NG-FC": { + countryCode: "NG", + subdivisionName: "Abuja Federal Capital Territory", + code: "NG-FC", + }, + "NG-AD": { countryCode: "NG", subdivisionName: "Adamawa", code: "NG-AD" }, + "NG-AK": { countryCode: "NG", subdivisionName: "Akwa Ibom", code: "NG-AK" }, + "NG-AN": { countryCode: "NG", subdivisionName: "Anambra", code: "NG-AN" }, + "NG-BA": { countryCode: "NG", subdivisionName: "Bauchi", code: "NG-BA" }, + "NG-BY": { countryCode: "NG", subdivisionName: "Bayelsa", code: "NG-BY" }, + "NG-BE": { countryCode: "NG", subdivisionName: "Benue", code: "NG-BE" }, + "NG-BO": { countryCode: "NG", subdivisionName: "Borno", code: "NG-BO" }, + "NG-CR": { countryCode: "NG", subdivisionName: "Cross River", code: "NG-CR" }, + "NG-DE": { countryCode: "NG", subdivisionName: "Delta", code: "NG-DE" }, + "NG-EB": { countryCode: "NG", subdivisionName: "Ebonyi", code: "NG-EB" }, + "NG-ED": { countryCode: "NG", subdivisionName: "Edo", code: "NG-ED" }, + "NG-EK": { countryCode: "NG", subdivisionName: "Ekiti", code: "NG-EK" }, + "NG-EN": { countryCode: "NG", subdivisionName: "Enugu", code: "NG-EN" }, + "NG-GO": { countryCode: "NG", subdivisionName: "Gombe", code: "NG-GO" }, + "NG-IM": { countryCode: "NG", subdivisionName: "Imo", code: "NG-IM" }, + "NG-JI": { countryCode: "NG", subdivisionName: "Jigawa", code: "NG-JI" }, + "NG-KD": { countryCode: "NG", subdivisionName: "Kaduna", code: "NG-KD" }, + "NG-KN": { countryCode: "NG", subdivisionName: "Kano", code: "NG-KN" }, + "NG-KT": { countryCode: "NG", subdivisionName: "Katsina", code: "NG-KT" }, + "NG-KE": { countryCode: "NG", subdivisionName: "Kebbi", code: "NG-KE" }, + "NG-KO": { countryCode: "NG", subdivisionName: "Kogi", code: "NG-KO" }, + "NG-KW": { countryCode: "NG", subdivisionName: "Kwara", code: "NG-KW" }, + "NG-LA": { countryCode: "NG", subdivisionName: "Lagos", code: "NG-LA" }, + "NG-NA": { countryCode: "NG", subdivisionName: "Nasarawa", code: "NG-NA" }, + "NG-NI": { countryCode: "NG", subdivisionName: "Niger", code: "NG-NI" }, + "NG-OG": { countryCode: "NG", subdivisionName: "Ogun", code: "NG-OG" }, + "NG-ON": { countryCode: "NG", subdivisionName: "Ondo", code: "NG-ON" }, + "NG-OS": { countryCode: "NG", subdivisionName: "Osun", code: "NG-OS" }, + "NG-OY": { countryCode: "NG", subdivisionName: "Oyo", code: "NG-OY" }, + "NG-PL": { countryCode: "NG", subdivisionName: "Plateau", code: "NG-PL" }, + "NG-RI": { countryCode: "NG", subdivisionName: "Rivers", code: "NG-RI" }, + "NG-SO": { countryCode: "NG", subdivisionName: "Sokoto", code: "NG-SO" }, + "NG-TA": { countryCode: "NG", subdivisionName: "Taraba", code: "NG-TA" }, + "NG-YO": { countryCode: "NG", subdivisionName: "Yobe", code: "NG-YO" }, + "NG-ZA": { countryCode: "NG", subdivisionName: "Zamfara", code: "NG-ZA" }, + "NI-BO": { countryCode: "NI", subdivisionName: "Boaco", code: "NI-BO" }, + "NI-CA": { countryCode: "NI", subdivisionName: "Carazo", code: "NI-CA" }, + "NI-CI": { countryCode: "NI", subdivisionName: "Chinandega", code: "NI-CI" }, + "NI-CO": { countryCode: "NI", subdivisionName: "Chontales", code: "NI-CO" }, + "NI-AN": { + countryCode: "NI", + subdivisionName: "Costa Caribe Norte", + code: "NI-AN", + }, + "NI-AS": { + countryCode: "NI", + subdivisionName: "Costa Caribe Sur", + code: "NI-AS", + }, + "NI-ES": { countryCode: "NI", subdivisionName: "Esteli", code: "NI-ES" }, + "NI-GR": { countryCode: "NI", subdivisionName: "Granada", code: "NI-GR" }, + "NI-JI": { countryCode: "NI", subdivisionName: "Jinotega", code: "NI-JI" }, + "NI-LE": { countryCode: "NI", subdivisionName: "Leon", code: "NI-LE" }, + "NI-MD": { countryCode: "NI", subdivisionName: "Madriz", code: "NI-MD" }, + "NI-MN": { countryCode: "NI", subdivisionName: "Managua", code: "NI-MN" }, + "NI-MS": { countryCode: "NI", subdivisionName: "Masaya", code: "NI-MS" }, + "NI-MT": { countryCode: "NI", subdivisionName: "Matagalpa", code: "NI-MT" }, + "NI-NS": { + countryCode: "NI", + subdivisionName: "Nueva Segovia", + code: "NI-NS", + }, + "NI-SJ": { + countryCode: "NI", + subdivisionName: "Rio San Juan", + code: "NI-SJ", + }, + "NI-RI": { countryCode: "NI", subdivisionName: "Rivas", code: "NI-RI" }, + "NL-DR": { countryCode: "NL", subdivisionName: "Drenthe", code: "NL-DR" }, + "NL-FL": { countryCode: "NL", subdivisionName: "Flevoland", code: "NL-FL" }, + "NL-FR": { countryCode: "NL", subdivisionName: "Fryslan", code: "NL-FR" }, + "NL-GE": { countryCode: "NL", subdivisionName: "Gelderland", code: "NL-GE" }, + "NL-GR": { countryCode: "NL", subdivisionName: "Groningen", code: "NL-GR" }, + "NL-LI": { countryCode: "NL", subdivisionName: "Limburg", code: "NL-LI" }, + "NL-NB": { + countryCode: "NL", + subdivisionName: "Noord-Brabant", + code: "NL-NB", + }, + "NL-NH": { + countryCode: "NL", + subdivisionName: "Noord-Holland", + code: "NL-NH", + }, + "NL-OV": { countryCode: "NL", subdivisionName: "Overijssel", code: "NL-OV" }, + "NL-UT": { countryCode: "NL", subdivisionName: "Utrecht", code: "NL-UT" }, + "NL-ZE": { countryCode: "NL", subdivisionName: "Zeeland", code: "NL-ZE" }, + "NL-ZH": { + countryCode: "NL", + subdivisionName: "Zuid-Holland", + code: "NL-ZH", + }, + "NO-42": { countryCode: "NO", subdivisionName: "Agder", code: "NO-42" }, + "NO-34": { countryCode: "NO", subdivisionName: "Innlandet", code: "NO-34" }, + "NO-15": { + countryCode: "NO", + subdivisionName: "More og Romsdal", + code: "NO-15", + }, + "NO-18": { countryCode: "NO", subdivisionName: "Nordland", code: "NO-18" }, + "NO-03": { countryCode: "NO", subdivisionName: "Oslo", code: "NO-03" }, + "NO-11": { countryCode: "NO", subdivisionName: "Rogaland", code: "NO-11" }, + "NO-54": { + countryCode: "NO", + subdivisionName: "Troms og Finnmark", + code: "NO-54", + }, + "NO-50": { countryCode: "NO", subdivisionName: "Trondelag", code: "NO-50" }, + "NO-38": { + countryCode: "NO", + subdivisionName: "Vestfold og Telemark", + code: "NO-38", + }, + "NO-46": { countryCode: "NO", subdivisionName: "Vestland", code: "NO-46" }, + "NO-30": { countryCode: "NO", subdivisionName: "Viken", code: "NO-30" }, + "NP-BA": { countryCode: "NP", subdivisionName: "Bagmati", code: "NP-BA" }, + "NP-BH": { countryCode: "NP", subdivisionName: "Bheri", code: "NP-BH" }, + "NP-DH": { countryCode: "NP", subdivisionName: "Dhawalagiri", code: "NP-DH" }, + "NP-GA": { countryCode: "NP", subdivisionName: "Gandaki", code: "NP-GA" }, + "NP-JA": { countryCode: "NP", subdivisionName: "Janakpur", code: "NP-JA" }, + "NP-KA": { countryCode: "NP", subdivisionName: "Karnali", code: "NP-KA" }, + "NP-KO": { countryCode: "NP", subdivisionName: "Kosi", code: "NP-KO" }, + "NP-LU": { countryCode: "NP", subdivisionName: "Lumbini", code: "NP-LU" }, + "NP-MA": { countryCode: "NP", subdivisionName: "Mahakali", code: "NP-MA" }, + "NP-ME": { countryCode: "NP", subdivisionName: "Mechi", code: "NP-ME" }, + "NP-NA": { countryCode: "NP", subdivisionName: "Narayani", code: "NP-NA" }, + "NP-RA": { countryCode: "NP", subdivisionName: "Rapti", code: "NP-RA" }, + "NP-SA": { countryCode: "NP", subdivisionName: "Sagarmatha", code: "NP-SA" }, + "NP-SE": { countryCode: "NP", subdivisionName: "Seti", code: "NP-SE" }, + "NR-01": { countryCode: "NR", subdivisionName: "Aiwo", code: "NR-01" }, + "NR-03": { countryCode: "NR", subdivisionName: "Anetan", code: "NR-03" }, + "NR-14": { countryCode: "NR", subdivisionName: "Yaren", code: "NR-14" }, + "NZ-AUK": { countryCode: "NZ", subdivisionName: "Auckland", code: "NZ-AUK" }, + "NZ-BOP": { + countryCode: "NZ", + subdivisionName: "Bay of Plenty", + code: "NZ-BOP", + }, + "NZ-CAN": { + countryCode: "NZ", + subdivisionName: "Canterbury", + code: "NZ-CAN", + }, + "NZ-CIT": { + countryCode: "NZ", + subdivisionName: "Chatham Islands Territory", + code: "NZ-CIT", + }, + "NZ-GIS": { countryCode: "NZ", subdivisionName: "Gisborne", code: "NZ-GIS" }, + "NZ-HKB": { + countryCode: "NZ", + subdivisionName: "Hawke's Bay", + code: "NZ-HKB", + }, + "NZ-MWT": { + countryCode: "NZ", + subdivisionName: "Manawatu-Wanganui", + code: "NZ-MWT", + }, + "NZ-MBH": { + countryCode: "NZ", + subdivisionName: "Marlborough", + code: "NZ-MBH", + }, + "NZ-NSN": { countryCode: "NZ", subdivisionName: "Nelson", code: "NZ-NSN" }, + "NZ-NTL": { countryCode: "NZ", subdivisionName: "Northland", code: "NZ-NTL" }, + "NZ-OTA": { countryCode: "NZ", subdivisionName: "Otago", code: "NZ-OTA" }, + "NZ-STL": { countryCode: "NZ", subdivisionName: "Southland", code: "NZ-STL" }, + "NZ-TKI": { countryCode: "NZ", subdivisionName: "Taranaki", code: "NZ-TKI" }, + "NZ-TAS": { countryCode: "NZ", subdivisionName: "Tasman", code: "NZ-TAS" }, + "NZ-WKO": { countryCode: "NZ", subdivisionName: "Waikato", code: "NZ-WKO" }, + "NZ-WGN": { + countryCode: "NZ", + subdivisionName: "Wellington", + code: "NZ-WGN", + }, + "NZ-WTC": { + countryCode: "NZ", + subdivisionName: "West Coast", + code: "NZ-WTC", + }, + "OM-DA": { + countryCode: "OM", + subdivisionName: "Ad Dakhiliyah", + code: "OM-DA", + }, + "OM-BU": { countryCode: "OM", subdivisionName: "Al Buraymi", code: "OM-BU" }, + "OM-WU": { countryCode: "OM", subdivisionName: "Al Wusta", code: "OM-WU" }, + "OM-ZA": { countryCode: "OM", subdivisionName: "Az Zahirah", code: "OM-ZA" }, + "OM-BJ": { + countryCode: "OM", + subdivisionName: "Janub al Batinah", + code: "OM-BJ", + }, + "OM-SJ": { + countryCode: "OM", + subdivisionName: "Janub ash Sharqiyah", + code: "OM-SJ", + }, + "OM-MA": { countryCode: "OM", subdivisionName: "Masqat", code: "OM-MA" }, + "OM-MU": { countryCode: "OM", subdivisionName: "Musandam", code: "OM-MU" }, + "OM-BS": { + countryCode: "OM", + subdivisionName: "Shamal al Batinah", + code: "OM-BS", + }, + "OM-SS": { + countryCode: "OM", + subdivisionName: "Shamal ash Sharqiyah", + code: "OM-SS", + }, + "OM-ZU": { countryCode: "OM", subdivisionName: "Zufar", code: "OM-ZU" }, + "PA-1": { + countryCode: "PA", + subdivisionName: "Bocas del Toro", + code: "PA-1", + }, + "PA-4": { countryCode: "PA", subdivisionName: "Chiriqui", code: "PA-4" }, + "PA-2": { countryCode: "PA", subdivisionName: "Cocle", code: "PA-2" }, + "PA-3": { countryCode: "PA", subdivisionName: "Colon", code: "PA-3" }, + "PA-5": { countryCode: "PA", subdivisionName: "Darien", code: "PA-5" }, + "PA-6": { countryCode: "PA", subdivisionName: "Herrera", code: "PA-6" }, + "PA-7": { countryCode: "PA", subdivisionName: "Los Santos", code: "PA-7" }, + "PA-NB": { countryCode: "PA", subdivisionName: "Ngobe-Bugle", code: "PA-NB" }, + "PA-8": { countryCode: "PA", subdivisionName: "Panama", code: "PA-8" }, + "PA-9": { countryCode: "PA", subdivisionName: "Veraguas", code: "PA-9" }, + "PE-AMA": { countryCode: "PE", subdivisionName: "Amazonas", code: "PE-AMA" }, + "PE-ANC": { countryCode: "PE", subdivisionName: "Ancash", code: "PE-ANC" }, + "PE-APU": { countryCode: "PE", subdivisionName: "Apurimac", code: "PE-APU" }, + "PE-ARE": { countryCode: "PE", subdivisionName: "Arequipa", code: "PE-ARE" }, + "PE-AYA": { countryCode: "PE", subdivisionName: "Ayacucho", code: "PE-AYA" }, + "PE-CAJ": { countryCode: "PE", subdivisionName: "Cajamarca", code: "PE-CAJ" }, + "PE-CUS": { countryCode: "PE", subdivisionName: "Cusco", code: "PE-CUS" }, + "PE-CAL": { countryCode: "PE", subdivisionName: "El Callao", code: "PE-CAL" }, + "PE-HUV": { + countryCode: "PE", + subdivisionName: "Huancavelica", + code: "PE-HUV", + }, + "PE-HUC": { countryCode: "PE", subdivisionName: "Huanuco", code: "PE-HUC" }, + "PE-ICA": { countryCode: "PE", subdivisionName: "Ica", code: "PE-ICA" }, + "PE-JUN": { countryCode: "PE", subdivisionName: "Junin", code: "PE-JUN" }, + "PE-LAL": { + countryCode: "PE", + subdivisionName: "La Libertad", + code: "PE-LAL", + }, + "PE-LAM": { + countryCode: "PE", + subdivisionName: "Lambayeque", + code: "PE-LAM", + }, + "PE-LIM": { countryCode: "PE", subdivisionName: "Lima", code: "PE-LIM" }, + "PE-LOR": { countryCode: "PE", subdivisionName: "Loreto", code: "PE-LOR" }, + "PE-MDD": { + countryCode: "PE", + subdivisionName: "Madre de Dios", + code: "PE-MDD", + }, + "PE-MOQ": { countryCode: "PE", subdivisionName: "Moquegua", code: "PE-MOQ" }, + "PE-PAS": { countryCode: "PE", subdivisionName: "Pasco", code: "PE-PAS" }, + "PE-PIU": { countryCode: "PE", subdivisionName: "Piura", code: "PE-PIU" }, + "PE-PUN": { countryCode: "PE", subdivisionName: "Puno", code: "PE-PUN" }, + "PE-SAM": { + countryCode: "PE", + subdivisionName: "San Martin", + code: "PE-SAM", + }, + "PE-TAC": { countryCode: "PE", subdivisionName: "Tacna", code: "PE-TAC" }, + "PE-TUM": { countryCode: "PE", subdivisionName: "Tumbes", code: "PE-TUM" }, + "PE-UCA": { countryCode: "PE", subdivisionName: "Ucayali", code: "PE-UCA" }, + "PG-NSB": { + countryCode: "PG", + subdivisionName: "Bougainville", + code: "PG-NSB", + }, + "PG-CPM": { countryCode: "PG", subdivisionName: "Central", code: "PG-CPM" }, + "PG-CPK": { countryCode: "PG", subdivisionName: "Chimbu", code: "PG-CPK" }, + "PG-EBR": { + countryCode: "PG", + subdivisionName: "East New Britain", + code: "PG-EBR", + }, + "PG-ESW": { + countryCode: "PG", + subdivisionName: "East Sepik", + code: "PG-ESW", + }, + "PG-EHG": { + countryCode: "PG", + subdivisionName: "Eastern Highlands", + code: "PG-EHG", + }, + "PG-MPM": { countryCode: "PG", subdivisionName: "Madang", code: "PG-MPM" }, + "PG-MRL": { countryCode: "PG", subdivisionName: "Manus", code: "PG-MRL" }, + "PG-MBA": { countryCode: "PG", subdivisionName: "Milne Bay", code: "PG-MBA" }, + "PG-MPL": { countryCode: "PG", subdivisionName: "Morobe", code: "PG-MPL" }, + "PG-NCD": { + countryCode: "PG", + subdivisionName: "National Capital District (Port Moresby)", + code: "PG-NCD", + }, + "PG-NIK": { + countryCode: "PG", + subdivisionName: "New Ireland", + code: "PG-NIK", + }, + "PG-SHM": { + countryCode: "PG", + subdivisionName: "Southern Highlands", + code: "PG-SHM", + }, + "PG-WBK": { + countryCode: "PG", + subdivisionName: "West New Britain", + code: "PG-WBK", + }, + "PG-SAN": { + countryCode: "PG", + subdivisionName: "West Sepik", + code: "PG-SAN", + }, + "PG-WPD": { countryCode: "PG", subdivisionName: "Western", code: "PG-WPD" }, + "PG-WHM": { + countryCode: "PG", + subdivisionName: "Western Highlands", + code: "PG-WHM", + }, + "PH-ABR": { countryCode: "PH", subdivisionName: "Abra", code: "PH-ABR" }, + "PH-AGN": { + countryCode: "PH", + subdivisionName: "Agusan del Norte", + code: "PH-AGN", + }, + "PH-AGS": { + countryCode: "PH", + subdivisionName: "Agusan del Sur", + code: "PH-AGS", + }, + "PH-AKL": { countryCode: "PH", subdivisionName: "Aklan", code: "PH-AKL" }, + "PH-ALB": { countryCode: "PH", subdivisionName: "Albay", code: "PH-ALB" }, + "PH-ANT": { countryCode: "PH", subdivisionName: "Antique", code: "PH-ANT" }, + "PH-APA": { countryCode: "PH", subdivisionName: "Apayao", code: "PH-APA" }, + "PH-AUR": { countryCode: "PH", subdivisionName: "Aurora", code: "PH-AUR" }, + "PH-BAS": { countryCode: "PH", subdivisionName: "Basilan", code: "PH-BAS" }, + "PH-BAN": { countryCode: "PH", subdivisionName: "Bataan", code: "PH-BAN" }, + "PH-BTN": { countryCode: "PH", subdivisionName: "Batanes", code: "PH-BTN" }, + "PH-BTG": { countryCode: "PH", subdivisionName: "Batangas", code: "PH-BTG" }, + "PH-BEN": { countryCode: "PH", subdivisionName: "Benguet", code: "PH-BEN" }, + "PH-BIL": { countryCode: "PH", subdivisionName: "Biliran", code: "PH-BIL" }, + "PH-BOH": { countryCode: "PH", subdivisionName: "Bohol", code: "PH-BOH" }, + "PH-BUK": { countryCode: "PH", subdivisionName: "Bukidnon", code: "PH-BUK" }, + "PH-BUL": { countryCode: "PH", subdivisionName: "Bulacan", code: "PH-BUL" }, + "PH-CAG": { countryCode: "PH", subdivisionName: "Cagayan", code: "PH-CAG" }, + "PH-CAN": { + countryCode: "PH", + subdivisionName: "Camarines Norte", + code: "PH-CAN", + }, + "PH-CAS": { + countryCode: "PH", + subdivisionName: "Camarines Sur", + code: "PH-CAS", + }, + "PH-CAM": { countryCode: "PH", subdivisionName: "Camiguin", code: "PH-CAM" }, + "PH-CAP": { countryCode: "PH", subdivisionName: "Capiz", code: "PH-CAP" }, + "PH-CAT": { + countryCode: "PH", + subdivisionName: "Catanduanes", + code: "PH-CAT", + }, + "PH-CAV": { countryCode: "PH", subdivisionName: "Cavite", code: "PH-CAV" }, + "PH-CEB": { countryCode: "PH", subdivisionName: "Cebu", code: "PH-CEB" }, + "PH-NCO": { countryCode: "PH", subdivisionName: "Cotabato", code: "PH-NCO" }, + "PH-DAO": { + countryCode: "PH", + subdivisionName: "Davao Oriental", + code: "PH-DAO", + }, + "PH-COM": { + countryCode: "PH", + subdivisionName: "Davao de Oro", + code: "PH-COM", + }, + "PH-DAV": { + countryCode: "PH", + subdivisionName: "Davao del Norte", + code: "PH-DAV", + }, + "PH-DAS": { + countryCode: "PH", + subdivisionName: "Davao del Sur", + code: "PH-DAS", + }, + "PH-DIN": { + countryCode: "PH", + subdivisionName: "Dinagat Islands", + code: "PH-DIN", + }, + "PH-EAS": { + countryCode: "PH", + subdivisionName: "Eastern Samar", + code: "PH-EAS", + }, + "PH-GUI": { countryCode: "PH", subdivisionName: "Guimaras", code: "PH-GUI" }, + "PH-IFU": { countryCode: "PH", subdivisionName: "Ifugao", code: "PH-IFU" }, + "PH-ILN": { + countryCode: "PH", + subdivisionName: "Ilocos Norte", + code: "PH-ILN", + }, + "PH-ILS": { + countryCode: "PH", + subdivisionName: "Ilocos Sur", + code: "PH-ILS", + }, + "PH-ILI": { countryCode: "PH", subdivisionName: "Iloilo", code: "PH-ILI" }, + "PH-ISA": { countryCode: "PH", subdivisionName: "Isabela", code: "PH-ISA" }, + "PH-KAL": { countryCode: "PH", subdivisionName: "Kalinga", code: "PH-KAL" }, + "PH-LUN": { countryCode: "PH", subdivisionName: "La Union", code: "PH-LUN" }, + "PH-LAG": { countryCode: "PH", subdivisionName: "Laguna", code: "PH-LAG" }, + "PH-LAN": { + countryCode: "PH", + subdivisionName: "Lanao del Norte", + code: "PH-LAN", + }, + "PH-LAS": { + countryCode: "PH", + subdivisionName: "Lanao del Sur", + code: "PH-LAS", + }, + "PH-LEY": { countryCode: "PH", subdivisionName: "Leyte", code: "PH-LEY" }, + "PH-MAG": { + countryCode: "PH", + subdivisionName: "Maguindanao", + code: "PH-MAG", + }, + "PH-MAD": { + countryCode: "PH", + subdivisionName: "Marinduque", + code: "PH-MAD", + }, + "PH-MAS": { countryCode: "PH", subdivisionName: "Masbate", code: "PH-MAS" }, + "PH-MDC": { + countryCode: "PH", + subdivisionName: "Mindoro Occidental", + code: "PH-MDC", + }, + "PH-MDR": { + countryCode: "PH", + subdivisionName: "Mindoro Oriental", + code: "PH-MDR", + }, + "PH-MSC": { + countryCode: "PH", + subdivisionName: "Misamis Occidental", + code: "PH-MSC", + }, + "PH-MSR": { + countryCode: "PH", + subdivisionName: "Misamis Oriental", + code: "PH-MSR", + }, + "PH-MOU": { + countryCode: "PH", + subdivisionName: "Mountain Province", + code: "PH-MOU", + }, + "PH-00": { + countryCode: "PH", + subdivisionName: "National Capital Region", + code: "PH-00", + }, + "PH-NEC": { + countryCode: "PH", + subdivisionName: "Negros Occidental", + code: "PH-NEC", + }, + "PH-NER": { + countryCode: "PH", + subdivisionName: "Negros Oriental", + code: "PH-NER", + }, + "PH-NSA": { + countryCode: "PH", + subdivisionName: "Northern Samar", + code: "PH-NSA", + }, + "PH-NUE": { + countryCode: "PH", + subdivisionName: "Nueva Ecija", + code: "PH-NUE", + }, + "PH-NUV": { + countryCode: "PH", + subdivisionName: "Nueva Vizcaya", + code: "PH-NUV", + }, + "PH-PLW": { countryCode: "PH", subdivisionName: "Palawan", code: "PH-PLW" }, + "PH-PAM": { countryCode: "PH", subdivisionName: "Pampanga", code: "PH-PAM" }, + "PH-PAN": { + countryCode: "PH", + subdivisionName: "Pangasinan", + code: "PH-PAN", + }, + "PH-QUE": { countryCode: "PH", subdivisionName: "Quezon", code: "PH-QUE" }, + "PH-QUI": { countryCode: "PH", subdivisionName: "Quirino", code: "PH-QUI" }, + "PH-RIZ": { countryCode: "PH", subdivisionName: "Rizal", code: "PH-RIZ" }, + "PH-ROM": { countryCode: "PH", subdivisionName: "Romblon", code: "PH-ROM" }, + "PH-WSA": { countryCode: "PH", subdivisionName: "Samar", code: "PH-WSA" }, + "PH-SAR": { countryCode: "PH", subdivisionName: "Sarangani", code: "PH-SAR" }, + "PH-SIG": { countryCode: "PH", subdivisionName: "Siquijor", code: "PH-SIG" }, + "PH-SOR": { countryCode: "PH", subdivisionName: "Sorsogon", code: "PH-SOR" }, + "PH-SCO": { + countryCode: "PH", + subdivisionName: "South Cotabato", + code: "PH-SCO", + }, + "PH-SLE": { + countryCode: "PH", + subdivisionName: "Southern Leyte", + code: "PH-SLE", + }, + "PH-SUK": { + countryCode: "PH", + subdivisionName: "Sultan Kudarat", + code: "PH-SUK", + }, + "PH-SLU": { countryCode: "PH", subdivisionName: "Sulu", code: "PH-SLU" }, + "PH-SUN": { + countryCode: "PH", + subdivisionName: "Surigao del Norte", + code: "PH-SUN", + }, + "PH-SUR": { + countryCode: "PH", + subdivisionName: "Surigao del Sur", + code: "PH-SUR", + }, + "PH-TAR": { countryCode: "PH", subdivisionName: "Tarlac", code: "PH-TAR" }, + "PH-TAW": { countryCode: "PH", subdivisionName: "Tawi-Tawi", code: "PH-TAW" }, + "PH-ZMB": { countryCode: "PH", subdivisionName: "Zambales", code: "PH-ZMB" }, + "PH-ZSI": { + countryCode: "PH", + subdivisionName: "Zamboanga Sibugay", + code: "PH-ZSI", + }, + "PH-ZAN": { + countryCode: "PH", + subdivisionName: "Zamboanga del Norte", + code: "PH-ZAN", + }, + "PH-ZAS": { + countryCode: "PH", + subdivisionName: "Zamboanga del Sur", + code: "PH-ZAS", + }, + "PK-JK": { + countryCode: "PK", + subdivisionName: "Azad Jammu and Kashmir", + code: "PK-JK", + }, + "PK-BA": { countryCode: "PK", subdivisionName: "Balochistan", code: "PK-BA" }, + "PK-GB": { + countryCode: "PK", + subdivisionName: "Gilgit-Baltistan", + code: "PK-GB", + }, + "PK-IS": { countryCode: "PK", subdivisionName: "Islamabad", code: "PK-IS" }, + "PK-KP": { + countryCode: "PK", + subdivisionName: "Khyber Pakhtunkhwa", + code: "PK-KP", + }, + "PK-PB": { countryCode: "PK", subdivisionName: "Punjab", code: "PK-PB" }, + "PK-SD": { countryCode: "PK", subdivisionName: "Sindh", code: "PK-SD" }, + "PL-02": { + countryCode: "PL", + subdivisionName: "Dolnoslaskie", + code: "PL-02", + }, + "PL-04": { + countryCode: "PL", + subdivisionName: "Kujawsko-pomorskie", + code: "PL-04", + }, + "PL-10": { countryCode: "PL", subdivisionName: "Lodzkie", code: "PL-10" }, + "PL-06": { countryCode: "PL", subdivisionName: "Lubelskie", code: "PL-06" }, + "PL-08": { countryCode: "PL", subdivisionName: "Lubuskie", code: "PL-08" }, + "PL-12": { countryCode: "PL", subdivisionName: "Malopolskie", code: "PL-12" }, + "PL-14": { countryCode: "PL", subdivisionName: "Mazowieckie", code: "PL-14" }, + "PL-16": { countryCode: "PL", subdivisionName: "Opolskie", code: "PL-16" }, + "PL-18": { + countryCode: "PL", + subdivisionName: "Podkarpackie", + code: "PL-18", + }, + "PL-20": { countryCode: "PL", subdivisionName: "Podlaskie", code: "PL-20" }, + "PL-22": { countryCode: "PL", subdivisionName: "Pomorskie", code: "PL-22" }, + "PL-24": { countryCode: "PL", subdivisionName: "Slaskie", code: "PL-24" }, + "PL-26": { + countryCode: "PL", + subdivisionName: "Swietokrzyskie", + code: "PL-26", + }, + "PL-28": { + countryCode: "PL", + subdivisionName: "Warminsko-mazurskie", + code: "PL-28", + }, + "PL-30": { + countryCode: "PL", + subdivisionName: "Wielkopolskie", + code: "PL-30", + }, + "PL-32": { + countryCode: "PL", + subdivisionName: "Zachodniopomorskie", + code: "PL-32", + }, + "PS-BTH": { countryCode: "PS", subdivisionName: "Bethlehem", code: "PS-BTH" }, + "PS-DEB": { + countryCode: "PS", + subdivisionName: "Deir El Balah", + code: "PS-DEB", + }, + "PS-GZA": { countryCode: "PS", subdivisionName: "Gaza", code: "PS-GZA" }, + "PS-HBN": { countryCode: "PS", subdivisionName: "Hebron", code: "PS-HBN" }, + "PS-JEN": { countryCode: "PS", subdivisionName: "Jenin", code: "PS-JEN" }, + "PS-JRH": { + countryCode: "PS", + subdivisionName: "Jericho and Al Aghwar", + code: "PS-JRH", + }, + "PS-JEM": { countryCode: "PS", subdivisionName: "Jerusalem", code: "PS-JEM" }, + "PS-KYS": { + countryCode: "PS", + subdivisionName: "Khan Yunis", + code: "PS-KYS", + }, + "PS-NBS": { countryCode: "PS", subdivisionName: "Nablus", code: "PS-NBS" }, + "PS-QQA": { countryCode: "PS", subdivisionName: "Qalqilya", code: "PS-QQA" }, + "PS-RFH": { countryCode: "PS", subdivisionName: "Rafah", code: "PS-RFH" }, + "PS-RBH": { countryCode: "PS", subdivisionName: "Ramallah", code: "PS-RBH" }, + "PS-SLT": { countryCode: "PS", subdivisionName: "Salfit", code: "PS-SLT" }, + "PS-TBS": { countryCode: "PS", subdivisionName: "Tubas", code: "PS-TBS" }, + "PS-TKM": { countryCode: "PS", subdivisionName: "Tulkarm", code: "PS-TKM" }, + "PT-01": { countryCode: "PT", subdivisionName: "Aveiro", code: "PT-01" }, + "PT-02": { countryCode: "PT", subdivisionName: "Beja", code: "PT-02" }, + "PT-03": { countryCode: "PT", subdivisionName: "Braga", code: "PT-03" }, + "PT-04": { countryCode: "PT", subdivisionName: "Braganca", code: "PT-04" }, + "PT-05": { + countryCode: "PT", + subdivisionName: "Castelo Branco", + code: "PT-05", + }, + "PT-06": { countryCode: "PT", subdivisionName: "Coimbra", code: "PT-06" }, + "PT-07": { countryCode: "PT", subdivisionName: "Evora", code: "PT-07" }, + "PT-08": { countryCode: "PT", subdivisionName: "Faro", code: "PT-08" }, + "PT-09": { countryCode: "PT", subdivisionName: "Guarda", code: "PT-09" }, + "PT-10": { countryCode: "PT", subdivisionName: "Leiria", code: "PT-10" }, + "PT-11": { countryCode: "PT", subdivisionName: "Lisboa", code: "PT-11" }, + "PT-12": { countryCode: "PT", subdivisionName: "Portalegre", code: "PT-12" }, + "PT-13": { countryCode: "PT", subdivisionName: "Porto", code: "PT-13" }, + "PT-30": { + countryCode: "PT", + subdivisionName: "Regiao Autonoma da Madeira", + code: "PT-30", + }, + "PT-20": { + countryCode: "PT", + subdivisionName: "Regiao Autonoma dos Acores", + code: "PT-20", + }, + "PT-14": { countryCode: "PT", subdivisionName: "Santarem", code: "PT-14" }, + "PT-15": { countryCode: "PT", subdivisionName: "Setubal", code: "PT-15" }, + "PT-16": { + countryCode: "PT", + subdivisionName: "Viana do Castelo", + code: "PT-16", + }, + "PT-17": { countryCode: "PT", subdivisionName: "Vila Real", code: "PT-17" }, + "PT-18": { countryCode: "PT", subdivisionName: "Viseu", code: "PT-18" }, + "PW-004": { countryCode: "PW", subdivisionName: "Airai", code: "PW-004" }, + "PW-150": { countryCode: "PW", subdivisionName: "Koror", code: "PW-150" }, + "PW-212": { countryCode: "PW", subdivisionName: "Melekeok", code: "PW-212" }, + "PY-10": { countryCode: "PY", subdivisionName: "Alto Parana", code: "PY-10" }, + "PY-13": { countryCode: "PY", subdivisionName: "Amambay", code: "PY-13" }, + "PY-ASU": { countryCode: "PY", subdivisionName: "Asuncion", code: "PY-ASU" }, + "PY-19": { countryCode: "PY", subdivisionName: "Boqueron", code: "PY-19" }, + "PY-5": { countryCode: "PY", subdivisionName: "Caaguazu", code: "PY-5" }, + "PY-6": { countryCode: "PY", subdivisionName: "Caazapa", code: "PY-6" }, + "PY-14": { countryCode: "PY", subdivisionName: "Canindeyu", code: "PY-14" }, + "PY-11": { countryCode: "PY", subdivisionName: "Central", code: "PY-11" }, + "PY-1": { countryCode: "PY", subdivisionName: "Concepcion", code: "PY-1" }, + "PY-3": { countryCode: "PY", subdivisionName: "Cordillera", code: "PY-3" }, + "PY-4": { countryCode: "PY", subdivisionName: "Guaira", code: "PY-4" }, + "PY-7": { countryCode: "PY", subdivisionName: "Itapua", code: "PY-7" }, + "PY-8": { countryCode: "PY", subdivisionName: "Misiones", code: "PY-8" }, + "PY-12": { countryCode: "PY", subdivisionName: "Neembucu", code: "PY-12" }, + "PY-9": { countryCode: "PY", subdivisionName: "Paraguari", code: "PY-9" }, + "PY-15": { + countryCode: "PY", + subdivisionName: "Presidente Hayes", + code: "PY-15", + }, + "PY-2": { countryCode: "PY", subdivisionName: "San Pedro", code: "PY-2" }, + "QA-DA": { countryCode: "QA", subdivisionName: "Ad Dawhah", code: "QA-DA" }, + "QA-KH": { + countryCode: "QA", + subdivisionName: "Al Khawr wa adh Dhakhirah", + code: "QA-KH", + }, + "QA-WA": { countryCode: "QA", subdivisionName: "Al Wakrah", code: "QA-WA" }, + "QA-RA": { countryCode: "QA", subdivisionName: "Ar Rayyan", code: "QA-RA" }, + "QA-MS": { countryCode: "QA", subdivisionName: "Ash Shamal", code: "QA-MS" }, + "QA-ZA": { countryCode: "QA", subdivisionName: "Az Za'ayin", code: "QA-ZA" }, + "QA-US": { countryCode: "QA", subdivisionName: "Umm Salal", code: "QA-US" }, + "RO-AB": { countryCode: "RO", subdivisionName: "Alba", code: "RO-AB" }, + "RO-AR": { countryCode: "RO", subdivisionName: "Arad", code: "RO-AR" }, + "RO-AG": { countryCode: "RO", subdivisionName: "Arges", code: "RO-AG" }, + "RO-BC": { countryCode: "RO", subdivisionName: "Bacau", code: "RO-BC" }, + "RO-BH": { countryCode: "RO", subdivisionName: "Bihor", code: "RO-BH" }, + "RO-BN": { + countryCode: "RO", + subdivisionName: "Bistrita-Nasaud", + code: "RO-BN", + }, + "RO-BT": { countryCode: "RO", subdivisionName: "Botosani", code: "RO-BT" }, + "RO-BR": { countryCode: "RO", subdivisionName: "Braila", code: "RO-BR" }, + "RO-BV": { countryCode: "RO", subdivisionName: "Brasov", code: "RO-BV" }, + "RO-B": { countryCode: "RO", subdivisionName: "Bucuresti", code: "RO-B" }, + "RO-BZ": { countryCode: "RO", subdivisionName: "Buzau", code: "RO-BZ" }, + "RO-CL": { countryCode: "RO", subdivisionName: "Calarasi", code: "RO-CL" }, + "RO-CS": { + countryCode: "RO", + subdivisionName: "Caras-Severin", + code: "RO-CS", + }, + "RO-CJ": { countryCode: "RO", subdivisionName: "Cluj", code: "RO-CJ" }, + "RO-CT": { countryCode: "RO", subdivisionName: "Constanta", code: "RO-CT" }, + "RO-CV": { countryCode: "RO", subdivisionName: "Covasna", code: "RO-CV" }, + "RO-DB": { countryCode: "RO", subdivisionName: "Dambovita", code: "RO-DB" }, + "RO-DJ": { countryCode: "RO", subdivisionName: "Dolj", code: "RO-DJ" }, + "RO-GL": { countryCode: "RO", subdivisionName: "Galati", code: "RO-GL" }, + "RO-GR": { countryCode: "RO", subdivisionName: "Giurgiu", code: "RO-GR" }, + "RO-GJ": { countryCode: "RO", subdivisionName: "Gorj", code: "RO-GJ" }, + "RO-HR": { countryCode: "RO", subdivisionName: "Harghita", code: "RO-HR" }, + "RO-HD": { countryCode: "RO", subdivisionName: "Hunedoara", code: "RO-HD" }, + "RO-IL": { countryCode: "RO", subdivisionName: "Ialomita", code: "RO-IL" }, + "RO-IS": { countryCode: "RO", subdivisionName: "Iasi", code: "RO-IS" }, + "RO-IF": { countryCode: "RO", subdivisionName: "Ilfov", code: "RO-IF" }, + "RO-MM": { countryCode: "RO", subdivisionName: "Maramures", code: "RO-MM" }, + "RO-MH": { countryCode: "RO", subdivisionName: "Mehedinti", code: "RO-MH" }, + "RO-MS": { countryCode: "RO", subdivisionName: "Mures", code: "RO-MS" }, + "RO-NT": { countryCode: "RO", subdivisionName: "Neamt", code: "RO-NT" }, + "RO-OT": { countryCode: "RO", subdivisionName: "Olt", code: "RO-OT" }, + "RO-PH": { countryCode: "RO", subdivisionName: "Prahova", code: "RO-PH" }, + "RO-SJ": { countryCode: "RO", subdivisionName: "Salaj", code: "RO-SJ" }, + "RO-SM": { countryCode: "RO", subdivisionName: "Satu Mare", code: "RO-SM" }, + "RO-SB": { countryCode: "RO", subdivisionName: "Sibiu", code: "RO-SB" }, + "RO-SV": { countryCode: "RO", subdivisionName: "Suceava", code: "RO-SV" }, + "RO-TR": { countryCode: "RO", subdivisionName: "Teleorman", code: "RO-TR" }, + "RO-TM": { countryCode: "RO", subdivisionName: "Timis", code: "RO-TM" }, + "RO-TL": { countryCode: "RO", subdivisionName: "Tulcea", code: "RO-TL" }, + "RO-VL": { countryCode: "RO", subdivisionName: "Valcea", code: "RO-VL" }, + "RO-VS": { countryCode: "RO", subdivisionName: "Vaslui", code: "RO-VS" }, + "RO-VN": { countryCode: "RO", subdivisionName: "Vrancea", code: "RO-VN" }, + "RS-00": { countryCode: "RS", subdivisionName: "Beograd", code: "RS-00" }, + "RS-14": { + countryCode: "RS", + subdivisionName: "Borski okrug", + code: "RS-14", + }, + "RS-11": { + countryCode: "RS", + subdivisionName: "Branicevski okrug", + code: "RS-11", + }, + "RS-23": { + countryCode: "RS", + subdivisionName: "Jablanicki okrug", + code: "RS-23", + }, + "RS-06": { + countryCode: "RS", + subdivisionName: "Juznobacki okrug", + code: "RS-06", + }, + "RS-04": { + countryCode: "RS", + subdivisionName: "Juznobanatski okrug", + code: "RS-04", + }, + "RS-09": { + countryCode: "RS", + subdivisionName: "Kolubarski okrug", + code: "RS-09", + }, + "RS-28": { + countryCode: "RS", + subdivisionName: "Kosovsko-Mitrovacki okrug", + code: "RS-28", + }, + "RS-08": { + countryCode: "RS", + subdivisionName: "Macvanski okrug", + code: "RS-08", + }, + "RS-17": { + countryCode: "RS", + subdivisionName: "Moravicki okrug", + code: "RS-17", + }, + "RS-20": { + countryCode: "RS", + subdivisionName: "Nisavski okrug", + code: "RS-20", + }, + "RS-24": { + countryCode: "RS", + subdivisionName: "Pcinjski okrug", + code: "RS-24", + }, + "RS-26": { countryCode: "RS", subdivisionName: "Pecki okrug", code: "RS-26" }, + "RS-22": { + countryCode: "RS", + subdivisionName: "Pirotski okrug", + code: "RS-22", + }, + "RS-10": { + countryCode: "RS", + subdivisionName: "Podunavski okrug", + code: "RS-10", + }, + "RS-13": { + countryCode: "RS", + subdivisionName: "Pomoravski okrug", + code: "RS-13", + }, + "RS-27": { + countryCode: "RS", + subdivisionName: "Prizrenski okrug", + code: "RS-27", + }, + "RS-19": { + countryCode: "RS", + subdivisionName: "Rasinski okrug", + code: "RS-19", + }, + "RS-18": { countryCode: "RS", subdivisionName: "Raski okrug", code: "RS-18" }, + "RS-01": { + countryCode: "RS", + subdivisionName: "Severnobacki okrug", + code: "RS-01", + }, + "RS-03": { + countryCode: "RS", + subdivisionName: "Severnobanatski okrug", + code: "RS-03", + }, + "RS-02": { + countryCode: "RS", + subdivisionName: "Srednjebanatski okrug", + code: "RS-02", + }, + "RS-07": { + countryCode: "RS", + subdivisionName: "Sremski okrug", + code: "RS-07", + }, + "RS-12": { + countryCode: "RS", + subdivisionName: "Sumadijski okrug", + code: "RS-12", + }, + "RS-21": { + countryCode: "RS", + subdivisionName: "Toplicki okrug", + code: "RS-21", + }, + "RS-15": { + countryCode: "RS", + subdivisionName: "Zajecarski okrug", + code: "RS-15", + }, + "RS-05": { + countryCode: "RS", + subdivisionName: "Zapadnobacki okrug", + code: "RS-05", + }, + "RS-16": { + countryCode: "RS", + subdivisionName: "Zlatiborski okrug", + code: "RS-16", + }, + "RU-AD": { + countryCode: "RU", + subdivisionName: "Adygeya, Respublika", + code: "RU-AD", + }, + "RU-AL": { + countryCode: "RU", + subdivisionName: "Altay, Respublika", + code: "RU-AL", + }, + "RU-ALT": { + countryCode: "RU", + subdivisionName: "Altayskiy kray", + code: "RU-ALT", + }, + "RU-AMU": { + countryCode: "RU", + subdivisionName: "Amurskaya oblast'", + code: "RU-AMU", + }, + "RU-ARK": { + countryCode: "RU", + subdivisionName: "Arkhangel'skaya oblast'", + code: "RU-ARK", + }, + "RU-AST": { + countryCode: "RU", + subdivisionName: "Astrakhanskaya oblast'", + code: "RU-AST", + }, + "RU-BA": { + countryCode: "RU", + subdivisionName: "Bashkortostan, Respublika", + code: "RU-BA", + }, + "RU-BEL": { + countryCode: "RU", + subdivisionName: "Belgorodskaya oblast'", + code: "RU-BEL", + }, + "RU-BRY": { + countryCode: "RU", + subdivisionName: "Bryanskaya oblast'", + code: "RU-BRY", + }, + "RU-BU": { + countryCode: "RU", + subdivisionName: "Buryatiya, Respublika", + code: "RU-BU", + }, + "RU-CE": { + countryCode: "RU", + subdivisionName: "Chechenskaya Respublika", + code: "RU-CE", + }, + "RU-CHE": { + countryCode: "RU", + subdivisionName: "Chelyabinskaya oblast'", + code: "RU-CHE", + }, + "RU-CHU": { + countryCode: "RU", + subdivisionName: "Chukotskiy avtonomnyy okrug", + code: "RU-CHU", + }, + "RU-CU": { + countryCode: "RU", + subdivisionName: "Chuvashskaya Respublika", + code: "RU-CU", + }, + "RU-DA": { + countryCode: "RU", + subdivisionName: "Dagestan, Respublika", + code: "RU-DA", + }, + "RU-IN": { + countryCode: "RU", + subdivisionName: "Ingushetiya, Respublika", + code: "RU-IN", + }, + "RU-IRK": { + countryCode: "RU", + subdivisionName: "Irkutskaya oblast'", + code: "RU-IRK", + }, + "RU-IVA": { + countryCode: "RU", + subdivisionName: "Ivanovskaya oblast'", + code: "RU-IVA", + }, + "RU-KB": { + countryCode: "RU", + subdivisionName: "Kabardino-Balkarskaya Respublika", + code: "RU-KB", + }, + "RU-KGD": { + countryCode: "RU", + subdivisionName: "Kaliningradskaya oblast'", + code: "RU-KGD", + }, + "RU-KL": { + countryCode: "RU", + subdivisionName: "Kalmykiya, Respublika", + code: "RU-KL", + }, + "RU-KLU": { + countryCode: "RU", + subdivisionName: "Kaluzhskaya oblast'", + code: "RU-KLU", + }, + "RU-KAM": { + countryCode: "RU", + subdivisionName: "Kamchatskiy kray", + code: "RU-KAM", + }, + "RU-KC": { + countryCode: "RU", + subdivisionName: "Karachayevo-Cherkesskaya Respublika", + code: "RU-KC", + }, + "RU-KR": { + countryCode: "RU", + subdivisionName: "Kareliya, Respublika", + code: "RU-KR", + }, + "RU-KEM": { + countryCode: "RU", + subdivisionName: "Kemerovskaya oblast'", + code: "RU-KEM", + }, + "RU-KHA": { + countryCode: "RU", + subdivisionName: "Khabarovskiy kray", + code: "RU-KHA", + }, + "RU-KK": { + countryCode: "RU", + subdivisionName: "Khakasiya, Respublika", + code: "RU-KK", + }, + "RU-KHM": { + countryCode: "RU", + subdivisionName: "Khanty-Mansiyskiy avtonomnyy okrug", + code: "RU-KHM", + }, + "RU-KIR": { + countryCode: "RU", + subdivisionName: "Kirovskaya oblast'", + code: "RU-KIR", + }, + "RU-KO": { + countryCode: "RU", + subdivisionName: "Komi, Respublika", + code: "RU-KO", + }, + "RU-KOS": { + countryCode: "RU", + subdivisionName: "Kostromskaya oblast'", + code: "RU-KOS", + }, + "RU-KDA": { + countryCode: "RU", + subdivisionName: "Krasnodarskiy kray", + code: "RU-KDA", + }, + "RU-KYA": { + countryCode: "RU", + subdivisionName: "Krasnoyarskiy kray", + code: "RU-KYA", + }, + "RU-KGN": { + countryCode: "RU", + subdivisionName: "Kurganskaya oblast'", + code: "RU-KGN", + }, + "RU-KRS": { + countryCode: "RU", + subdivisionName: "Kurskaya oblast'", + code: "RU-KRS", + }, + "RU-LEN": { + countryCode: "RU", + subdivisionName: "Leningradskaya oblast'", + code: "RU-LEN", + }, + "RU-LIP": { + countryCode: "RU", + subdivisionName: "Lipetskaya oblast'", + code: "RU-LIP", + }, + "RU-MAG": { + countryCode: "RU", + subdivisionName: "Magadanskaya oblast'", + code: "RU-MAG", + }, + "RU-ME": { + countryCode: "RU", + subdivisionName: "Mariy El, Respublika", + code: "RU-ME", + }, + "RU-MO": { + countryCode: "RU", + subdivisionName: "Mordoviya, Respublika", + code: "RU-MO", + }, + "RU-MOS": { + countryCode: "RU", + subdivisionName: "Moskovskaya oblast'", + code: "RU-MOS", + }, + "RU-MOW": { countryCode: "RU", subdivisionName: "Moskva", code: "RU-MOW" }, + "RU-MUR": { + countryCode: "RU", + subdivisionName: "Murmanskaya oblast'", + code: "RU-MUR", + }, + "RU-NEN": { + countryCode: "RU", + subdivisionName: "Nenetskiy avtonomnyy okrug", + code: "RU-NEN", + }, + "RU-NIZ": { + countryCode: "RU", + subdivisionName: "Nizhegorodskaya oblast'", + code: "RU-NIZ", + }, + "RU-NGR": { + countryCode: "RU", + subdivisionName: "Novgorodskaya oblast'", + code: "RU-NGR", + }, + "RU-NVS": { + countryCode: "RU", + subdivisionName: "Novosibirskaya oblast'", + code: "RU-NVS", + }, + "RU-OMS": { + countryCode: "RU", + subdivisionName: "Omskaya oblast'", + code: "RU-OMS", + }, + "RU-ORE": { + countryCode: "RU", + subdivisionName: "Orenburgskaya oblast'", + code: "RU-ORE", + }, + "RU-ORL": { + countryCode: "RU", + subdivisionName: "Orlovskaya oblast'", + code: "RU-ORL", + }, + "RU-PNZ": { + countryCode: "RU", + subdivisionName: "Penzenskaya oblast'", + code: "RU-PNZ", + }, + "RU-PER": { + countryCode: "RU", + subdivisionName: "Permskiy kray", + code: "RU-PER", + }, + "RU-PRI": { + countryCode: "RU", + subdivisionName: "Primorskiy kray", + code: "RU-PRI", + }, + "RU-PSK": { + countryCode: "RU", + subdivisionName: "Pskovskaya oblast'", + code: "RU-PSK", + }, + "RU-ROS": { + countryCode: "RU", + subdivisionName: "Rostovskaya oblast'", + code: "RU-ROS", + }, + "RU-RYA": { + countryCode: "RU", + subdivisionName: "Ryazanskaya oblast'", + code: "RU-RYA", + }, + "RU-SA": { + countryCode: "RU", + subdivisionName: "Saha, Respublika", + code: "RU-SA", + }, + "RU-SAK": { + countryCode: "RU", + subdivisionName: "Sakhalinskaya oblast'", + code: "RU-SAK", + }, + "RU-SAM": { + countryCode: "RU", + subdivisionName: "Samarskaya oblast'", + code: "RU-SAM", + }, + "RU-SPE": { + countryCode: "RU", + subdivisionName: "Sankt-Peterburg", + code: "RU-SPE", + }, + "RU-SAR": { + countryCode: "RU", + subdivisionName: "Saratovskaya oblast'", + code: "RU-SAR", + }, + "RU-SE": { + countryCode: "RU", + subdivisionName: "Severnaya Osetiya, Respublika", + code: "RU-SE", + }, + "RU-SMO": { + countryCode: "RU", + subdivisionName: "Smolenskaya oblast'", + code: "RU-SMO", + }, + "RU-STA": { + countryCode: "RU", + subdivisionName: "Stavropol'skiy kray", + code: "RU-STA", + }, + "RU-SVE": { + countryCode: "RU", + subdivisionName: "Sverdlovskaya oblast'", + code: "RU-SVE", + }, + "RU-TAM": { + countryCode: "RU", + subdivisionName: "Tambovskaya oblast'", + code: "RU-TAM", + }, + "RU-TA": { + countryCode: "RU", + subdivisionName: "Tatarstan, Respublika", + code: "RU-TA", + }, + "RU-TOM": { + countryCode: "RU", + subdivisionName: "Tomskaya oblast'", + code: "RU-TOM", + }, + "RU-TUL": { + countryCode: "RU", + subdivisionName: "Tul'skaya oblast'", + code: "RU-TUL", + }, + "RU-TVE": { + countryCode: "RU", + subdivisionName: "Tverskaya oblast'", + code: "RU-TVE", + }, + "RU-TYU": { + countryCode: "RU", + subdivisionName: "Tyumenskaya oblast'", + code: "RU-TYU", + }, + "RU-TY": { + countryCode: "RU", + subdivisionName: "Tyva, Respublika", + code: "RU-TY", + }, + "RU-UD": { + countryCode: "RU", + subdivisionName: "Udmurtskaya Respublika", + code: "RU-UD", + }, + "RU-ULY": { + countryCode: "RU", + subdivisionName: "Ul'yanovskaya oblast'", + code: "RU-ULY", + }, + "RU-VLA": { + countryCode: "RU", + subdivisionName: "Vladimirskaya oblast'", + code: "RU-VLA", + }, + "RU-VGG": { + countryCode: "RU", + subdivisionName: "Volgogradskaya oblast'", + code: "RU-VGG", + }, + "RU-VLG": { + countryCode: "RU", + subdivisionName: "Vologodskaya oblast'", + code: "RU-VLG", + }, + "RU-VOR": { + countryCode: "RU", + subdivisionName: "Voronezhskaya oblast'", + code: "RU-VOR", + }, + "RU-YAN": { + countryCode: "RU", + subdivisionName: "Yamalo-Nenetskiy avtonomnyy okrug", + code: "RU-YAN", + }, + "RU-YAR": { + countryCode: "RU", + subdivisionName: "Yaroslavskaya oblast'", + code: "RU-YAR", + }, + "RU-YEV": { + countryCode: "RU", + subdivisionName: "Yevreyskaya avtonomnaya oblast'", + code: "RU-YEV", + }, + "RU-ZAB": { + countryCode: "RU", + subdivisionName: "Zabaykal'skiy kray", + code: "RU-ZAB", + }, + "RW-02": { countryCode: "RW", subdivisionName: "Est", code: "RW-02" }, + "RW-03": { countryCode: "RW", subdivisionName: "Nord", code: "RW-03" }, + "RW-04": { countryCode: "RW", subdivisionName: "Ouest", code: "RW-04" }, + "RW-05": { countryCode: "RW", subdivisionName: "Sud", code: "RW-05" }, + "RW-01": { + countryCode: "RW", + subdivisionName: "Ville de Kigali", + code: "RW-01", + }, + "SA-14": { countryCode: "SA", subdivisionName: "'Asir", code: "SA-14" }, + "SA-11": { countryCode: "SA", subdivisionName: "Al Bahah", code: "SA-11" }, + "SA-08": { + countryCode: "SA", + subdivisionName: "Al Hudud ash Shamaliyah", + code: "SA-08", + }, + "SA-12": { countryCode: "SA", subdivisionName: "Al Jawf", code: "SA-12" }, + "SA-03": { + countryCode: "SA", + subdivisionName: "Al Madinah al Munawwarah", + code: "SA-03", + }, + "SA-05": { countryCode: "SA", subdivisionName: "Al Qasim", code: "SA-05" }, + "SA-01": { countryCode: "SA", subdivisionName: "Ar Riyad", code: "SA-01" }, + "SA-04": { + countryCode: "SA", + subdivisionName: "Ash Sharqiyah", + code: "SA-04", + }, + "SA-06": { countryCode: "SA", subdivisionName: "Ha'il", code: "SA-06" }, + "SA-09": { countryCode: "SA", subdivisionName: "Jazan", code: "SA-09" }, + "SA-02": { + countryCode: "SA", + subdivisionName: "Makkah al Mukarramah", + code: "SA-02", + }, + "SA-10": { countryCode: "SA", subdivisionName: "Najran", code: "SA-10" }, + "SA-07": { countryCode: "SA", subdivisionName: "Tabuk", code: "SA-07" }, + "SB-CH": { countryCode: "SB", subdivisionName: "Choiseul", code: "SB-CH" }, + "SB-GU": { countryCode: "SB", subdivisionName: "Guadalcanal", code: "SB-GU" }, + "SB-WE": { countryCode: "SB", subdivisionName: "Western", code: "SB-WE" }, + "SC-02": { + countryCode: "SC", + subdivisionName: "Anse Boileau", + code: "SC-02", + }, + "SC-05": { countryCode: "SC", subdivisionName: "Anse Royale", code: "SC-05" }, + "SC-01": { + countryCode: "SC", + subdivisionName: "Anse aux Pins", + code: "SC-01", + }, + "SC-06": { countryCode: "SC", subdivisionName: "Baie Lazare", code: "SC-06" }, + "SC-07": { + countryCode: "SC", + subdivisionName: "Baie Sainte Anne", + code: "SC-07", + }, + "SC-08": { countryCode: "SC", subdivisionName: "Beau Vallon", code: "SC-08" }, + "SC-10": { countryCode: "SC", subdivisionName: "Bel Ombre", code: "SC-10" }, + "SC-11": { countryCode: "SC", subdivisionName: "Cascade", code: "SC-11" }, + "SC-16": { + countryCode: "SC", + subdivisionName: "English River", + code: "SC-16", + }, + "SC-13": { + countryCode: "SC", + subdivisionName: "Grand Anse Mahe", + code: "SC-13", + }, + "SC-14": { + countryCode: "SC", + subdivisionName: "Grand Anse Praslin", + code: "SC-14", + }, + "SC-15": { countryCode: "SC", subdivisionName: "La Digue", code: "SC-15" }, + "SC-20": { + countryCode: "SC", + subdivisionName: "Pointe Larue", + code: "SC-20", + }, + "SC-23": { countryCode: "SC", subdivisionName: "Takamaka", code: "SC-23" }, + "SD-NB": { countryCode: "SD", subdivisionName: "Blue Nile", code: "SD-NB" }, + "SD-DC": { + countryCode: "SD", + subdivisionName: "Central Darfur", + code: "SD-DC", + }, + "SD-GD": { countryCode: "SD", subdivisionName: "Gedaref", code: "SD-GD" }, + "SD-GZ": { countryCode: "SD", subdivisionName: "Gezira", code: "SD-GZ" }, + "SD-KA": { countryCode: "SD", subdivisionName: "Kassala", code: "SD-KA" }, + "SD-KH": { countryCode: "SD", subdivisionName: "Khartoum", code: "SD-KH" }, + "SD-DN": { + countryCode: "SD", + subdivisionName: "North Darfur", + code: "SD-DN", + }, + "SD-KN": { + countryCode: "SD", + subdivisionName: "North Kordofan", + code: "SD-KN", + }, + "SD-NO": { countryCode: "SD", subdivisionName: "Northern", code: "SD-NO" }, + "SD-RS": { countryCode: "SD", subdivisionName: "Red Sea", code: "SD-RS" }, + "SD-NR": { countryCode: "SD", subdivisionName: "River Nile", code: "SD-NR" }, + "SD-SI": { countryCode: "SD", subdivisionName: "Sennar", code: "SD-SI" }, + "SD-DS": { + countryCode: "SD", + subdivisionName: "South Darfur", + code: "SD-DS", + }, + "SD-KS": { + countryCode: "SD", + subdivisionName: "South Kordofan", + code: "SD-KS", + }, + "SD-DW": { countryCode: "SD", subdivisionName: "West Darfur", code: "SD-DW" }, + "SD-GK": { + countryCode: "SD", + subdivisionName: "West Kordofan", + code: "SD-GK", + }, + "SD-NW": { countryCode: "SD", subdivisionName: "White Nile", code: "SD-NW" }, + "SE-K": { countryCode: "SE", subdivisionName: "Blekinge lan", code: "SE-K" }, + "SE-W": { countryCode: "SE", subdivisionName: "Dalarnas lan", code: "SE-W" }, + "SE-X": { + countryCode: "SE", + subdivisionName: "Gavleborgs lan", + code: "SE-X", + }, + "SE-I": { countryCode: "SE", subdivisionName: "Gotlands lan", code: "SE-I" }, + "SE-N": { countryCode: "SE", subdivisionName: "Hallands lan", code: "SE-N" }, + "SE-Z": { countryCode: "SE", subdivisionName: "Jamtlands lan", code: "SE-Z" }, + "SE-F": { + countryCode: "SE", + subdivisionName: "Jonkopings lan", + code: "SE-F", + }, + "SE-H": { countryCode: "SE", subdivisionName: "Kalmar lan", code: "SE-H" }, + "SE-G": { + countryCode: "SE", + subdivisionName: "Kronobergs lan", + code: "SE-G", + }, + "SE-BD": { + countryCode: "SE", + subdivisionName: "Norrbottens lan", + code: "SE-BD", + }, + "SE-T": { countryCode: "SE", subdivisionName: "Orebro lan", code: "SE-T" }, + "SE-E": { + countryCode: "SE", + subdivisionName: "Ostergotlands lan", + code: "SE-E", + }, + "SE-M": { countryCode: "SE", subdivisionName: "Skane lan", code: "SE-M" }, + "SE-D": { + countryCode: "SE", + subdivisionName: "Sodermanlands lan", + code: "SE-D", + }, + "SE-AB": { + countryCode: "SE", + subdivisionName: "Stockholms lan", + code: "SE-AB", + }, + "SE-C": { countryCode: "SE", subdivisionName: "Uppsala lan", code: "SE-C" }, + "SE-S": { countryCode: "SE", subdivisionName: "Varmlands lan", code: "SE-S" }, + "SE-AC": { + countryCode: "SE", + subdivisionName: "Vasterbottens lan", + code: "SE-AC", + }, + "SE-Y": { + countryCode: "SE", + subdivisionName: "Vasternorrlands lan", + code: "SE-Y", + }, + "SE-U": { + countryCode: "SE", + subdivisionName: "Vastmanlands lan", + code: "SE-U", + }, + "SE-O": { + countryCode: "SE", + subdivisionName: "Vastra Gotalands lan", + code: "SE-O", + }, + "SH-HL": { + countryCode: "SH", + subdivisionName: "Saint Helena", + code: "SH-HL", + }, + "SI-001": { + countryCode: "SI", + subdivisionName: "Ajdovscina", + code: "SI-001", + }, + "SI-213": { countryCode: "SI", subdivisionName: "Ankaran", code: "SI-213" }, + "SI-195": { countryCode: "SI", subdivisionName: "Apace", code: "SI-195" }, + "SI-002": { countryCode: "SI", subdivisionName: "Beltinci", code: "SI-002" }, + "SI-148": { countryCode: "SI", subdivisionName: "Benedikt", code: "SI-148" }, + "SI-149": { + countryCode: "SI", + subdivisionName: "Bistrica ob Sotli", + code: "SI-149", + }, + "SI-003": { countryCode: "SI", subdivisionName: "Bled", code: "SI-003" }, + "SI-150": { countryCode: "SI", subdivisionName: "Bloke", code: "SI-150" }, + "SI-004": { countryCode: "SI", subdivisionName: "Bohinj", code: "SI-004" }, + "SI-005": { countryCode: "SI", subdivisionName: "Borovnica", code: "SI-005" }, + "SI-006": { countryCode: "SI", subdivisionName: "Bovec", code: "SI-006" }, + "SI-151": { countryCode: "SI", subdivisionName: "Braslovce", code: "SI-151" }, + "SI-007": { countryCode: "SI", subdivisionName: "Brda", code: "SI-007" }, + "SI-009": { countryCode: "SI", subdivisionName: "Brezice", code: "SI-009" }, + "SI-008": { countryCode: "SI", subdivisionName: "Brezovica", code: "SI-008" }, + "SI-152": { countryCode: "SI", subdivisionName: "Cankova", code: "SI-152" }, + "SI-011": { countryCode: "SI", subdivisionName: "Celje", code: "SI-011" }, + "SI-012": { + countryCode: "SI", + subdivisionName: "Cerklje na Gorenjskem", + code: "SI-012", + }, + "SI-013": { countryCode: "SI", subdivisionName: "Cerknica", code: "SI-013" }, + "SI-014": { countryCode: "SI", subdivisionName: "Cerkno", code: "SI-014" }, + "SI-196": { countryCode: "SI", subdivisionName: "Cirkulane", code: "SI-196" }, + "SI-015": { countryCode: "SI", subdivisionName: "Crensovci", code: "SI-015" }, + "SI-017": { countryCode: "SI", subdivisionName: "Crnomelj", code: "SI-017" }, + "SI-018": { countryCode: "SI", subdivisionName: "Destrnik", code: "SI-018" }, + "SI-019": { countryCode: "SI", subdivisionName: "Divaca", code: "SI-019" }, + "SI-154": { countryCode: "SI", subdivisionName: "Dobje", code: "SI-154" }, + "SI-020": { + countryCode: "SI", + subdivisionName: "Dobrepolje", + code: "SI-020", + }, + "SI-155": { countryCode: "SI", subdivisionName: "Dobrna", code: "SI-155" }, + "SI-021": { + countryCode: "SI", + subdivisionName: "Dobrova-Polhov Gradec", + code: "SI-021", + }, + "SI-156": { countryCode: "SI", subdivisionName: "Dobrovnik", code: "SI-156" }, + "SI-023": { countryCode: "SI", subdivisionName: "Domzale", code: "SI-023" }, + "SI-024": { countryCode: "SI", subdivisionName: "Dornava", code: "SI-024" }, + "SI-025": { countryCode: "SI", subdivisionName: "Dravograd", code: "SI-025" }, + "SI-026": { countryCode: "SI", subdivisionName: "Duplek", code: "SI-026" }, + "SI-207": { countryCode: "SI", subdivisionName: "Gorje", code: "SI-207" }, + "SI-029": { + countryCode: "SI", + subdivisionName: "Gornja Radgona", + code: "SI-029", + }, + "SI-031": { + countryCode: "SI", + subdivisionName: "Gornji Petrovci", + code: "SI-031", + }, + "SI-158": { countryCode: "SI", subdivisionName: "Grad", code: "SI-158" }, + "SI-032": { countryCode: "SI", subdivisionName: "Grosuplje", code: "SI-032" }, + "SI-159": { countryCode: "SI", subdivisionName: "Hajdina", code: "SI-159" }, + "SI-160": { + countryCode: "SI", + subdivisionName: "Hoce-Slivnica", + code: "SI-160", + }, + "SI-161": { countryCode: "SI", subdivisionName: "Hodos", code: "SI-161" }, + "SI-162": { countryCode: "SI", subdivisionName: "Horjul", code: "SI-162" }, + "SI-034": { countryCode: "SI", subdivisionName: "Hrastnik", code: "SI-034" }, + "SI-035": { + countryCode: "SI", + subdivisionName: "Hrpelje-Kozina", + code: "SI-035", + }, + "SI-036": { countryCode: "SI", subdivisionName: "Idrija", code: "SI-036" }, + "SI-037": { countryCode: "SI", subdivisionName: "Ig", code: "SI-037" }, + "SI-038": { + countryCode: "SI", + subdivisionName: "Ilirska Bistrica", + code: "SI-038", + }, + "SI-039": { + countryCode: "SI", + subdivisionName: "Ivancna Gorica", + code: "SI-039", + }, + "SI-040": { countryCode: "SI", subdivisionName: "Izola", code: "SI-040" }, + "SI-041": { countryCode: "SI", subdivisionName: "Jesenice", code: "SI-041" }, + "SI-042": { countryCode: "SI", subdivisionName: "Jursinci", code: "SI-042" }, + "SI-043": { countryCode: "SI", subdivisionName: "Kamnik", code: "SI-043" }, + "SI-044": { countryCode: "SI", subdivisionName: "Kanal", code: "SI-044" }, + "SI-045": { countryCode: "SI", subdivisionName: "Kidricevo", code: "SI-045" }, + "SI-046": { countryCode: "SI", subdivisionName: "Kobarid", code: "SI-046" }, + "SI-047": { countryCode: "SI", subdivisionName: "Kobilje", code: "SI-047" }, + "SI-048": { countryCode: "SI", subdivisionName: "Kocevje", code: "SI-048" }, + "SI-049": { countryCode: "SI", subdivisionName: "Komen", code: "SI-049" }, + "SI-164": { countryCode: "SI", subdivisionName: "Komenda", code: "SI-164" }, + "SI-050": { countryCode: "SI", subdivisionName: "Koper", code: "SI-050" }, + "SI-197": { + countryCode: "SI", + subdivisionName: "Kosanjevica na Krki", + code: "SI-197", + }, + "SI-165": { countryCode: "SI", subdivisionName: "Kostel", code: "SI-165" }, + "SI-052": { countryCode: "SI", subdivisionName: "Kranj", code: "SI-052" }, + "SI-053": { + countryCode: "SI", + subdivisionName: "Kranjska Gora", + code: "SI-053", + }, + "SI-166": { countryCode: "SI", subdivisionName: "Krizevci", code: "SI-166" }, + "SI-054": { countryCode: "SI", subdivisionName: "Krsko", code: "SI-054" }, + "SI-055": { countryCode: "SI", subdivisionName: "Kungota", code: "SI-055" }, + "SI-056": { countryCode: "SI", subdivisionName: "Kuzma", code: "SI-056" }, + "SI-057": { countryCode: "SI", subdivisionName: "Lasko", code: "SI-057" }, + "SI-058": { countryCode: "SI", subdivisionName: "Lenart", code: "SI-058" }, + "SI-059": { countryCode: "SI", subdivisionName: "Lendava", code: "SI-059" }, + "SI-060": { countryCode: "SI", subdivisionName: "Litija", code: "SI-060" }, + "SI-061": { countryCode: "SI", subdivisionName: "Ljubljana", code: "SI-061" }, + "SI-063": { countryCode: "SI", subdivisionName: "Ljutomer", code: "SI-063" }, + "SI-208": { + countryCode: "SI", + subdivisionName: "Log-Dragomer", + code: "SI-208", + }, + "SI-064": { countryCode: "SI", subdivisionName: "Logatec", code: "SI-064" }, + "SI-065": { + countryCode: "SI", + subdivisionName: "Loska dolina", + code: "SI-065", + }, + "SI-066": { + countryCode: "SI", + subdivisionName: "Loski Potok", + code: "SI-066", + }, + "SI-167": { + countryCode: "SI", + subdivisionName: "Lovrenc na Pohorju", + code: "SI-167", + }, + "SI-067": { countryCode: "SI", subdivisionName: "Luce", code: "SI-067" }, + "SI-068": { countryCode: "SI", subdivisionName: "Lukovica", code: "SI-068" }, + "SI-069": { countryCode: "SI", subdivisionName: "Majsperk", code: "SI-069" }, + "SI-198": { countryCode: "SI", subdivisionName: "Makole", code: "SI-198" }, + "SI-070": { countryCode: "SI", subdivisionName: "Maribor", code: "SI-070" }, + "SI-168": { countryCode: "SI", subdivisionName: "Markovci", code: "SI-168" }, + "SI-071": { countryCode: "SI", subdivisionName: "Medvode", code: "SI-071" }, + "SI-072": { countryCode: "SI", subdivisionName: "Menges", code: "SI-072" }, + "SI-073": { countryCode: "SI", subdivisionName: "Metlika", code: "SI-073" }, + "SI-074": { countryCode: "SI", subdivisionName: "Mezica", code: "SI-074" }, + "SI-169": { + countryCode: "SI", + subdivisionName: "Miklavz na Dravskem polju", + code: "SI-169", + }, + "SI-075": { + countryCode: "SI", + subdivisionName: "Miren-Kostanjevica", + code: "SI-075", + }, + "SI-212": { countryCode: "SI", subdivisionName: "Mirna", code: "SI-212" }, + "SI-170": { countryCode: "SI", subdivisionName: "Mirna Pec", code: "SI-170" }, + "SI-076": { countryCode: "SI", subdivisionName: "Mislinja", code: "SI-076" }, + "SI-199": { + countryCode: "SI", + subdivisionName: "Mokronog-Trebelno", + code: "SI-199", + }, + "SI-077": { countryCode: "SI", subdivisionName: "Moravce", code: "SI-077" }, + "SI-079": { countryCode: "SI", subdivisionName: "Mozirje", code: "SI-079" }, + "SI-080": { + countryCode: "SI", + subdivisionName: "Murska Sobota", + code: "SI-080", + }, + "SI-081": { countryCode: "SI", subdivisionName: "Muta", code: "SI-081" }, + "SI-082": { countryCode: "SI", subdivisionName: "Naklo", code: "SI-082" }, + "SI-083": { countryCode: "SI", subdivisionName: "Nazarje", code: "SI-083" }, + "SI-084": { + countryCode: "SI", + subdivisionName: "Nova Gorica", + code: "SI-084", + }, + "SI-085": { + countryCode: "SI", + subdivisionName: "Novo Mesto", + code: "SI-085", + }, + "SI-086": { countryCode: "SI", subdivisionName: "Odranci", code: "SI-086" }, + "SI-171": { countryCode: "SI", subdivisionName: "Oplotnica", code: "SI-171" }, + "SI-087": { countryCode: "SI", subdivisionName: "Ormoz", code: "SI-087" }, + "SI-090": { countryCode: "SI", subdivisionName: "Piran", code: "SI-090" }, + "SI-091": { countryCode: "SI", subdivisionName: "Pivka", code: "SI-091" }, + "SI-092": { + countryCode: "SI", + subdivisionName: "Podcetrtek", + code: "SI-092", + }, + "SI-172": { countryCode: "SI", subdivisionName: "Podlehnik", code: "SI-172" }, + "SI-200": { countryCode: "SI", subdivisionName: "Poljcane", code: "SI-200" }, + "SI-173": { countryCode: "SI", subdivisionName: "Polzela", code: "SI-173" }, + "SI-094": { countryCode: "SI", subdivisionName: "Postojna", code: "SI-094" }, + "SI-174": { countryCode: "SI", subdivisionName: "Prebold", code: "SI-174" }, + "SI-095": { countryCode: "SI", subdivisionName: "Preddvor", code: "SI-095" }, + "SI-175": { countryCode: "SI", subdivisionName: "Prevalje", code: "SI-175" }, + "SI-096": { countryCode: "SI", subdivisionName: "Ptuj", code: "SI-096" }, + "SI-097": { countryCode: "SI", subdivisionName: "Puconci", code: "SI-097" }, + "SI-098": { countryCode: "SI", subdivisionName: "Race-Fram", code: "SI-098" }, + "SI-099": { countryCode: "SI", subdivisionName: "Radece", code: "SI-099" }, + "SI-100": { countryCode: "SI", subdivisionName: "Radenci", code: "SI-100" }, + "SI-101": { + countryCode: "SI", + subdivisionName: "Radlje ob Dravi", + code: "SI-101", + }, + "SI-102": { + countryCode: "SI", + subdivisionName: "Radovljica", + code: "SI-102", + }, + "SI-103": { + countryCode: "SI", + subdivisionName: "Ravne na Koroskem", + code: "SI-103", + }, + "SI-176": { countryCode: "SI", subdivisionName: "Razkrizje", code: "SI-176" }, + "SI-209": { + countryCode: "SI", + subdivisionName: "Recica ob Savinji", + code: "SI-209", + }, + "SI-201": { + countryCode: "SI", + subdivisionName: "Rence-Vogrsko", + code: "SI-201", + }, + "SI-104": { countryCode: "SI", subdivisionName: "Ribnica", code: "SI-104" }, + "SI-106": { + countryCode: "SI", + subdivisionName: "Rogaska Slatina", + code: "SI-106", + }, + "SI-105": { countryCode: "SI", subdivisionName: "Rogasovci", code: "SI-105" }, + "SI-108": { countryCode: "SI", subdivisionName: "Ruse", code: "SI-108" }, + "SI-033": { countryCode: "SI", subdivisionName: "Salovci", code: "SI-033" }, + "SI-109": { countryCode: "SI", subdivisionName: "Semic", code: "SI-109" }, + "SI-183": { + countryCode: "SI", + subdivisionName: "Sempeter-Vrtojba", + code: "SI-183", + }, + "SI-117": { countryCode: "SI", subdivisionName: "Sencur", code: "SI-117" }, + "SI-118": { countryCode: "SI", subdivisionName: "Sentilj", code: "SI-118" }, + "SI-119": { + countryCode: "SI", + subdivisionName: "Sentjernej", + code: "SI-119", + }, + "SI-120": { countryCode: "SI", subdivisionName: "Sentjur", code: "SI-120" }, + "SI-211": { + countryCode: "SI", + subdivisionName: "Sentrupert", + code: "SI-211", + }, + "SI-110": { countryCode: "SI", subdivisionName: "Sevnica", code: "SI-110" }, + "SI-111": { countryCode: "SI", subdivisionName: "Sezana", code: "SI-111" }, + "SI-121": { countryCode: "SI", subdivisionName: "Skocjan", code: "SI-121" }, + "SI-122": { + countryCode: "SI", + subdivisionName: "Skofja Loka", + code: "SI-122", + }, + "SI-123": { countryCode: "SI", subdivisionName: "Skofljica", code: "SI-123" }, + "SI-112": { + countryCode: "SI", + subdivisionName: "Slovenj Gradec", + code: "SI-112", + }, + "SI-113": { + countryCode: "SI", + subdivisionName: "Slovenska Bistrica", + code: "SI-113", + }, + "SI-114": { + countryCode: "SI", + subdivisionName: "Slovenske Konjice", + code: "SI-114", + }, + "SI-124": { + countryCode: "SI", + subdivisionName: "Smarje pri Jelsah", + code: "SI-124", + }, + "SI-206": { + countryCode: "SI", + subdivisionName: "Smarjeske Toplice", + code: "SI-206", + }, + "SI-125": { + countryCode: "SI", + subdivisionName: "Smartno ob Paki", + code: "SI-125", + }, + "SI-194": { + countryCode: "SI", + subdivisionName: "Smartno pri Litiji", + code: "SI-194", + }, + "SI-179": { countryCode: "SI", subdivisionName: "Sodrazica", code: "SI-179" }, + "SI-180": { countryCode: "SI", subdivisionName: "Solcava", code: "SI-180" }, + "SI-126": { countryCode: "SI", subdivisionName: "Sostanj", code: "SI-126" }, + "SI-115": { countryCode: "SI", subdivisionName: "Starse", code: "SI-115" }, + "SI-127": { countryCode: "SI", subdivisionName: "Store", code: "SI-127" }, + "SI-203": { countryCode: "SI", subdivisionName: "Straza", code: "SI-203" }, + "SI-204": { + countryCode: "SI", + subdivisionName: "Sveta Trojica v Slovenskih goricah", + code: "SI-204", + }, + "SI-182": { + countryCode: "SI", + subdivisionName: "Sveti Andraz v Slovenskih Goricah", + code: "SI-182", + }, + "SI-116": { + countryCode: "SI", + subdivisionName: "Sveti Jurij ob Scavnici", + code: "SI-116", + }, + "SI-210": { + countryCode: "SI", + subdivisionName: "Sveti Jurij v Slovenskih goricah", + code: "SI-210", + }, + "SI-205": { + countryCode: "SI", + subdivisionName: "Sveti Tomaz", + code: "SI-205", + }, + "SI-184": { countryCode: "SI", subdivisionName: "Tabor", code: "SI-184" }, + "SI-010": { countryCode: "SI", subdivisionName: "Tisina", code: "SI-010" }, + "SI-128": { countryCode: "SI", subdivisionName: "Tolmin", code: "SI-128" }, + "SI-129": { countryCode: "SI", subdivisionName: "Trbovlje", code: "SI-129" }, + "SI-130": { countryCode: "SI", subdivisionName: "Trebnje", code: "SI-130" }, + "SI-185": { + countryCode: "SI", + subdivisionName: "Trnovska Vas", + code: "SI-185", + }, + "SI-131": { countryCode: "SI", subdivisionName: "Trzic", code: "SI-131" }, + "SI-186": { countryCode: "SI", subdivisionName: "Trzin", code: "SI-186" }, + "SI-132": { countryCode: "SI", subdivisionName: "Turnisce", code: "SI-132" }, + "SI-133": { countryCode: "SI", subdivisionName: "Velenje", code: "SI-133" }, + "SI-187": { + countryCode: "SI", + subdivisionName: "Velika Polana", + code: "SI-187", + }, + "SI-134": { + countryCode: "SI", + subdivisionName: "Velike Lasce", + code: "SI-134", + }, + "SI-188": { countryCode: "SI", subdivisionName: "Verzej", code: "SI-188" }, + "SI-135": { countryCode: "SI", subdivisionName: "Videm", code: "SI-135" }, + "SI-136": { countryCode: "SI", subdivisionName: "Vipava", code: "SI-136" }, + "SI-137": { countryCode: "SI", subdivisionName: "Vitanje", code: "SI-137" }, + "SI-138": { countryCode: "SI", subdivisionName: "Vodice", code: "SI-138" }, + "SI-139": { countryCode: "SI", subdivisionName: "Vojnik", code: "SI-139" }, + "SI-189": { countryCode: "SI", subdivisionName: "Vransko", code: "SI-189" }, + "SI-140": { countryCode: "SI", subdivisionName: "Vrhnika", code: "SI-140" }, + "SI-141": { countryCode: "SI", subdivisionName: "Vuzenica", code: "SI-141" }, + "SI-142": { + countryCode: "SI", + subdivisionName: "Zagorje ob Savi", + code: "SI-142", + }, + "SI-190": { countryCode: "SI", subdivisionName: "Zalec", code: "SI-190" }, + "SI-143": { countryCode: "SI", subdivisionName: "Zavrc", code: "SI-143" }, + "SI-146": { countryCode: "SI", subdivisionName: "Zelezniki", code: "SI-146" }, + "SI-191": { countryCode: "SI", subdivisionName: "Zetale", code: "SI-191" }, + "SI-147": { countryCode: "SI", subdivisionName: "Ziri", code: "SI-147" }, + "SI-144": { countryCode: "SI", subdivisionName: "Zrece", code: "SI-144" }, + "SI-193": { countryCode: "SI", subdivisionName: "Zuzemberk", code: "SI-193" }, + "SK-BC": { + countryCode: "SK", + subdivisionName: "Banskobystricky kraj", + code: "SK-BC", + }, + "SK-BL": { + countryCode: "SK", + subdivisionName: "Bratislavsky kraj", + code: "SK-BL", + }, + "SK-KI": { + countryCode: "SK", + subdivisionName: "Kosicky kraj", + code: "SK-KI", + }, + "SK-NI": { + countryCode: "SK", + subdivisionName: "Nitriansky kraj", + code: "SK-NI", + }, + "SK-PV": { + countryCode: "SK", + subdivisionName: "Presovsky kraj", + code: "SK-PV", + }, + "SK-TC": { + countryCode: "SK", + subdivisionName: "Trenciansky kraj", + code: "SK-TC", + }, + "SK-TA": { + countryCode: "SK", + subdivisionName: "Trnavsky kraj", + code: "SK-TA", + }, + "SK-ZI": { + countryCode: "SK", + subdivisionName: "Zilinsky kraj", + code: "SK-ZI", + }, + "SL-E": { countryCode: "SL", subdivisionName: "Eastern", code: "SL-E" }, + "SL-NW": { + countryCode: "SL", + subdivisionName: "North Western", + code: "SL-NW", + }, + "SL-N": { countryCode: "SL", subdivisionName: "Northern", code: "SL-N" }, + "SL-S": { countryCode: "SL", subdivisionName: "Southern", code: "SL-S" }, + "SL-W": { countryCode: "SL", subdivisionName: "Western Area", code: "SL-W" }, + "SM-02": { countryCode: "SM", subdivisionName: "Chiesanuova", code: "SM-02" }, + "SM-07": { + countryCode: "SM", + subdivisionName: "Citta di San Marino", + code: "SM-07", + }, + "SM-04": { countryCode: "SM", subdivisionName: "Faetano", code: "SM-04" }, + "SM-09": { countryCode: "SM", subdivisionName: "Serravalle", code: "SM-09" }, + "SN-DK": { countryCode: "SN", subdivisionName: "Dakar", code: "SN-DK" }, + "SN-DB": { countryCode: "SN", subdivisionName: "Diourbel", code: "SN-DB" }, + "SN-FK": { countryCode: "SN", subdivisionName: "Fatick", code: "SN-FK" }, + "SN-KA": { countryCode: "SN", subdivisionName: "Kaffrine", code: "SN-KA" }, + "SN-KL": { countryCode: "SN", subdivisionName: "Kaolack", code: "SN-KL" }, + "SN-KE": { countryCode: "SN", subdivisionName: "Kedougou", code: "SN-KE" }, + "SN-KD": { countryCode: "SN", subdivisionName: "Kolda", code: "SN-KD" }, + "SN-LG": { countryCode: "SN", subdivisionName: "Louga", code: "SN-LG" }, + "SN-MT": { countryCode: "SN", subdivisionName: "Matam", code: "SN-MT" }, + "SN-SL": { countryCode: "SN", subdivisionName: "Saint-Louis", code: "SN-SL" }, + "SN-SE": { countryCode: "SN", subdivisionName: "Sedhiou", code: "SN-SE" }, + "SN-TC": { countryCode: "SN", subdivisionName: "Tambacounda", code: "SN-TC" }, + "SN-TH": { countryCode: "SN", subdivisionName: "Thies", code: "SN-TH" }, + "SN-ZG": { countryCode: "SN", subdivisionName: "Ziguinchor", code: "SN-ZG" }, + "SO-AW": { countryCode: "SO", subdivisionName: "Awdal", code: "SO-AW" }, + "SO-BN": { countryCode: "SO", subdivisionName: "Banaadir", code: "SO-BN" }, + "SO-BR": { countryCode: "SO", subdivisionName: "Bari", code: "SO-BR" }, + "SO-BY": { countryCode: "SO", subdivisionName: "Bay", code: "SO-BY" }, + "SO-GA": { countryCode: "SO", subdivisionName: "Galguduud", code: "SO-GA" }, + "SO-GE": { countryCode: "SO", subdivisionName: "Gedo", code: "SO-GE" }, + "SO-HI": { countryCode: "SO", subdivisionName: "Hiiraan", code: "SO-HI" }, + "SO-JH": { + countryCode: "SO", + subdivisionName: "Jubbada Hoose", + code: "SO-JH", + }, + "SO-MU": { countryCode: "SO", subdivisionName: "Mudug", code: "SO-MU" }, + "SO-NU": { countryCode: "SO", subdivisionName: "Nugaal", code: "SO-NU" }, + "SO-SO": { countryCode: "SO", subdivisionName: "Sool", code: "SO-SO" }, + "SO-TO": { countryCode: "SO", subdivisionName: "Togdheer", code: "SO-TO" }, + "SO-WO": { + countryCode: "SO", + subdivisionName: "Woqooyi Galbeed", + code: "SO-WO", + }, + "SR-BR": { countryCode: "SR", subdivisionName: "Brokopondo", code: "SR-BR" }, + "SR-CM": { countryCode: "SR", subdivisionName: "Commewijne", code: "SR-CM" }, + "SR-CR": { countryCode: "SR", subdivisionName: "Coronie", code: "SR-CR" }, + "SR-NI": { countryCode: "SR", subdivisionName: "Nickerie", code: "SR-NI" }, + "SR-PR": { countryCode: "SR", subdivisionName: "Para", code: "SR-PR" }, + "SR-PM": { countryCode: "SR", subdivisionName: "Paramaribo", code: "SR-PM" }, + "SR-SA": { countryCode: "SR", subdivisionName: "Saramacca", code: "SR-SA" }, + "SR-SI": { countryCode: "SR", subdivisionName: "Sipaliwini", code: "SR-SI" }, + "SR-WA": { countryCode: "SR", subdivisionName: "Wanica", code: "SR-WA" }, + "SS-EC": { + countryCode: "SS", + subdivisionName: "Central Equatoria", + code: "SS-EC", + }, + "SS-EE": { + countryCode: "SS", + subdivisionName: "Eastern Equatoria", + code: "SS-EE", + }, + "SS-BN": { + countryCode: "SS", + subdivisionName: "Northern Bahr el Ghazal", + code: "SS-BN", + }, + "SS-UY": { countryCode: "SS", subdivisionName: "Unity", code: "SS-UY" }, + "SS-NU": { countryCode: "SS", subdivisionName: "Upper Nile", code: "SS-NU" }, + "SS-EW": { + countryCode: "SS", + subdivisionName: "Western Equatoria", + code: "SS-EW", + }, + "ST-01": { countryCode: "ST", subdivisionName: "Agua Grande", code: "ST-01" }, + "SV-AH": { countryCode: "SV", subdivisionName: "Ahuachapan", code: "SV-AH" }, + "SV-CA": { countryCode: "SV", subdivisionName: "Cabanas", code: "SV-CA" }, + "SV-CH": { + countryCode: "SV", + subdivisionName: "Chalatenango", + code: "SV-CH", + }, + "SV-CU": { countryCode: "SV", subdivisionName: "Cuscatlan", code: "SV-CU" }, + "SV-LI": { countryCode: "SV", subdivisionName: "La Libertad", code: "SV-LI" }, + "SV-PA": { countryCode: "SV", subdivisionName: "La Paz", code: "SV-PA" }, + "SV-UN": { countryCode: "SV", subdivisionName: "La Union", code: "SV-UN" }, + "SV-MO": { countryCode: "SV", subdivisionName: "Morazan", code: "SV-MO" }, + "SV-SM": { countryCode: "SV", subdivisionName: "San Miguel", code: "SV-SM" }, + "SV-SS": { + countryCode: "SV", + subdivisionName: "San Salvador", + code: "SV-SS", + }, + "SV-SV": { countryCode: "SV", subdivisionName: "San Vicente", code: "SV-SV" }, + "SV-SA": { countryCode: "SV", subdivisionName: "Santa Ana", code: "SV-SA" }, + "SV-SO": { countryCode: "SV", subdivisionName: "Sonsonate", code: "SV-SO" }, + "SV-US": { countryCode: "SV", subdivisionName: "Usulutan", code: "SV-US" }, + "SY-HA": { countryCode: "SY", subdivisionName: "Al Hasakah", code: "SY-HA" }, + "SY-LA": { + countryCode: "SY", + subdivisionName: "Al Ladhiqiyah", + code: "SY-LA", + }, + "SY-QU": { + countryCode: "SY", + subdivisionName: "Al Qunaytirah", + code: "SY-QU", + }, + "SY-RA": { countryCode: "SY", subdivisionName: "Ar Raqqah", code: "SY-RA" }, + "SY-SU": { countryCode: "SY", subdivisionName: "As Suwayda'", code: "SY-SU" }, + "SY-DR": { countryCode: "SY", subdivisionName: "Dar'a", code: "SY-DR" }, + "SY-DY": { + countryCode: "SY", + subdivisionName: "Dayr az Zawr", + code: "SY-DY", + }, + "SY-DI": { countryCode: "SY", subdivisionName: "Dimashq", code: "SY-DI" }, + "SY-HL": { countryCode: "SY", subdivisionName: "Halab", code: "SY-HL" }, + "SY-HM": { countryCode: "SY", subdivisionName: "Hamah", code: "SY-HM" }, + "SY-HI": { countryCode: "SY", subdivisionName: "Hims", code: "SY-HI" }, + "SY-ID": { countryCode: "SY", subdivisionName: "Idlib", code: "SY-ID" }, + "SY-RD": { countryCode: "SY", subdivisionName: "Rif Dimashq", code: "SY-RD" }, + "SY-TA": { countryCode: "SY", subdivisionName: "Tartus", code: "SY-TA" }, + "SZ-HH": { countryCode: "SZ", subdivisionName: "Hhohho", code: "SZ-HH" }, + "SZ-LU": { countryCode: "SZ", subdivisionName: "Lubombo", code: "SZ-LU" }, + "SZ-MA": { countryCode: "SZ", subdivisionName: "Manzini", code: "SZ-MA" }, + "TD-BG": { + countryCode: "TD", + subdivisionName: "Bahr el Ghazal", + code: "TD-BG", + }, + "TD-CB": { + countryCode: "TD", + subdivisionName: "Chari-Baguirmi", + code: "TD-CB", + }, + "TD-GR": { countryCode: "TD", subdivisionName: "Guera", code: "TD-GR" }, + "TD-LC": { countryCode: "TD", subdivisionName: "Lac", code: "TD-LC" }, + "TD-LR": { + countryCode: "TD", + subdivisionName: "Logone-Oriental", + code: "TD-LR", + }, + "TD-OD": { countryCode: "TD", subdivisionName: "Ouaddai", code: "TD-OD" }, + "TD-ND": { + countryCode: "TD", + subdivisionName: "Ville de Ndjamena", + code: "TD-ND", + }, + "TG-C": { countryCode: "TG", subdivisionName: "Centrale", code: "TG-C" }, + "TG-K": { countryCode: "TG", subdivisionName: "Kara", code: "TG-K" }, + "TG-M": { countryCode: "TG", subdivisionName: "Maritime", code: "TG-M" }, + "TG-P": { countryCode: "TG", subdivisionName: "Plateaux", code: "TG-P" }, + "TG-S": { countryCode: "TG", subdivisionName: "Savanes", code: "TG-S" }, + "TH-37": { + countryCode: "TH", + subdivisionName: "Amnat Charoen", + code: "TH-37", + }, + "TH-15": { countryCode: "TH", subdivisionName: "Ang Thong", code: "TH-15" }, + "TH-38": { countryCode: "TH", subdivisionName: "Bueng Kan", code: "TH-38" }, + "TH-31": { countryCode: "TH", subdivisionName: "Buri Ram", code: "TH-31" }, + "TH-24": { + countryCode: "TH", + subdivisionName: "Chachoengsao", + code: "TH-24", + }, + "TH-18": { countryCode: "TH", subdivisionName: "Chai Nat", code: "TH-18" }, + "TH-36": { countryCode: "TH", subdivisionName: "Chaiyaphum", code: "TH-36" }, + "TH-22": { countryCode: "TH", subdivisionName: "Chanthaburi", code: "TH-22" }, + "TH-50": { countryCode: "TH", subdivisionName: "Chiang Mai", code: "TH-50" }, + "TH-57": { countryCode: "TH", subdivisionName: "Chiang Rai", code: "TH-57" }, + "TH-20": { countryCode: "TH", subdivisionName: "Chon Buri", code: "TH-20" }, + "TH-86": { countryCode: "TH", subdivisionName: "Chumphon", code: "TH-86" }, + "TH-46": { countryCode: "TH", subdivisionName: "Kalasin", code: "TH-46" }, + "TH-62": { + countryCode: "TH", + subdivisionName: "Kamphaeng Phet", + code: "TH-62", + }, + "TH-71": { + countryCode: "TH", + subdivisionName: "Kanchanaburi", + code: "TH-71", + }, + "TH-40": { countryCode: "TH", subdivisionName: "Khon Kaen", code: "TH-40" }, + "TH-81": { countryCode: "TH", subdivisionName: "Krabi", code: "TH-81" }, + "TH-10": { + countryCode: "TH", + subdivisionName: "Krung Thep Maha Nakhon", + code: "TH-10", + }, + "TH-52": { countryCode: "TH", subdivisionName: "Lampang", code: "TH-52" }, + "TH-51": { countryCode: "TH", subdivisionName: "Lamphun", code: "TH-51" }, + "TH-42": { countryCode: "TH", subdivisionName: "Loei", code: "TH-42" }, + "TH-16": { countryCode: "TH", subdivisionName: "Lop Buri", code: "TH-16" }, + "TH-58": { + countryCode: "TH", + subdivisionName: "Mae Hong Son", + code: "TH-58", + }, + "TH-44": { + countryCode: "TH", + subdivisionName: "Maha Sarakham", + code: "TH-44", + }, + "TH-49": { countryCode: "TH", subdivisionName: "Mukdahan", code: "TH-49" }, + "TH-26": { + countryCode: "TH", + subdivisionName: "Nakhon Nayok", + code: "TH-26", + }, + "TH-73": { + countryCode: "TH", + subdivisionName: "Nakhon Pathom", + code: "TH-73", + }, + "TH-48": { + countryCode: "TH", + subdivisionName: "Nakhon Phanom", + code: "TH-48", + }, + "TH-30": { + countryCode: "TH", + subdivisionName: "Nakhon Ratchasima", + code: "TH-30", + }, + "TH-60": { + countryCode: "TH", + subdivisionName: "Nakhon Sawan", + code: "TH-60", + }, + "TH-80": { + countryCode: "TH", + subdivisionName: "Nakhon Si Thammarat", + code: "TH-80", + }, + "TH-55": { countryCode: "TH", subdivisionName: "Nan", code: "TH-55" }, + "TH-96": { countryCode: "TH", subdivisionName: "Narathiwat", code: "TH-96" }, + "TH-39": { + countryCode: "TH", + subdivisionName: "Nong Bua Lam Phu", + code: "TH-39", + }, + "TH-43": { countryCode: "TH", subdivisionName: "Nong Khai", code: "TH-43" }, + "TH-12": { countryCode: "TH", subdivisionName: "Nonthaburi", code: "TH-12" }, + "TH-13": { + countryCode: "TH", + subdivisionName: "Pathum Thani", + code: "TH-13", + }, + "TH-94": { countryCode: "TH", subdivisionName: "Pattani", code: "TH-94" }, + "TH-82": { countryCode: "TH", subdivisionName: "Phangnga", code: "TH-82" }, + "TH-93": { countryCode: "TH", subdivisionName: "Phatthalung", code: "TH-93" }, + "TH-56": { countryCode: "TH", subdivisionName: "Phayao", code: "TH-56" }, + "TH-67": { countryCode: "TH", subdivisionName: "Phetchabun", code: "TH-67" }, + "TH-76": { countryCode: "TH", subdivisionName: "Phetchaburi", code: "TH-76" }, + "TH-66": { countryCode: "TH", subdivisionName: "Phichit", code: "TH-66" }, + "TH-65": { countryCode: "TH", subdivisionName: "Phitsanulok", code: "TH-65" }, + "TH-14": { + countryCode: "TH", + subdivisionName: "Phra Nakhon Si Ayutthaya", + code: "TH-14", + }, + "TH-54": { countryCode: "TH", subdivisionName: "Phrae", code: "TH-54" }, + "TH-83": { countryCode: "TH", subdivisionName: "Phuket", code: "TH-83" }, + "TH-25": { + countryCode: "TH", + subdivisionName: "Prachin Buri", + code: "TH-25", + }, + "TH-77": { + countryCode: "TH", + subdivisionName: "Prachuap Khiri Khan", + code: "TH-77", + }, + "TH-85": { countryCode: "TH", subdivisionName: "Ranong", code: "TH-85" }, + "TH-70": { countryCode: "TH", subdivisionName: "Ratchaburi", code: "TH-70" }, + "TH-21": { countryCode: "TH", subdivisionName: "Rayong", code: "TH-21" }, + "TH-45": { countryCode: "TH", subdivisionName: "Roi Et", code: "TH-45" }, + "TH-27": { countryCode: "TH", subdivisionName: "Sa Kaeo", code: "TH-27" }, + "TH-47": { + countryCode: "TH", + subdivisionName: "Sakon Nakhon", + code: "TH-47", + }, + "TH-11": { + countryCode: "TH", + subdivisionName: "Samut Prakan", + code: "TH-11", + }, + "TH-74": { + countryCode: "TH", + subdivisionName: "Samut Sakhon", + code: "TH-74", + }, + "TH-75": { + countryCode: "TH", + subdivisionName: "Samut Songkhram", + code: "TH-75", + }, + "TH-19": { countryCode: "TH", subdivisionName: "Saraburi", code: "TH-19" }, + "TH-91": { countryCode: "TH", subdivisionName: "Satun", code: "TH-91" }, + "TH-33": { countryCode: "TH", subdivisionName: "Si Sa Ket", code: "TH-33" }, + "TH-17": { countryCode: "TH", subdivisionName: "Sing Buri", code: "TH-17" }, + "TH-90": { countryCode: "TH", subdivisionName: "Songkhla", code: "TH-90" }, + "TH-64": { countryCode: "TH", subdivisionName: "Sukhothai", code: "TH-64" }, + "TH-72": { countryCode: "TH", subdivisionName: "Suphan Buri", code: "TH-72" }, + "TH-84": { countryCode: "TH", subdivisionName: "Surat Thani", code: "TH-84" }, + "TH-32": { countryCode: "TH", subdivisionName: "Surin", code: "TH-32" }, + "TH-63": { countryCode: "TH", subdivisionName: "Tak", code: "TH-63" }, + "TH-92": { countryCode: "TH", subdivisionName: "Trang", code: "TH-92" }, + "TH-23": { countryCode: "TH", subdivisionName: "Trat", code: "TH-23" }, + "TH-34": { + countryCode: "TH", + subdivisionName: "Ubon Ratchathani", + code: "TH-34", + }, + "TH-41": { countryCode: "TH", subdivisionName: "Udon Thani", code: "TH-41" }, + "TH-61": { countryCode: "TH", subdivisionName: "Uthai Thani", code: "TH-61" }, + "TH-53": { countryCode: "TH", subdivisionName: "Uttaradit", code: "TH-53" }, + "TH-95": { countryCode: "TH", subdivisionName: "Yala", code: "TH-95" }, + "TH-35": { countryCode: "TH", subdivisionName: "Yasothon", code: "TH-35" }, + "TJ-DU": { countryCode: "TJ", subdivisionName: "Dushanbe", code: "TJ-DU" }, + "TJ-KT": { countryCode: "TJ", subdivisionName: "Khatlon", code: "TJ-KT" }, + "TJ-GB": { + countryCode: "TJ", + subdivisionName: "Kuhistoni Badakhshon", + code: "TJ-GB", + }, + "TJ-RA": { + countryCode: "TJ", + subdivisionName: "Nohiyahoi Tobei Jumhuri", + code: "TJ-RA", + }, + "TJ-SU": { countryCode: "TJ", subdivisionName: "Sughd", code: "TJ-SU" }, + "TL-AL": { countryCode: "TL", subdivisionName: "Aileu", code: "TL-AL" }, + "TL-AN": { countryCode: "TL", subdivisionName: "Ainaro", code: "TL-AN" }, + "TL-CO": { countryCode: "TL", subdivisionName: "Cova Lima", code: "TL-CO" }, + "TL-DI": { countryCode: "TL", subdivisionName: "Dili", code: "TL-DI" }, + "TL-LI": { countryCode: "TL", subdivisionName: "Liquica", code: "TL-LI" }, + "TM-A": { countryCode: "TM", subdivisionName: "Ahal", code: "TM-A" }, + "TM-B": { countryCode: "TM", subdivisionName: "Balkan", code: "TM-B" }, + "TM-D": { countryCode: "TM", subdivisionName: "Dasoguz", code: "TM-D" }, + "TM-L": { countryCode: "TM", subdivisionName: "Lebap", code: "TM-L" }, + "TM-M": { countryCode: "TM", subdivisionName: "Mary", code: "TM-M" }, + "TN-31": { countryCode: "TN", subdivisionName: "Beja", code: "TN-31" }, + "TN-13": { countryCode: "TN", subdivisionName: "Ben Arous", code: "TN-13" }, + "TN-23": { countryCode: "TN", subdivisionName: "Bizerte", code: "TN-23" }, + "TN-81": { countryCode: "TN", subdivisionName: "Gabes", code: "TN-81" }, + "TN-71": { countryCode: "TN", subdivisionName: "Gafsa", code: "TN-71" }, + "TN-32": { countryCode: "TN", subdivisionName: "Jendouba", code: "TN-32" }, + "TN-41": { countryCode: "TN", subdivisionName: "Kairouan", code: "TN-41" }, + "TN-42": { countryCode: "TN", subdivisionName: "Kasserine", code: "TN-42" }, + "TN-73": { countryCode: "TN", subdivisionName: "Kebili", code: "TN-73" }, + "TN-12": { countryCode: "TN", subdivisionName: "L'Ariana", code: "TN-12" }, + "TN-14": { countryCode: "TN", subdivisionName: "La Manouba", code: "TN-14" }, + "TN-33": { countryCode: "TN", subdivisionName: "Le Kef", code: "TN-33" }, + "TN-53": { countryCode: "TN", subdivisionName: "Mahdia", code: "TN-53" }, + "TN-82": { countryCode: "TN", subdivisionName: "Medenine", code: "TN-82" }, + "TN-52": { countryCode: "TN", subdivisionName: "Monastir", code: "TN-52" }, + "TN-21": { countryCode: "TN", subdivisionName: "Nabeul", code: "TN-21" }, + "TN-61": { countryCode: "TN", subdivisionName: "Sfax", code: "TN-61" }, + "TN-43": { countryCode: "TN", subdivisionName: "Sidi Bouzid", code: "TN-43" }, + "TN-34": { countryCode: "TN", subdivisionName: "Siliana", code: "TN-34" }, + "TN-51": { countryCode: "TN", subdivisionName: "Sousse", code: "TN-51" }, + "TN-83": { countryCode: "TN", subdivisionName: "Tataouine", code: "TN-83" }, + "TN-72": { countryCode: "TN", subdivisionName: "Tozeur", code: "TN-72" }, + "TN-11": { countryCode: "TN", subdivisionName: "Tunis", code: "TN-11" }, + "TN-22": { countryCode: "TN", subdivisionName: "Zaghouan", code: "TN-22" }, + "TO-01": { countryCode: "TO", subdivisionName: "'Eua", code: "TO-01" }, + "TO-02": { countryCode: "TO", subdivisionName: "Ha'apai", code: "TO-02" }, + "TO-03": { countryCode: "TO", subdivisionName: "Niuas", code: "TO-03" }, + "TO-04": { countryCode: "TO", subdivisionName: "Tongatapu", code: "TO-04" }, + "TO-05": { countryCode: "TO", subdivisionName: "Vava'u", code: "TO-05" }, + "TR-01": { countryCode: "TR", subdivisionName: "Adana", code: "TR-01" }, + "TR-02": { countryCode: "TR", subdivisionName: "Adiyaman", code: "TR-02" }, + "TR-03": { + countryCode: "TR", + subdivisionName: "Afyonkarahisar", + code: "TR-03", + }, + "TR-04": { countryCode: "TR", subdivisionName: "Agri", code: "TR-04" }, + "TR-68": { countryCode: "TR", subdivisionName: "Aksaray", code: "TR-68" }, + "TR-05": { countryCode: "TR", subdivisionName: "Amasya", code: "TR-05" }, + "TR-06": { countryCode: "TR", subdivisionName: "Ankara", code: "TR-06" }, + "TR-07": { countryCode: "TR", subdivisionName: "Antalya", code: "TR-07" }, + "TR-75": { countryCode: "TR", subdivisionName: "Ardahan", code: "TR-75" }, + "TR-08": { countryCode: "TR", subdivisionName: "Artvin", code: "TR-08" }, + "TR-09": { countryCode: "TR", subdivisionName: "Aydin", code: "TR-09" }, + "TR-10": { countryCode: "TR", subdivisionName: "Balikesir", code: "TR-10" }, + "TR-74": { countryCode: "TR", subdivisionName: "Bartin", code: "TR-74" }, + "TR-72": { countryCode: "TR", subdivisionName: "Batman", code: "TR-72" }, + "TR-69": { countryCode: "TR", subdivisionName: "Bayburt", code: "TR-69" }, + "TR-11": { countryCode: "TR", subdivisionName: "Bilecik", code: "TR-11" }, + "TR-12": { countryCode: "TR", subdivisionName: "Bingol", code: "TR-12" }, + "TR-13": { countryCode: "TR", subdivisionName: "Bitlis", code: "TR-13" }, + "TR-14": { countryCode: "TR", subdivisionName: "Bolu", code: "TR-14" }, + "TR-15": { countryCode: "TR", subdivisionName: "Burdur", code: "TR-15" }, + "TR-16": { countryCode: "TR", subdivisionName: "Bursa", code: "TR-16" }, + "TR-17": { countryCode: "TR", subdivisionName: "Canakkale", code: "TR-17" }, + "TR-18": { countryCode: "TR", subdivisionName: "Cankiri", code: "TR-18" }, + "TR-19": { countryCode: "TR", subdivisionName: "Corum", code: "TR-19" }, + "TR-20": { countryCode: "TR", subdivisionName: "Denizli", code: "TR-20" }, + "TR-21": { countryCode: "TR", subdivisionName: "Diyarbakir", code: "TR-21" }, + "TR-81": { countryCode: "TR", subdivisionName: "Duzce", code: "TR-81" }, + "TR-22": { countryCode: "TR", subdivisionName: "Edirne", code: "TR-22" }, + "TR-23": { countryCode: "TR", subdivisionName: "Elazig", code: "TR-23" }, + "TR-24": { countryCode: "TR", subdivisionName: "Erzincan", code: "TR-24" }, + "TR-25": { countryCode: "TR", subdivisionName: "Erzurum", code: "TR-25" }, + "TR-26": { countryCode: "TR", subdivisionName: "Eskisehir", code: "TR-26" }, + "TR-27": { countryCode: "TR", subdivisionName: "Gaziantep", code: "TR-27" }, + "TR-28": { countryCode: "TR", subdivisionName: "Giresun", code: "TR-28" }, + "TR-29": { countryCode: "TR", subdivisionName: "Gumushane", code: "TR-29" }, + "TR-30": { countryCode: "TR", subdivisionName: "Hakkari", code: "TR-30" }, + "TR-31": { countryCode: "TR", subdivisionName: "Hatay", code: "TR-31" }, + "TR-76": { countryCode: "TR", subdivisionName: "Igdir", code: "TR-76" }, + "TR-32": { countryCode: "TR", subdivisionName: "Isparta", code: "TR-32" }, + "TR-34": { countryCode: "TR", subdivisionName: "Istanbul", code: "TR-34" }, + "TR-35": { countryCode: "TR", subdivisionName: "Izmir", code: "TR-35" }, + "TR-46": { + countryCode: "TR", + subdivisionName: "Kahramanmaras", + code: "TR-46", + }, + "TR-78": { countryCode: "TR", subdivisionName: "Karabuk", code: "TR-78" }, + "TR-70": { countryCode: "TR", subdivisionName: "Karaman", code: "TR-70" }, + "TR-36": { countryCode: "TR", subdivisionName: "Kars", code: "TR-36" }, + "TR-37": { countryCode: "TR", subdivisionName: "Kastamonu", code: "TR-37" }, + "TR-38": { countryCode: "TR", subdivisionName: "Kayseri", code: "TR-38" }, + "TR-79": { countryCode: "TR", subdivisionName: "Kilis", code: "TR-79" }, + "TR-71": { countryCode: "TR", subdivisionName: "Kirikkale", code: "TR-71" }, + "TR-39": { countryCode: "TR", subdivisionName: "Kirklareli", code: "TR-39" }, + "TR-40": { countryCode: "TR", subdivisionName: "Kirsehir", code: "TR-40" }, + "TR-41": { countryCode: "TR", subdivisionName: "Kocaeli", code: "TR-41" }, + "TR-42": { countryCode: "TR", subdivisionName: "Konya", code: "TR-42" }, + "TR-43": { countryCode: "TR", subdivisionName: "Kutahya", code: "TR-43" }, + "TR-44": { countryCode: "TR", subdivisionName: "Malatya", code: "TR-44" }, + "TR-45": { countryCode: "TR", subdivisionName: "Manisa", code: "TR-45" }, + "TR-47": { countryCode: "TR", subdivisionName: "Mardin", code: "TR-47" }, + "TR-33": { countryCode: "TR", subdivisionName: "Mersin", code: "TR-33" }, + "TR-48": { countryCode: "TR", subdivisionName: "Mugla", code: "TR-48" }, + "TR-49": { countryCode: "TR", subdivisionName: "Mus", code: "TR-49" }, + "TR-50": { countryCode: "TR", subdivisionName: "Nevsehir", code: "TR-50" }, + "TR-51": { countryCode: "TR", subdivisionName: "Nigde", code: "TR-51" }, + "TR-52": { countryCode: "TR", subdivisionName: "Ordu", code: "TR-52" }, + "TR-80": { countryCode: "TR", subdivisionName: "Osmaniye", code: "TR-80" }, + "TR-53": { countryCode: "TR", subdivisionName: "Rize", code: "TR-53" }, + "TR-54": { countryCode: "TR", subdivisionName: "Sakarya", code: "TR-54" }, + "TR-55": { countryCode: "TR", subdivisionName: "Samsun", code: "TR-55" }, + "TR-63": { countryCode: "TR", subdivisionName: "Sanliurfa", code: "TR-63" }, + "TR-56": { countryCode: "TR", subdivisionName: "Siirt", code: "TR-56" }, + "TR-57": { countryCode: "TR", subdivisionName: "Sinop", code: "TR-57" }, + "TR-73": { countryCode: "TR", subdivisionName: "Sirnak", code: "TR-73" }, + "TR-58": { countryCode: "TR", subdivisionName: "Sivas", code: "TR-58" }, + "TR-59": { countryCode: "TR", subdivisionName: "Tekirdag", code: "TR-59" }, + "TR-60": { countryCode: "TR", subdivisionName: "Tokat", code: "TR-60" }, + "TR-61": { countryCode: "TR", subdivisionName: "Trabzon", code: "TR-61" }, + "TR-62": { countryCode: "TR", subdivisionName: "Tunceli", code: "TR-62" }, + "TR-64": { countryCode: "TR", subdivisionName: "Usak", code: "TR-64" }, + "TR-65": { countryCode: "TR", subdivisionName: "Van", code: "TR-65" }, + "TR-77": { countryCode: "TR", subdivisionName: "Yalova", code: "TR-77" }, + "TR-66": { countryCode: "TR", subdivisionName: "Yozgat", code: "TR-66" }, + "TR-67": { countryCode: "TR", subdivisionName: "Zonguldak", code: "TR-67" }, + "TT-ARI": { countryCode: "TT", subdivisionName: "Arima", code: "TT-ARI" }, + "TT-CHA": { countryCode: "TT", subdivisionName: "Chaguanas", code: "TT-CHA" }, + "TT-CTT": { + countryCode: "TT", + subdivisionName: "Couva-Tabaquite-Talparo", + code: "TT-CTT", + }, + "TT-DMN": { + countryCode: "TT", + subdivisionName: "Diego Martin", + code: "TT-DMN", + }, + "TT-MRC": { + countryCode: "TT", + subdivisionName: "Mayaro-Rio Claro", + code: "TT-MRC", + }, + "TT-PED": { + countryCode: "TT", + subdivisionName: "Penal-Debe", + code: "TT-PED", + }, + "TT-PTF": { + countryCode: "TT", + subdivisionName: "Point Fortin", + code: "TT-PTF", + }, + "TT-POS": { + countryCode: "TT", + subdivisionName: "Port of Spain", + code: "TT-POS", + }, + "TT-PRT": { + countryCode: "TT", + subdivisionName: "Princes Town", + code: "TT-PRT", + }, + "TT-SFO": { + countryCode: "TT", + subdivisionName: "San Fernando", + code: "TT-SFO", + }, + "TT-SJL": { + countryCode: "TT", + subdivisionName: "San Juan-Laventille", + code: "TT-SJL", + }, + "TT-SGE": { + countryCode: "TT", + subdivisionName: "Sangre Grande", + code: "TT-SGE", + }, + "TT-SIP": { countryCode: "TT", subdivisionName: "Siparia", code: "TT-SIP" }, + "TT-TOB": { countryCode: "TT", subdivisionName: "Tobago", code: "TT-TOB" }, + "TT-TUP": { + countryCode: "TT", + subdivisionName: "Tunapuna-Piarco", + code: "TT-TUP", + }, + "TV-FUN": { countryCode: "TV", subdivisionName: "Funafuti", code: "TV-FUN" }, + "TW-CHA": { countryCode: "TW", subdivisionName: "Changhua", code: "TW-CHA" }, + "TW-CYQ": { countryCode: "TW", subdivisionName: "Chiayi", code: "TW-CYQ" }, + "TW-HSQ": { countryCode: "TW", subdivisionName: "Hsinchu", code: "TW-HSQ" }, + "TW-HUA": { countryCode: "TW", subdivisionName: "Hualien", code: "TW-HUA" }, + "TW-KHH": { countryCode: "TW", subdivisionName: "Kaohsiung", code: "TW-KHH" }, + "TW-KEE": { countryCode: "TW", subdivisionName: "Keelung", code: "TW-KEE" }, + "TW-KIN": { countryCode: "TW", subdivisionName: "Kinmen", code: "TW-KIN" }, + "TW-LIE": { + countryCode: "TW", + subdivisionName: "Lienchiang", + code: "TW-LIE", + }, + "TW-MIA": { countryCode: "TW", subdivisionName: "Miaoli", code: "TW-MIA" }, + "TW-NAN": { countryCode: "TW", subdivisionName: "Nantou", code: "TW-NAN" }, + "TW-NWT": { + countryCode: "TW", + subdivisionName: "New Taipei", + code: "TW-NWT", + }, + "TW-PEN": { countryCode: "TW", subdivisionName: "Penghu", code: "TW-PEN" }, + "TW-PIF": { countryCode: "TW", subdivisionName: "Pingtung", code: "TW-PIF" }, + "TW-TXG": { countryCode: "TW", subdivisionName: "Taichung", code: "TW-TXG" }, + "TW-TNN": { countryCode: "TW", subdivisionName: "Tainan", code: "TW-TNN" }, + "TW-TPE": { countryCode: "TW", subdivisionName: "Taipei", code: "TW-TPE" }, + "TW-TTT": { countryCode: "TW", subdivisionName: "Taitung", code: "TW-TTT" }, + "TW-TAO": { countryCode: "TW", subdivisionName: "Taoyuan", code: "TW-TAO" }, + "TW-ILA": { countryCode: "TW", subdivisionName: "Yilan", code: "TW-ILA" }, + "TW-YUN": { countryCode: "TW", subdivisionName: "Yunlin", code: "TW-YUN" }, + "TZ-01": { countryCode: "TZ", subdivisionName: "Arusha", code: "TZ-01" }, + "TZ-02": { + countryCode: "TZ", + subdivisionName: "Dar es Salaam", + code: "TZ-02", + }, + "TZ-03": { countryCode: "TZ", subdivisionName: "Dodoma", code: "TZ-03" }, + "TZ-27": { countryCode: "TZ", subdivisionName: "Geita", code: "TZ-27" }, + "TZ-04": { countryCode: "TZ", subdivisionName: "Iringa", code: "TZ-04" }, + "TZ-05": { countryCode: "TZ", subdivisionName: "Kagera", code: "TZ-05" }, + "TZ-06": { + countryCode: "TZ", + subdivisionName: "Kaskazini Pemba", + code: "TZ-06", + }, + "TZ-07": { + countryCode: "TZ", + subdivisionName: "Kaskazini Unguja", + code: "TZ-07", + }, + "TZ-28": { countryCode: "TZ", subdivisionName: "Katavi", code: "TZ-28" }, + "TZ-08": { countryCode: "TZ", subdivisionName: "Kigoma", code: "TZ-08" }, + "TZ-09": { countryCode: "TZ", subdivisionName: "Kilimanjaro", code: "TZ-09" }, + "TZ-10": { + countryCode: "TZ", + subdivisionName: "Kusini Pemba", + code: "TZ-10", + }, + "TZ-11": { + countryCode: "TZ", + subdivisionName: "Kusini Unguja", + code: "TZ-11", + }, + "TZ-12": { countryCode: "TZ", subdivisionName: "Lindi", code: "TZ-12" }, + "TZ-26": { countryCode: "TZ", subdivisionName: "Manyara", code: "TZ-26" }, + "TZ-13": { countryCode: "TZ", subdivisionName: "Mara", code: "TZ-13" }, + "TZ-14": { countryCode: "TZ", subdivisionName: "Mbeya", code: "TZ-14" }, + "TZ-15": { + countryCode: "TZ", + subdivisionName: "Mjini Magharibi", + code: "TZ-15", + }, + "TZ-16": { countryCode: "TZ", subdivisionName: "Morogoro", code: "TZ-16" }, + "TZ-17": { countryCode: "TZ", subdivisionName: "Mtwara", code: "TZ-17" }, + "TZ-18": { countryCode: "TZ", subdivisionName: "Mwanza", code: "TZ-18" }, + "TZ-29": { countryCode: "TZ", subdivisionName: "Njombe", code: "TZ-29" }, + "TZ-19": { countryCode: "TZ", subdivisionName: "Pwani", code: "TZ-19" }, + "TZ-20": { countryCode: "TZ", subdivisionName: "Rukwa", code: "TZ-20" }, + "TZ-21": { countryCode: "TZ", subdivisionName: "Ruvuma", code: "TZ-21" }, + "TZ-22": { countryCode: "TZ", subdivisionName: "Shinyanga", code: "TZ-22" }, + "TZ-30": { countryCode: "TZ", subdivisionName: "Simiyu", code: "TZ-30" }, + "TZ-23": { countryCode: "TZ", subdivisionName: "Singida", code: "TZ-23" }, + "TZ-31": { countryCode: "TZ", subdivisionName: "Songwe", code: "TZ-31" }, + "TZ-24": { countryCode: "TZ", subdivisionName: "Tabora", code: "TZ-24" }, + "TZ-25": { countryCode: "TZ", subdivisionName: "Tanga", code: "TZ-25" }, + "UA-43": { + countryCode: "UA", + subdivisionName: "Avtonomna Respublika Krym", + code: "UA-43", + }, + "UA-71": { + countryCode: "UA", + subdivisionName: "Cherkaska oblast", + code: "UA-71", + }, + "UA-74": { + countryCode: "UA", + subdivisionName: "Chernihivska oblast", + code: "UA-74", + }, + "UA-77": { + countryCode: "UA", + subdivisionName: "Chernivetska oblast", + code: "UA-77", + }, + "UA-12": { + countryCode: "UA", + subdivisionName: "Dnipropetrovska oblast", + code: "UA-12", + }, + "UA-14": { + countryCode: "UA", + subdivisionName: "Donetska oblast", + code: "UA-14", + }, + "UA-26": { + countryCode: "UA", + subdivisionName: "Ivano-Frankivska oblast", + code: "UA-26", + }, + "UA-63": { + countryCode: "UA", + subdivisionName: "Kharkivska oblast", + code: "UA-63", + }, + "UA-65": { + countryCode: "UA", + subdivisionName: "Khersonska oblast", + code: "UA-65", + }, + "UA-68": { + countryCode: "UA", + subdivisionName: "Khmelnytska oblast", + code: "UA-68", + }, + "UA-35": { + countryCode: "UA", + subdivisionName: "Kirovohradska oblast", + code: "UA-35", + }, + "UA-30": { countryCode: "UA", subdivisionName: "Kyiv", code: "UA-30" }, + "UA-32": { + countryCode: "UA", + subdivisionName: "Kyivska oblast", + code: "UA-32", + }, + "UA-09": { + countryCode: "UA", + subdivisionName: "Luhanska oblast", + code: "UA-09", + }, + "UA-46": { + countryCode: "UA", + subdivisionName: "Lvivska oblast", + code: "UA-46", + }, + "UA-48": { + countryCode: "UA", + subdivisionName: "Mykolaivska oblast", + code: "UA-48", + }, + "UA-51": { + countryCode: "UA", + subdivisionName: "Odeska oblast", + code: "UA-51", + }, + "UA-53": { + countryCode: "UA", + subdivisionName: "Poltavska oblast", + code: "UA-53", + }, + "UA-56": { + countryCode: "UA", + subdivisionName: "Rivnenska oblast", + code: "UA-56", + }, + "UA-40": { countryCode: "UA", subdivisionName: "Sevastopol", code: "UA-40" }, + "UA-59": { + countryCode: "UA", + subdivisionName: "Sumska oblast", + code: "UA-59", + }, + "UA-61": { + countryCode: "UA", + subdivisionName: "Ternopilska oblast", + code: "UA-61", + }, + "UA-05": { + countryCode: "UA", + subdivisionName: "Vinnytska oblast", + code: "UA-05", + }, + "UA-07": { + countryCode: "UA", + subdivisionName: "Volynska oblast", + code: "UA-07", + }, + "UA-21": { + countryCode: "UA", + subdivisionName: "Zakarpatska oblast", + code: "UA-21", + }, + "UA-23": { + countryCode: "UA", + subdivisionName: "Zaporizka oblast", + code: "UA-23", + }, + "UA-18": { + countryCode: "UA", + subdivisionName: "Zhytomyrska oblast", + code: "UA-18", + }, + "UG-314": { countryCode: "UG", subdivisionName: "Abim", code: "UG-314" }, + "UG-301": { countryCode: "UG", subdivisionName: "Adjumani", code: "UG-301" }, + "UG-322": { countryCode: "UG", subdivisionName: "Agago", code: "UG-322" }, + "UG-323": { countryCode: "UG", subdivisionName: "Alebtong", code: "UG-323" }, + "UG-302": { countryCode: "UG", subdivisionName: "Apac", code: "UG-302" }, + "UG-303": { countryCode: "UG", subdivisionName: "Arua", code: "UG-303" }, + "UG-218": { countryCode: "UG", subdivisionName: "Bududa", code: "UG-218" }, + "UG-201": { countryCode: "UG", subdivisionName: "Bugiri", code: "UG-201" }, + "UG-420": { countryCode: "UG", subdivisionName: "Buhweju", code: "UG-420" }, + "UG-117": { countryCode: "UG", subdivisionName: "Buikwe", code: "UG-117" }, + "UG-219": { countryCode: "UG", subdivisionName: "Bukedea", code: "UG-219" }, + "UG-118": { + countryCode: "UG", + subdivisionName: "Bukomansibi", + code: "UG-118", + }, + "UG-225": { countryCode: "UG", subdivisionName: "Bulambuli", code: "UG-225" }, + "UG-401": { + countryCode: "UG", + subdivisionName: "Bundibugyo", + code: "UG-401", + }, + "UG-402": { countryCode: "UG", subdivisionName: "Bushenyi", code: "UG-402" }, + "UG-202": { countryCode: "UG", subdivisionName: "Busia", code: "UG-202" }, + "UG-120": { countryCode: "UG", subdivisionName: "Buvuma", code: "UG-120" }, + "UG-226": { countryCode: "UG", subdivisionName: "Buyende", code: "UG-226" }, + "UG-121": { countryCode: "UG", subdivisionName: "Gomba", code: "UG-121" }, + "UG-304": { countryCode: "UG", subdivisionName: "Gulu", code: "UG-304" }, + "UG-403": { countryCode: "UG", subdivisionName: "Hoima", code: "UG-403" }, + "UG-417": { countryCode: "UG", subdivisionName: "Ibanda", code: "UG-417" }, + "UG-203": { countryCode: "UG", subdivisionName: "Iganga", code: "UG-203" }, + "UG-418": { countryCode: "UG", subdivisionName: "Isingiro", code: "UG-418" }, + "UG-204": { countryCode: "UG", subdivisionName: "Jinja", code: "UG-204" }, + "UG-318": { countryCode: "UG", subdivisionName: "Kaabong", code: "UG-318" }, + "UG-404": { countryCode: "UG", subdivisionName: "Kabale", code: "UG-404" }, + "UG-405": { countryCode: "UG", subdivisionName: "Kabarole", code: "UG-405" }, + "UG-213": { + countryCode: "UG", + subdivisionName: "Kaberamaido", + code: "UG-213", + }, + "UG-101": { countryCode: "UG", subdivisionName: "Kalangala", code: "UG-101" }, + "UG-222": { countryCode: "UG", subdivisionName: "Kaliro", code: "UG-222" }, + "UG-122": { countryCode: "UG", subdivisionName: "Kalungu", code: "UG-122" }, + "UG-102": { countryCode: "UG", subdivisionName: "Kampala", code: "UG-102" }, + "UG-205": { countryCode: "UG", subdivisionName: "Kamuli", code: "UG-205" }, + "UG-413": { countryCode: "UG", subdivisionName: "Kamwenge", code: "UG-413" }, + "UG-414": { countryCode: "UG", subdivisionName: "Kanungu", code: "UG-414" }, + "UG-206": { countryCode: "UG", subdivisionName: "Kapchorwa", code: "UG-206" }, + "UG-406": { countryCode: "UG", subdivisionName: "Kasese", code: "UG-406" }, + "UG-207": { countryCode: "UG", subdivisionName: "Katakwi", code: "UG-207" }, + "UG-112": { countryCode: "UG", subdivisionName: "Kayunga", code: "UG-112" }, + "UG-407": { countryCode: "UG", subdivisionName: "Kibaale", code: "UG-407" }, + "UG-103": { countryCode: "UG", subdivisionName: "Kiboga", code: "UG-103" }, + "UG-227": { countryCode: "UG", subdivisionName: "Kibuku", code: "UG-227" }, + "UG-419": { countryCode: "UG", subdivisionName: "Kiruhura", code: "UG-419" }, + "UG-421": { + countryCode: "UG", + subdivisionName: "Kiryandongo", + code: "UG-421", + }, + "UG-408": { countryCode: "UG", subdivisionName: "Kisoro", code: "UG-408" }, + "UG-305": { countryCode: "UG", subdivisionName: "Kitgum", code: "UG-305" }, + "UG-319": { countryCode: "UG", subdivisionName: "Koboko", code: "UG-319" }, + "UG-325": { countryCode: "UG", subdivisionName: "Kole", code: "UG-325" }, + "UG-228": { countryCode: "UG", subdivisionName: "Kween", code: "UG-228" }, + "UG-123": { + countryCode: "UG", + subdivisionName: "Kyankwanzi", + code: "UG-123", + }, + "UG-422": { countryCode: "UG", subdivisionName: "Kyegegwa", code: "UG-422" }, + "UG-415": { countryCode: "UG", subdivisionName: "Kyenjojo", code: "UG-415" }, + "UG-326": { countryCode: "UG", subdivisionName: "Lamwo", code: "UG-326" }, + "UG-307": { countryCode: "UG", subdivisionName: "Lira", code: "UG-307" }, + "UG-229": { countryCode: "UG", subdivisionName: "Luuka", code: "UG-229" }, + "UG-104": { countryCode: "UG", subdivisionName: "Luwero", code: "UG-104" }, + "UG-124": { countryCode: "UG", subdivisionName: "Lwengo", code: "UG-124" }, + "UG-114": { countryCode: "UG", subdivisionName: "Lyantonde", code: "UG-114" }, + "UG-223": { countryCode: "UG", subdivisionName: "Manafwa", code: "UG-223" }, + "UG-320": { countryCode: "UG", subdivisionName: "Maracha", code: "UG-320" }, + "UG-105": { countryCode: "UG", subdivisionName: "Masaka", code: "UG-105" }, + "UG-409": { countryCode: "UG", subdivisionName: "Masindi", code: "UG-409" }, + "UG-214": { countryCode: "UG", subdivisionName: "Mayuge", code: "UG-214" }, + "UG-209": { countryCode: "UG", subdivisionName: "Mbale", code: "UG-209" }, + "UG-410": { countryCode: "UG", subdivisionName: "Mbarara", code: "UG-410" }, + "UG-423": { countryCode: "UG", subdivisionName: "Mitooma", code: "UG-423" }, + "UG-115": { countryCode: "UG", subdivisionName: "Mityana", code: "UG-115" }, + "UG-308": { countryCode: "UG", subdivisionName: "Moroto", code: "UG-308" }, + "UG-309": { countryCode: "UG", subdivisionName: "Moyo", code: "UG-309" }, + "UG-106": { countryCode: "UG", subdivisionName: "Mpigi", code: "UG-106" }, + "UG-107": { countryCode: "UG", subdivisionName: "Mubende", code: "UG-107" }, + "UG-108": { countryCode: "UG", subdivisionName: "Mukono", code: "UG-108" }, + "UG-311": { + countryCode: "UG", + subdivisionName: "Nakapiripirit", + code: "UG-311", + }, + "UG-116": { countryCode: "UG", subdivisionName: "Nakaseke", code: "UG-116" }, + "UG-109": { + countryCode: "UG", + subdivisionName: "Nakasongola", + code: "UG-109", + }, + "UG-230": { countryCode: "UG", subdivisionName: "Namayingo", code: "UG-230" }, + "UG-327": { countryCode: "UG", subdivisionName: "Napak", code: "UG-327" }, + "UG-310": { countryCode: "UG", subdivisionName: "Nebbi", code: "UG-310" }, + "UG-424": { countryCode: "UG", subdivisionName: "Ntoroko", code: "UG-424" }, + "UG-411": { countryCode: "UG", subdivisionName: "Ntungamo", code: "UG-411" }, + "UG-328": { countryCode: "UG", subdivisionName: "Nwoya", code: "UG-328" }, + "UG-321": { countryCode: "UG", subdivisionName: "Oyam", code: "UG-321" }, + "UG-312": { countryCode: "UG", subdivisionName: "Pader", code: "UG-312" }, + "UG-110": { countryCode: "UG", subdivisionName: "Rakai", code: "UG-110" }, + "UG-425": { countryCode: "UG", subdivisionName: "Rubirizi", code: "UG-425" }, + "UG-412": { countryCode: "UG", subdivisionName: "Rukungiri", code: "UG-412" }, + "UG-111": { countryCode: "UG", subdivisionName: "Sembabule", code: "UG-111" }, + "UG-426": { countryCode: "UG", subdivisionName: "Sheema", code: "UG-426" }, + "UG-215": { countryCode: "UG", subdivisionName: "Sironko", code: "UG-215" }, + "UG-211": { countryCode: "UG", subdivisionName: "Soroti", code: "UG-211" }, + "UG-212": { countryCode: "UG", subdivisionName: "Tororo", code: "UG-212" }, + "UG-113": { countryCode: "UG", subdivisionName: "Wakiso", code: "UG-113" }, + "UG-313": { countryCode: "UG", subdivisionName: "Yumbe", code: "UG-313" }, + "UG-330": { countryCode: "UG", subdivisionName: "Zombo", code: "UG-330" }, + "UM-95": { + countryCode: "UM", + subdivisionName: "Palmyra Atoll", + code: "UM-95", + }, + "US-AL": { countryCode: "US", subdivisionName: "Alabama", code: "US-AL" }, + "US-AK": { countryCode: "US", subdivisionName: "Alaska", code: "US-AK" }, + "US-AZ": { countryCode: "US", subdivisionName: "Arizona", code: "US-AZ" }, + "US-AR": { countryCode: "US", subdivisionName: "Arkansas", code: "US-AR" }, + "US-CA": { countryCode: "US", subdivisionName: "California", code: "US-CA" }, + "US-CO": { countryCode: "US", subdivisionName: "Colorado", code: "US-CO" }, + "US-CT": { countryCode: "US", subdivisionName: "Connecticut", code: "US-CT" }, + "US-DE": { countryCode: "US", subdivisionName: "Delaware", code: "US-DE" }, + "US-DC": { + countryCode: "US", + subdivisionName: "District of Columbia", + code: "US-DC", + }, + "US-FL": { countryCode: "US", subdivisionName: "Florida", code: "US-FL" }, + "US-GA": { countryCode: "US", subdivisionName: "Georgia", code: "US-GA" }, + "US-HI": { countryCode: "US", subdivisionName: "Hawaii", code: "US-HI" }, + "US-ID": { countryCode: "US", subdivisionName: "Idaho", code: "US-ID" }, + "US-IL": { countryCode: "US", subdivisionName: "Illinois", code: "US-IL" }, + "US-IN": { countryCode: "US", subdivisionName: "Indiana", code: "US-IN" }, + "US-IA": { countryCode: "US", subdivisionName: "Iowa", code: "US-IA" }, + "US-KS": { countryCode: "US", subdivisionName: "Kansas", code: "US-KS" }, + "US-KY": { countryCode: "US", subdivisionName: "Kentucky", code: "US-KY" }, + "US-LA": { countryCode: "US", subdivisionName: "Louisiana", code: "US-LA" }, + "US-ME": { countryCode: "US", subdivisionName: "Maine", code: "US-ME" }, + "US-MD": { countryCode: "US", subdivisionName: "Maryland", code: "US-MD" }, + "US-MA": { + countryCode: "US", + subdivisionName: "Massachusetts", + code: "US-MA", + }, + "US-MI": { countryCode: "US", subdivisionName: "Michigan", code: "US-MI" }, + "US-MN": { countryCode: "US", subdivisionName: "Minnesota", code: "US-MN" }, + "US-MS": { countryCode: "US", subdivisionName: "Mississippi", code: "US-MS" }, + "US-MO": { countryCode: "US", subdivisionName: "Missouri", code: "US-MO" }, + "US-MT": { countryCode: "US", subdivisionName: "Montana", code: "US-MT" }, + "US-NE": { countryCode: "US", subdivisionName: "Nebraska", code: "US-NE" }, + "US-NV": { countryCode: "US", subdivisionName: "Nevada", code: "US-NV" }, + "US-NH": { + countryCode: "US", + subdivisionName: "New Hampshire", + code: "US-NH", + }, + "US-NJ": { countryCode: "US", subdivisionName: "New Jersey", code: "US-NJ" }, + "US-NM": { countryCode: "US", subdivisionName: "New Mexico", code: "US-NM" }, + "US-NY": { countryCode: "US", subdivisionName: "New York", code: "US-NY" }, + "US-NC": { + countryCode: "US", + subdivisionName: "North Carolina", + code: "US-NC", + }, + "US-ND": { + countryCode: "US", + subdivisionName: "North Dakota", + code: "US-ND", + }, + "US-OH": { countryCode: "US", subdivisionName: "Ohio", code: "US-OH" }, + "US-OK": { countryCode: "US", subdivisionName: "Oklahoma", code: "US-OK" }, + "US-OR": { countryCode: "US", subdivisionName: "Oregon", code: "US-OR" }, + "US-PA": { + countryCode: "US", + subdivisionName: "Pennsylvania", + code: "US-PA", + }, + "US-RI": { + countryCode: "US", + subdivisionName: "Rhode Island", + code: "US-RI", + }, + "US-SC": { + countryCode: "US", + subdivisionName: "South Carolina", + code: "US-SC", + }, + "US-SD": { + countryCode: "US", + subdivisionName: "South Dakota", + code: "US-SD", + }, + "US-TN": { countryCode: "US", subdivisionName: "Tennessee", code: "US-TN" }, + "US-TX": { countryCode: "US", subdivisionName: "Texas", code: "US-TX" }, + "US-UT": { countryCode: "US", subdivisionName: "Utah", code: "US-UT" }, + "US-VT": { countryCode: "US", subdivisionName: "Vermont", code: "US-VT" }, + "US-VA": { countryCode: "US", subdivisionName: "Virginia", code: "US-VA" }, + "US-WA": { countryCode: "US", subdivisionName: "Washington", code: "US-WA" }, + "US-WV": { + countryCode: "US", + subdivisionName: "West Virginia", + code: "US-WV", + }, + "US-WI": { countryCode: "US", subdivisionName: "Wisconsin", code: "US-WI" }, + "US-WY": { countryCode: "US", subdivisionName: "Wyoming", code: "US-WY" }, + "UY-AR": { countryCode: "UY", subdivisionName: "Artigas", code: "UY-AR" }, + "UY-CA": { countryCode: "UY", subdivisionName: "Canelones", code: "UY-CA" }, + "UY-CL": { countryCode: "UY", subdivisionName: "Cerro Largo", code: "UY-CL" }, + "UY-CO": { countryCode: "UY", subdivisionName: "Colonia", code: "UY-CO" }, + "UY-DU": { countryCode: "UY", subdivisionName: "Durazno", code: "UY-DU" }, + "UY-FS": { countryCode: "UY", subdivisionName: "Flores", code: "UY-FS" }, + "UY-FD": { countryCode: "UY", subdivisionName: "Florida", code: "UY-FD" }, + "UY-LA": { countryCode: "UY", subdivisionName: "Lavalleja", code: "UY-LA" }, + "UY-MA": { countryCode: "UY", subdivisionName: "Maldonado", code: "UY-MA" }, + "UY-MO": { countryCode: "UY", subdivisionName: "Montevideo", code: "UY-MO" }, + "UY-PA": { countryCode: "UY", subdivisionName: "Paysandu", code: "UY-PA" }, + "UY-RN": { countryCode: "UY", subdivisionName: "Rio Negro", code: "UY-RN" }, + "UY-RV": { countryCode: "UY", subdivisionName: "Rivera", code: "UY-RV" }, + "UY-RO": { countryCode: "UY", subdivisionName: "Rocha", code: "UY-RO" }, + "UY-SA": { countryCode: "UY", subdivisionName: "Salto", code: "UY-SA" }, + "UY-SJ": { countryCode: "UY", subdivisionName: "San Jose", code: "UY-SJ" }, + "UY-SO": { countryCode: "UY", subdivisionName: "Soriano", code: "UY-SO" }, + "UY-TA": { countryCode: "UY", subdivisionName: "Tacuarembo", code: "UY-TA" }, + "UY-TT": { + countryCode: "UY", + subdivisionName: "Treinta y Tres", + code: "UY-TT", + }, + "UZ-AN": { countryCode: "UZ", subdivisionName: "Andijon", code: "UZ-AN" }, + "UZ-BU": { countryCode: "UZ", subdivisionName: "Buxoro", code: "UZ-BU" }, + "UZ-FA": { countryCode: "UZ", subdivisionName: "Farg'ona", code: "UZ-FA" }, + "UZ-JI": { countryCode: "UZ", subdivisionName: "Jizzax", code: "UZ-JI" }, + "UZ-NG": { countryCode: "UZ", subdivisionName: "Namangan", code: "UZ-NG" }, + "UZ-NW": { countryCode: "UZ", subdivisionName: "Navoiy", code: "UZ-NW" }, + "UZ-QA": { countryCode: "UZ", subdivisionName: "Qashqadaryo", code: "UZ-QA" }, + "UZ-QR": { + countryCode: "UZ", + subdivisionName: "Qoraqalpog'iston Respublikasi", + code: "UZ-QR", + }, + "UZ-SA": { countryCode: "UZ", subdivisionName: "Samarqand", code: "UZ-SA" }, + "UZ-SI": { countryCode: "UZ", subdivisionName: "Sirdaryo", code: "UZ-SI" }, + "UZ-SU": { countryCode: "UZ", subdivisionName: "Surxondaryo", code: "UZ-SU" }, + "UZ-TK": { countryCode: "UZ", subdivisionName: "Toshkent", code: "UZ-TK" }, + "UZ-XO": { countryCode: "UZ", subdivisionName: "Xorazm", code: "UZ-XO" }, + "VC-01": { countryCode: "VC", subdivisionName: "Charlotte", code: "VC-01" }, + "VC-06": { countryCode: "VC", subdivisionName: "Grenadines", code: "VC-06" }, + "VC-04": { + countryCode: "VC", + subdivisionName: "Saint George", + code: "VC-04", + }, + "VC-05": { + countryCode: "VC", + subdivisionName: "Saint Patrick", + code: "VC-05", + }, + "VE-Z": { countryCode: "VE", subdivisionName: "Amazonas", code: "VE-Z" }, + "VE-B": { countryCode: "VE", subdivisionName: "Anzoategui", code: "VE-B" }, + "VE-C": { countryCode: "VE", subdivisionName: "Apure", code: "VE-C" }, + "VE-D": { countryCode: "VE", subdivisionName: "Aragua", code: "VE-D" }, + "VE-E": { countryCode: "VE", subdivisionName: "Barinas", code: "VE-E" }, + "VE-F": { countryCode: "VE", subdivisionName: "Bolivar", code: "VE-F" }, + "VE-G": { countryCode: "VE", subdivisionName: "Carabobo", code: "VE-G" }, + "VE-H": { countryCode: "VE", subdivisionName: "Cojedes", code: "VE-H" }, + "VE-Y": { countryCode: "VE", subdivisionName: "Delta Amacuro", code: "VE-Y" }, + "VE-W": { + countryCode: "VE", + subdivisionName: "Dependencias Federales", + code: "VE-W", + }, + "VE-A": { + countryCode: "VE", + subdivisionName: "Distrito Capital", + code: "VE-A", + }, + "VE-I": { countryCode: "VE", subdivisionName: "Falcon", code: "VE-I" }, + "VE-J": { countryCode: "VE", subdivisionName: "Guarico", code: "VE-J" }, + "VE-X": { countryCode: "VE", subdivisionName: "La Guaira", code: "VE-X" }, + "VE-K": { countryCode: "VE", subdivisionName: "Lara", code: "VE-K" }, + "VE-L": { countryCode: "VE", subdivisionName: "Merida", code: "VE-L" }, + "VE-M": { countryCode: "VE", subdivisionName: "Miranda", code: "VE-M" }, + "VE-N": { countryCode: "VE", subdivisionName: "Monagas", code: "VE-N" }, + "VE-O": { countryCode: "VE", subdivisionName: "Nueva Esparta", code: "VE-O" }, + "VE-P": { countryCode: "VE", subdivisionName: "Portuguesa", code: "VE-P" }, + "VE-R": { countryCode: "VE", subdivisionName: "Sucre", code: "VE-R" }, + "VE-S": { countryCode: "VE", subdivisionName: "Tachira", code: "VE-S" }, + "VE-T": { countryCode: "VE", subdivisionName: "Trujillo", code: "VE-T" }, + "VE-U": { countryCode: "VE", subdivisionName: "Yaracuy", code: "VE-U" }, + "VE-V": { countryCode: "VE", subdivisionName: "Zulia", code: "VE-V" }, + "VN-44": { countryCode: "VN", subdivisionName: "An Giang", code: "VN-44" }, + "VN-43": { + countryCode: "VN", + subdivisionName: "Ba Ria - Vung Tau", + code: "VN-43", + }, + "VN-54": { countryCode: "VN", subdivisionName: "Bac Giang", code: "VN-54" }, + "VN-53": { countryCode: "VN", subdivisionName: "Bac Kan", code: "VN-53" }, + "VN-55": { countryCode: "VN", subdivisionName: "Bac Lieu", code: "VN-55" }, + "VN-56": { countryCode: "VN", subdivisionName: "Bac Ninh", code: "VN-56" }, + "VN-50": { countryCode: "VN", subdivisionName: "Ben Tre", code: "VN-50" }, + "VN-31": { countryCode: "VN", subdivisionName: "Binh Dinh", code: "VN-31" }, + "VN-57": { countryCode: "VN", subdivisionName: "Binh Duong", code: "VN-57" }, + "VN-58": { countryCode: "VN", subdivisionName: "Binh Phuoc", code: "VN-58" }, + "VN-40": { countryCode: "VN", subdivisionName: "Binh Thuan", code: "VN-40" }, + "VN-59": { countryCode: "VN", subdivisionName: "Ca Mau", code: "VN-59" }, + "VN-CT": { countryCode: "VN", subdivisionName: "Can Tho", code: "VN-CT" }, + "VN-04": { countryCode: "VN", subdivisionName: "Cao Bang", code: "VN-04" }, + "VN-DN": { countryCode: "VN", subdivisionName: "Da Nang", code: "VN-DN" }, + "VN-33": { countryCode: "VN", subdivisionName: "Dak Lak", code: "VN-33" }, + "VN-72": { countryCode: "VN", subdivisionName: "Dak Nong", code: "VN-72" }, + "VN-71": { countryCode: "VN", subdivisionName: "Dien Bien", code: "VN-71" }, + "VN-39": { countryCode: "VN", subdivisionName: "Dong Nai", code: "VN-39" }, + "VN-45": { countryCode: "VN", subdivisionName: "Dong Thap", code: "VN-45" }, + "VN-30": { countryCode: "VN", subdivisionName: "Gia Lai", code: "VN-30" }, + "VN-03": { countryCode: "VN", subdivisionName: "Ha Giang", code: "VN-03" }, + "VN-63": { countryCode: "VN", subdivisionName: "Ha Nam", code: "VN-63" }, + "VN-HN": { countryCode: "VN", subdivisionName: "Ha Noi", code: "VN-HN" }, + "VN-23": { countryCode: "VN", subdivisionName: "Ha Tinh", code: "VN-23" }, + "VN-61": { countryCode: "VN", subdivisionName: "Hai Duong", code: "VN-61" }, + "VN-HP": { countryCode: "VN", subdivisionName: "Hai Phong", code: "VN-HP" }, + "VN-73": { countryCode: "VN", subdivisionName: "Hau Giang", code: "VN-73" }, + "VN-SG": { countryCode: "VN", subdivisionName: "Ho Chi Minh", code: "VN-SG" }, + "VN-14": { countryCode: "VN", subdivisionName: "Hoa Binh", code: "VN-14" }, + "VN-66": { countryCode: "VN", subdivisionName: "Hung Yen", code: "VN-66" }, + "VN-34": { countryCode: "VN", subdivisionName: "Khanh Hoa", code: "VN-34" }, + "VN-47": { countryCode: "VN", subdivisionName: "Kien Giang", code: "VN-47" }, + "VN-28": { countryCode: "VN", subdivisionName: "Kon Tum", code: "VN-28" }, + "VN-01": { countryCode: "VN", subdivisionName: "Lai Chau", code: "VN-01" }, + "VN-35": { countryCode: "VN", subdivisionName: "Lam Dong", code: "VN-35" }, + "VN-09": { countryCode: "VN", subdivisionName: "Lang Son", code: "VN-09" }, + "VN-02": { countryCode: "VN", subdivisionName: "Lao Cai", code: "VN-02" }, + "VN-41": { countryCode: "VN", subdivisionName: "Long An", code: "VN-41" }, + "VN-67": { countryCode: "VN", subdivisionName: "Nam Dinh", code: "VN-67" }, + "VN-22": { countryCode: "VN", subdivisionName: "Nghe An", code: "VN-22" }, + "VN-18": { countryCode: "VN", subdivisionName: "Ninh Binh", code: "VN-18" }, + "VN-36": { countryCode: "VN", subdivisionName: "Ninh Thuan", code: "VN-36" }, + "VN-68": { countryCode: "VN", subdivisionName: "Phu Tho", code: "VN-68" }, + "VN-32": { countryCode: "VN", subdivisionName: "Phu Yen", code: "VN-32" }, + "VN-24": { countryCode: "VN", subdivisionName: "Quang Binh", code: "VN-24" }, + "VN-27": { countryCode: "VN", subdivisionName: "Quang Nam", code: "VN-27" }, + "VN-29": { countryCode: "VN", subdivisionName: "Quang Ngai", code: "VN-29" }, + "VN-13": { countryCode: "VN", subdivisionName: "Quang Ninh", code: "VN-13" }, + "VN-25": { countryCode: "VN", subdivisionName: "Quang Tri", code: "VN-25" }, + "VN-52": { countryCode: "VN", subdivisionName: "Soc Trang", code: "VN-52" }, + "VN-05": { countryCode: "VN", subdivisionName: "Son La", code: "VN-05" }, + "VN-37": { countryCode: "VN", subdivisionName: "Tay Ninh", code: "VN-37" }, + "VN-20": { countryCode: "VN", subdivisionName: "Thai Binh", code: "VN-20" }, + "VN-69": { countryCode: "VN", subdivisionName: "Thai Nguyen", code: "VN-69" }, + "VN-21": { countryCode: "VN", subdivisionName: "Thanh Hoa", code: "VN-21" }, + "VN-26": { + countryCode: "VN", + subdivisionName: "Thua Thien-Hue", + code: "VN-26", + }, + "VN-46": { countryCode: "VN", subdivisionName: "Tien Giang", code: "VN-46" }, + "VN-51": { countryCode: "VN", subdivisionName: "Tra Vinh", code: "VN-51" }, + "VN-07": { countryCode: "VN", subdivisionName: "Tuyen Quang", code: "VN-07" }, + "VN-49": { countryCode: "VN", subdivisionName: "Vinh Long", code: "VN-49" }, + "VN-70": { countryCode: "VN", subdivisionName: "Vinh Phuc", code: "VN-70" }, + "VN-06": { countryCode: "VN", subdivisionName: "Yen Bai", code: "VN-06" }, + "VU-SAM": { countryCode: "VU", subdivisionName: "Sanma", code: "VU-SAM" }, + "VU-SEE": { countryCode: "VU", subdivisionName: "Shefa", code: "VU-SEE" }, + "VU-TAE": { countryCode: "VU", subdivisionName: "Tafea", code: "VU-TAE" }, + "VU-TOB": { countryCode: "VU", subdivisionName: "Torba", code: "VU-TOB" }, + "WF-SG": { countryCode: "WF", subdivisionName: "Sigave", code: "WF-SG" }, + "WF-UV": { countryCode: "WF", subdivisionName: "Uvea", code: "WF-UV" }, + "WS-AT": { countryCode: "WS", subdivisionName: "Atua", code: "WS-AT" }, + "WS-FA": { + countryCode: "WS", + subdivisionName: "Fa'asaleleaga", + code: "WS-FA", + }, + "WS-GI": { + countryCode: "WS", + subdivisionName: "Gagaifomauga", + code: "WS-GI", + }, + "WS-TU": { countryCode: "WS", subdivisionName: "Tuamasaga", code: "WS-TU" }, + "YE-AD": { countryCode: "YE", subdivisionName: "'Adan", code: "YE-AD" }, + "YE-AM": { countryCode: "YE", subdivisionName: "'Amran", code: "YE-AM" }, + "YE-DA": { countryCode: "YE", subdivisionName: "Ad Dali'", code: "YE-DA" }, + "YE-BA": { countryCode: "YE", subdivisionName: "Al Bayda'", code: "YE-BA" }, + "YE-HU": { countryCode: "YE", subdivisionName: "Al Hudaydah", code: "YE-HU" }, + "YE-JA": { countryCode: "YE", subdivisionName: "Al Jawf", code: "YE-JA" }, + "YE-MW": { countryCode: "YE", subdivisionName: "Al Mahwit", code: "YE-MW" }, + "YE-SA": { + countryCode: "YE", + subdivisionName: "Amanat al 'Asimah", + code: "YE-SA", + }, + "YE-DH": { countryCode: "YE", subdivisionName: "Dhamar", code: "YE-DH" }, + "YE-HD": { countryCode: "YE", subdivisionName: "Hadramawt", code: "YE-HD" }, + "YE-HJ": { countryCode: "YE", subdivisionName: "Hajjah", code: "YE-HJ" }, + "YE-IB": { countryCode: "YE", subdivisionName: "Ibb", code: "YE-IB" }, + "YE-LA": { countryCode: "YE", subdivisionName: "Lahij", code: "YE-LA" }, + "YE-MA": { countryCode: "YE", subdivisionName: "Ma'rib", code: "YE-MA" }, + "YE-RA": { countryCode: "YE", subdivisionName: "Raymah", code: "YE-RA" }, + "YE-SD": { countryCode: "YE", subdivisionName: "Sa'dah", code: "YE-SD" }, + "YE-SN": { countryCode: "YE", subdivisionName: "San'a'", code: "YE-SN" }, + "YE-SH": { countryCode: "YE", subdivisionName: "Shabwah", code: "YE-SH" }, + "YE-TA": { countryCode: "YE", subdivisionName: "Ta'izz", code: "YE-TA" }, + "ZA-EC": { + countryCode: "ZA", + subdivisionName: "Eastern Cape", + code: "ZA-EC", + }, + "ZA-FS": { countryCode: "ZA", subdivisionName: "Free State", code: "ZA-FS" }, + "ZA-GP": { countryCode: "ZA", subdivisionName: "Gauteng", code: "ZA-GP" }, + "ZA-KZN": { + countryCode: "ZA", + subdivisionName: "Kwazulu-Natal", + code: "ZA-KZN", + }, + "ZA-LP": { countryCode: "ZA", subdivisionName: "Limpopo", code: "ZA-LP" }, + "ZA-MP": { countryCode: "ZA", subdivisionName: "Mpumalanga", code: "ZA-MP" }, + "ZA-NW": { countryCode: "ZA", subdivisionName: "North-West", code: "ZA-NW" }, + "ZA-NC": { + countryCode: "ZA", + subdivisionName: "Northern Cape", + code: "ZA-NC", + }, + "ZA-WC": { + countryCode: "ZA", + subdivisionName: "Western Cape", + code: "ZA-WC", + }, + "ZM-02": { countryCode: "ZM", subdivisionName: "Central", code: "ZM-02" }, + "ZM-08": { countryCode: "ZM", subdivisionName: "Copperbelt", code: "ZM-08" }, + "ZM-03": { countryCode: "ZM", subdivisionName: "Eastern", code: "ZM-03" }, + "ZM-04": { countryCode: "ZM", subdivisionName: "Luapula", code: "ZM-04" }, + "ZM-09": { countryCode: "ZM", subdivisionName: "Lusaka", code: "ZM-09" }, + "ZM-10": { countryCode: "ZM", subdivisionName: "Muchinga", code: "ZM-10" }, + "ZM-06": { + countryCode: "ZM", + subdivisionName: "North-Western", + code: "ZM-06", + }, + "ZM-05": { countryCode: "ZM", subdivisionName: "Northern", code: "ZM-05" }, + "ZM-07": { countryCode: "ZM", subdivisionName: "Southern", code: "ZM-07" }, + "ZM-01": { countryCode: "ZM", subdivisionName: "Western", code: "ZM-01" }, + "ZW-BU": { countryCode: "ZW", subdivisionName: "Bulawayo", code: "ZW-BU" }, + "ZW-HA": { countryCode: "ZW", subdivisionName: "Harare", code: "ZW-HA" }, + "ZW-MA": { countryCode: "ZW", subdivisionName: "Manicaland", code: "ZW-MA" }, + "ZW-MC": { + countryCode: "ZW", + subdivisionName: "Mashonaland Central", + code: "ZW-MC", + }, + "ZW-ME": { + countryCode: "ZW", + subdivisionName: "Mashonaland East", + code: "ZW-ME", + }, + "ZW-MW": { + countryCode: "ZW", + subdivisionName: "Mashonaland West", + code: "ZW-MW", + }, + "ZW-MV": { countryCode: "ZW", subdivisionName: "Masvingo", code: "ZW-MV" }, + "ZW-MN": { + countryCode: "ZW", + subdivisionName: "Matabeleland North", + code: "ZW-MN", + }, + "ZW-MS": { + countryCode: "ZW", + subdivisionName: "Matabeleland South", + code: "ZW-MS", + }, + "ZW-MI": { countryCode: "ZW", subdivisionName: "Midlands", code: "ZW-MI" }, +}; diff --git a/packages/widget/src/utils/text.ts b/packages/widget/src/utils/text.ts index 9ba1f0ad..d9d3a3c5 100644 --- a/packages/widget/src/utils/text.ts +++ b/packages/widget/src/utils/text.ts @@ -1,5 +1,5 @@ import { memoize } from "./memoize"; export const capitalizeFirstLowerRest = memoize( - (txt: string) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase() + (txt: string) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase(), ); diff --git a/packages/widget/src/worker.ts b/packages/widget/src/worker.ts index fb4b7385..0dbb41bb 100644 --- a/packages/widget/src/worker.ts +++ b/packages/widget/src/worker.ts @@ -222,7 +222,7 @@ export const worker = setupWorker( return HttpResponse.json( url.searchParams.has("ledgerWalletAPICompatible") ? legacyYieldDto - : yieldApiYieldDto + : yieldApiYieldDto, ); }), http.post("*/v1/yields/balances", async () => { @@ -277,5 +277,5 @@ export const worker = setupWorker( token: maticToken, gasLimit: "", }); - }) + }), ); diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index ea543d00..933b9dc7 100644 --- a/packages/widget/tests/fixtures/index.ts +++ b/packages/widget/tests/fixtures/index.ts @@ -1,5 +1,4 @@ import { faker } from "@faker-js/faker"; -import type { ActionDto, TransactionDto, YieldDto } from "@stakekit/api-hooks"; import { getActionControllerEnterResponseMock, getActionControllerPendingResponseMock, @@ -16,10 +15,18 @@ import type { YieldTransactionDto, } from "../../src/providers/yield-api-client-provider/types"; +type LegacyActionDto = ReturnType; +type LegacyTransactionDto = ReturnType< + typeof getTransactionControllerConstructResponseMock +>; +type LegacyYieldDto = ReturnType< + typeof getYieldV2ControllerGetYieldByIdResponseMock +>; + const apyFaker = () => faker.number.float({ min: 0, max: 0.05 }); export const yieldRewardRateFixture = ( - overrides?: Partial + overrides?: Partial, ): YieldRewardRateDto => ({ total: apyFaker(), rateType: "APY", @@ -28,7 +35,7 @@ export const yieldRewardRateFixture = ( }); export const yieldApiYieldFixture = ( - overrides?: Partial + overrides?: Partial, ): YieldApiYieldDto => ({ ...getYieldV2ControllerGetYieldByIdResponseMock(), @@ -37,7 +44,7 @@ export const yieldApiYieldFixture = ( }) as YieldApiYieldDto; export const yieldBalanceFixture = ( - overrides?: Partial + overrides?: Partial, ): YieldBalanceDto => { const token = overrides?.token ?? yieldApiYieldFixture().token; @@ -53,7 +60,7 @@ export const yieldBalanceFixture = ( } as YieldBalanceDto; }; -export const yieldFixture = (overrides?: Partial) => +export const yieldFixture = (overrides?: Partial) => Just(getYieldV2ControllerGetYieldByIdResponseMock()) .map( (val) => @@ -74,36 +81,38 @@ export const yieldFixture = (overrides?: Partial) => status: { enter: true, exit: true }, validators: val.validators.map((v) => ({ ...v, apr: apyFaker() })), ...overrides, - }) satisfies YieldDto + }) satisfies LegacyYieldDto, ) .unsafeCoerce(); -export const yieldValidatorsFixture = (validators?: YieldDto["validators"]) => +export const yieldValidatorsFixture = ( + validators?: LegacyYieldDto["validators"], +) => (validators ?? yieldFixture().validators).map((validator) => ({ ...validator, apr: validator.apr ?? apyFaker(), })); -export const enterResponseFixture = (overrides?: Partial) => ({ +export const enterResponseFixture = (overrides?: Partial) => ({ ...getActionControllerEnterResponseMock(), ...overrides, }); export const transactionConstructFixture = ( - overrides?: Partial + overrides?: Partial, ) => ({ ...getTransactionControllerConstructResponseMock(), ...overrides, }); -export const pendingActionFixture = (overrides?: Partial) => ({ +export const pendingActionFixture = (overrides?: Partial) => ({ ...getActionControllerPendingResponseMock(), ...overrides, }); export const yieldApiTransactionFixture = ( - tx: TransactionDto, - overrides?: Partial + tx: LegacyTransactionDto, + overrides?: Partial, ) => ({ id: tx.id || faker.string.uuid(), @@ -133,7 +142,7 @@ export const yieldApiActionFixture = ({ transactions, overrides, }: { - action: ActionDto; + action: LegacyActionDto; address: string; rawArguments?: YieldActionArgumentsDto | null; transactions?: YieldTransactionDto[]; diff --git a/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx b/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx index af98aa92..b96bb409 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx +++ b/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx @@ -32,7 +32,7 @@ describe("Deep links flow", () => { .element( withAvaxLiquidStakingApp .getByText(`${avaxLiquidStaking.metadata.rewardTokens[0].symbol}`) - .first() + .first(), ) .toBeInTheDocument(); @@ -40,7 +40,7 @@ describe("Deep links flow", () => { .element( withAvaxLiquidStakingApp .getByText(`via ${avaxLiquidStaking.metadata.provider.name}`) - .first() + .first(), ) .toBeInTheDocument(); @@ -48,7 +48,7 @@ describe("Deep links flow", () => { .element( withAvaxLiquidStakingApp .getByText(`${APToPercentage(avaxLiquidStaking.rewardRate)}%`) - .first() + .first(), ) .toBeInTheDocument(); @@ -68,7 +68,7 @@ describe("Deep links flow", () => { .element( withAvaxNativeStakingApp .getByText(`${APToPercentage(avaxNativeStaking.rewardRate)}%`) - .first() + .first(), ) .toBeInTheDocument(); @@ -93,7 +93,7 @@ describe("Deep links flow", () => { await expect .element( - app.getByText("By clicking confirm you agree to", { exact: false }) + app.getByText("By clicking confirm you agree to", { exact: false }), ) .toBeInTheDocument(); @@ -150,7 +150,7 @@ describe("Deep links flow", () => { await expect .element( - app.getByText("By clicking confirm you agree to", { exact: false }) + app.getByText("By clicking confirm you agree to", { exact: false }), ) .toBeInTheDocument(); diff --git a/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx b/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx index a9a0edc9..c341b1c5 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx +++ b/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx @@ -1,6 +1,6 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { I18nextProvider } from "react-i18next"; import { describe, expect, it } from "vitest"; +import type { ActionType } from "../../../src/domain/types/action"; import { useInitQueryParams } from "../../../src/hooks/use-init-query-params"; import { SettingsContextProvider } from "../../../src/providers/settings"; import { i18nInstance } from "../../../src/translation"; @@ -11,7 +11,7 @@ describe("Deep link param validation", () => { it("Should validate yieldId param", async () => { const setAndAssertIsValidYieldIdParam = async ( yieldId: string, - valid: boolean + valid: boolean, ) => { _setUrl({ yieldId }); @@ -27,14 +27,14 @@ describe("Deep link param validation", () => { }); expect(result.result.current.map((v) => v.yieldId).extract()).toEqual( - valid ? yieldId : null + valid ? yieldId : null, ); }; await setAndAssertIsValidYieldIdParam("ethereum-eth-native-staking", true); await setAndAssertIsValidYieldIdParam( "../ethereum-eth-native-staking", - false + false, ); await setAndAssertIsValidYieldIdParam("..", false); await setAndAssertIsValidYieldIdParam("..%2f", false); @@ -42,26 +42,26 @@ describe("Deep link param validation", () => { await setAndAssertIsValidYieldIdParam("AAA-%2f..%2f..%2f-whatever", false); await setAndAssertIsValidYieldIdParam( "./ethereum-eth-native-staking", - false + false, ); await setAndAssertIsValidYieldIdParam( "ethereum-../eth-native-staking", - false + false, ); await setAndAssertIsValidYieldIdParam( "ethereum-eth-native-staking../", - false + false, ); await setAndAssertIsValidYieldIdParam( "ethereum-eth-native-staking/../", - false + false, ); }); it("Should validate pendingAction param", async () => { const setAndAssertIsValidPendingActionParam = async ( - pendingaction: ActionTypes | (string & {}), - valid: boolean + pendingaction: ActionType | (string & {}), + valid: boolean, ) => { _setUrl({ pendingaction }); @@ -77,7 +77,7 @@ describe("Deep link param validation", () => { }); expect(result.current.map((v) => v.pendingaction).extract()).toEqual( - valid ? pendingaction : null + valid ? pendingaction : null, ); }; 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 035fee20..5ece82d9 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -1,4 +1,3 @@ -import type { TokenDto, YieldBalanceDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { vitest } from "vitest"; @@ -15,12 +14,14 @@ import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; import { setUrl as _setUrl } from "./utils"; +type LegacyTokenDto = ReturnType["token"]; + export const setup = async (opts?: { withValidatorAddressesRequired?: boolean; }) => { const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; - const ether: TokenDto = { + const ether: LegacyTokenDto = { network: "ethereum", name: "Ethereum", symbol: "ETH", @@ -29,7 +30,7 @@ export const setup = async (opts?: { logoURI: "https://assets.stakek.it/tokens/eth.svg", }; - const token: TokenDto = { + const token: LegacyTokenDto = { name: "Avalanche C Chain", symbol: "AVAX", decimals: 18, @@ -42,34 +43,35 @@ export const setup = async (opts?: { const avaxNativeStaking = Just(yieldFixture()) .map( - (def): YieldDto => ({ + (def): ReturnType => ({ ...def, id: "avalanche-avax-native-staking", token, tokens: [token], metadata: { ...def.metadata, type: "staking" }, validators: [], - }) + }), ) .unsafeCoerce(); - const avaxLiquidStakingValidators: YieldDto["validators"] = - opts?.withValidatorAddressesRequired - ? [ - { - address: "0xe92b7ba8497486e94bb59c51f595b590c4a5f894", - status: "active", - name: "Stakely", - image: "https://assets.stakek.it/validators/stakely.png", - website: "https://stakely.io/", - apr: 0.0393, - commission: 0.1, - stakedBalance: "2263157", - votingPower: 0.0090962642447408, - preferred: true, - }, - ] - : []; + const avaxLiquidStakingValidators: ReturnType< + typeof yieldFixture + >["validators"] = opts?.withValidatorAddressesRequired + ? [ + { + address: "0xe92b7ba8497486e94bb59c51f595b590c4a5f894", + status: "active", + name: "Stakely", + image: "https://assets.stakek.it/validators/stakely.png", + website: "https://stakely.io/", + apr: 0.0393, + commission: 0.1, + stakedBalance: "2263157", + votingPower: 0.0090962642447408, + preferred: true, + }, + ] + : []; const avaxLiquidStaking = Just(yieldFixture()) .map( @@ -102,11 +104,11 @@ export const setup = async (opts?: { ], }, validators: avaxLiquidStakingValidators, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); - const avaxLiquidStakingBalances: YieldBalanceDto[] = [ + const avaxLiquidStakingBalances = [ { groupId: "b4684f63-fe54-540d-b0ae-06a2c2ecdb9e", type: "rewards", @@ -317,7 +319,7 @@ export const setup = async (opts?: { status: "CONFIRMED", }), }); - }) + }), ); let currentChainId = 43114; @@ -348,7 +350,7 @@ export const setup = async (opts?: { default: throw new Error("unhandled method"); } - } + }, ); const customConnectors = rkMockWallet({ accounts: [account], requestFn }); diff --git a/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx b/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx index ec853d16..444df694 100644 --- a/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx +++ b/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx @@ -32,7 +32,7 @@ describe("External Provider", () => { await expect .element( - app.getByText(formatAddress(skProps.externalProviders.currentAddress)) + app.getByText(formatAddress(skProps.externalProviders.currentAddress)), ) .toBeInTheDocument(); @@ -45,8 +45,8 @@ describe("External Provider", () => { const chainText = app.getByText( new RegExp( - `${chainNames.eth}|${chainNames.avalanche}|${chainNames.solana}|${chainNames.ton}` - ) + `${chainNames.eth}|${chainNames.avalanche}|${chainNames.solana}|${chainNames.ton}`, + ), ); await expect.element(chainText).toBeInTheDocument(); @@ -78,17 +78,17 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - /> + />, ); await expect .poll(() => - app.getByText(new RegExp(`${chainNames.avalanche}|${chainNames.ton}`)) + app.getByText(new RegExp(`${chainNames.avalanche}|${chainNames.ton}`)), ) .toBeTruthy(); const chainButton = app.getByText( - new RegExp(`${chainNames.avalanche}|${chainNames.ton}`) + new RegExp(`${chainNames.avalanche}|${chainNames.ton}`), ); await chainButton.click(); @@ -117,12 +117,12 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - /> + />, ); await expect .element( - app.getByText(formatAddress(skProps.externalProviders.currentAddress)) + app.getByText(formatAddress(skProps.externalProviders.currentAddress)), ) .toBeInTheDocument(); @@ -136,7 +136,7 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - /> + />, ); await expect @@ -153,12 +153,12 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - /> + />, ); await expect .element( - app.getByText(formatAddress(skProps.externalProviders.currentAddress)) + app.getByText(formatAddress(skProps.externalProviders.currentAddress)), ) .toBeInTheDocument(); app.unmount(); diff --git a/packages/widget/tests/use-cases/external-provider/setup.ts b/packages/widget/tests/use-cases/external-provider/setup.ts index 706816ec..493c9605 100644 --- a/packages/widget/tests/use-cases/external-provider/setup.ts +++ b/packages/widget/tests/use-cases/external-provider/setup.ts @@ -1,11 +1,12 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { yieldFixture, yieldValidatorsFixture } from "../../fixtures"; import { worker } from "../../mocks/worker"; +type LegacyTokenDto = ReturnType["token"]; + export const setup = () => { - const avalancheCToken: TokenDto = { + const avalancheCToken: LegacyTokenDto = { name: "Avalanche C Chain", symbol: "AVAX", decimals: 18, @@ -14,7 +15,7 @@ export const setup = () => { logoURI: "https://assets.stakek.it/tokens/avax.svg", }; - const ether: TokenDto = { + const ether: LegacyTokenDto = { network: "ethereum", name: "Ethereum", symbol: "ETH", @@ -23,7 +24,7 @@ export const setup = () => { logoURI: "https://assets.stakek.it/tokens/eth.svg", }; - const solanaToken: TokenDto = { + const solanaToken: LegacyTokenDto = { network: "solana", name: "Solana", symbol: "SOL", @@ -32,7 +33,7 @@ export const setup = () => { logoURI: "https://assets.stakek.it/tokens/sol.svg", }; - const tonToken: TokenDto = { + const tonToken: LegacyTokenDto = { network: "ton", name: "Toncoin", symbol: "TON", @@ -55,7 +56,7 @@ export const setup = () => { type: "staking", gasFeeToken: avalancheCToken, }, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); @@ -73,7 +74,7 @@ export const setup = () => { type: "staking", gasFeeToken: ether, }, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); @@ -91,7 +92,7 @@ export const setup = () => { type: "staking", gasFeeToken: solanaToken, }, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); @@ -109,7 +110,7 @@ export const setup = () => { type: "staking", gasFeeToken: tonToken, }, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); @@ -188,20 +189,23 @@ export const setup = () => { await delay(); const yieldId = info.params.yieldId as string; - const validatorsByYieldId = new Map([ + const validatorsByYieldId = new Map< + string, + ReturnType["validators"] + >([ [etherNativeStaking.id, etherNativeStaking.validators], [avalancheAvaxNativeStaking.id, avalancheAvaxNativeStaking.validators], [solanaNativeStaking.id, solanaNativeStaking.validators], [tonNativeStaking.id, tonNativeStaking.validators], ]); const validators = yieldValidatorsFixture( - validatorsByYieldId.get(yieldId) ?? [] + validatorsByYieldId.get(yieldId) ?? [], ); return HttpResponse.json({ items: validators, total: validators.length, }); - }) + }), ); }; diff --git a/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx b/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx index 26bdb4ef..f989290a 100644 --- a/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx +++ b/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx @@ -1,4 +1,3 @@ -import type { YieldDto } from "@stakekit/api-hooks"; import { describe, expect, it } from "vitest"; import { userEvent } from "vitest/browser"; import { formatAddress } from "../../../src/utils"; @@ -14,7 +13,9 @@ describe("Gas warning flow", () => { customConnectors, }: { availableAmount: string; - yieldDto: YieldDto; + yieldDto: ReturnType< + typeof setup + >["yieldWithSameGasAndStakeToken"]["yieldDto"]; withWarning: boolean; } & Pick, "account" | "customConnectors">) => { const app = await renderApp({ @@ -40,7 +41,7 @@ describe("Gas warning flow", () => { .poll( () => app.getByTestId("select-opportunity").getByText(yieldDto.token.symbol) - .length + .length, ) .greaterThan(0); @@ -57,13 +58,13 @@ describe("Gas warning flow", () => { if (withWarning) { await expect .element( - app.getByText("This action is unlikely to succeed", { exact: false }) + app.getByText("This action is unlikely to succeed", { exact: false }), ) .toBeInTheDocument(); } else { await expect .element( - app.getByText("This action is unlikely to succeed", { exact: false }) + app.getByText("This action is unlikely to succeed", { exact: false }), ) .not.toBeInTheDocument(); } diff --git a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts index 46c4525d..c4fffa3b 100644 --- a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts +++ b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts @@ -1,9 +1,3 @@ -import type { - ActionDto, - TokenDto, - TransactionDto, - YieldDto, -} from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { vitest } from "vitest"; @@ -20,8 +14,10 @@ import { import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; +type LegacyTokenDto = ReturnType["token"]; + export const setup = () => { - const avalancheCToken: TokenDto = { + const avalancheCToken: LegacyTokenDto = { name: "Avalanche C Chain", symbol: "AVAX", decimals: 18, @@ -29,7 +25,7 @@ export const setup = () => { coinGeckoId: "avalanche-2", logoURI: "https://assets.stakek.it/tokens/avax.svg", }; - const usdcToken: TokenDto = { + const usdcToken: LegacyTokenDto = { network: "avalanche-c", symbol: "USDC", name: "USD Coin", @@ -52,7 +48,7 @@ export const setup = () => { type: "staking", gasFeeToken: avalancheCToken, }, - } satisfies YieldDto, + } satisfies ReturnType, })) .map((val) => ({ ...val, @@ -73,7 +69,7 @@ export const setup = () => { gasEstimate: null, }, ], - } satisfies ActionDto, + } satisfies ReturnType, })) .unsafeCoerce(); @@ -90,7 +86,7 @@ export const setup = () => { type: "staking", gasFeeToken: avalancheCToken, }, - } satisfies YieldDto, + } satisfies ReturnType, })) .map((val) => ({ ...val, @@ -125,13 +121,16 @@ export const setup = () => { usdcTokenAmount = amount.toString(); }; - const yieldsTxGasAmountMap = new Map([]); + const yieldsTxGasAmountMap = new Map< + ReturnType["id"], + string + >([]); const setTxGas = ({ yieldId, amount, }: { - yieldId: YieldDto["id"]; + yieldId: ReturnType["id"]; amount: string; }) => yieldsTxGasAmountMap.set(yieldId, amount); @@ -192,7 +191,7 @@ export const setup = () => { await delay(); return HttpResponse.json(yieldWithSameGasAndStakeToken.yieldDto); - } + }, ), http.get( `*/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}`, @@ -200,7 +199,7 @@ export const setup = () => { await delay(); return HttpResponse.json(yieldWithDifferentGasAndStakeToken.yieldDto); - } + }, ), http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); @@ -209,10 +208,10 @@ export const setup = () => { const validators = yieldId === yieldWithSameGasAndStakeToken.yieldDto.id ? yieldValidatorsFixture( - yieldWithSameGasAndStakeToken.yieldDto.validators + yieldWithSameGasAndStakeToken.yieldDto.validators, ) : yieldValidatorsFixture( - yieldWithDifferentGasAndStakeToken.yieldDto.validators + yieldWithDifferentGasAndStakeToken.yieldDto.validators, ); return HttpResponse.json({ @@ -232,15 +231,20 @@ export const setup = () => { return HttpResponse.json({ ...yieldApiActionFixture({ - action: selectedYield.actionDto as ActionDto, + action: selectedYield.actionDto as ReturnType< + typeof enterResponseFixture + >, address: body.address, rawArguments: body.arguments ?? null, transactions: selectedYield.actionDto.transactions.map((tx, index) => - yieldApiTransactionFixture(tx as TransactionDto, { - gasEstimate: gasAmount, - status: "CREATED", - stepIndex: index, - }) + yieldApiTransactionFixture( + tx as ReturnType, + { + gasEstimate: gasAmount, + status: "CREATED", + stepIndex: index, + }, + ), ), overrides: { amount: body.arguments?.amount ?? null, @@ -248,7 +252,7 @@ export const setup = () => { }, }), }); - }) + }), ); const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; diff --git a/packages/widget/tests/use-cases/geo-block.test.tsx b/packages/widget/tests/use-cases/geo-block.test.tsx index 209af567..4976826a 100644 --- a/packages/widget/tests/use-cases/geo-block.test.tsx +++ b/packages/widget/tests/use-cases/geo-block.test.tsx @@ -15,9 +15,9 @@ describe("Geo block", () => { regionCode: "AT-9", type: "GEO_LOCATION", }, - { status: 403 } + { status: 403 }, ); - }) + }), ); const app = await renderApp(); diff --git a/packages/widget/tests/use-cases/renders-initial-page.test.tsx b/packages/widget/tests/use-cases/renders-initial-page.test.tsx index e68efdbf..c28b5646 100644 --- a/packages/widget/tests/use-cases/renders-initial-page.test.tsx +++ b/packages/widget/tests/use-cases/renders-initial-page.test.tsx @@ -1,4 +1,3 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { describe, expect, it } from "vitest"; @@ -6,9 +5,11 @@ import { yieldFixture } from "../fixtures"; import { worker } from "../mocks/worker"; import { renderApp } from "../utils/test-utils"; +type LegacyTokenDto = ReturnType["token"]; + describe("Renders initial page", () => { it("Works as expected", async () => { - const avalancheCToken: TokenDto = { + const avalancheCToken: LegacyTokenDto = { name: "Avalanche C Chain", symbol: "AVAX", decimals: 18, @@ -17,7 +18,7 @@ describe("Renders initial page", () => { logoURI: "https://assets.stakek.it/tokens/avax.svg", }; - const ether: TokenDto = { + const ether: LegacyTokenDto = { network: "ethereum", name: "Ethereum", symbol: "ETH", @@ -39,7 +40,7 @@ describe("Renders initial page", () => { type: "staking", gasFeeToken: avalancheCToken, }, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); @@ -56,7 +57,7 @@ describe("Renders initial page", () => { type: "staking", gasFeeToken: ether, }, - }) satisfies YieldDto + }) satisfies ReturnType, ) .unsafeCoerce(); @@ -90,7 +91,7 @@ describe("Renders initial page", () => { await delay(); return HttpResponse.json(avalancheAvaxNativeStaking); - }) + }), ); const app = await renderApp(); diff --git a/packages/widget/tests/use-cases/select-opportunity.test.tsx b/packages/widget/tests/use-cases/select-opportunity.test.tsx index 04b4ee7c..97819790 100644 --- a/packages/widget/tests/use-cases/select-opportunity.test.tsx +++ b/packages/widget/tests/use-cases/select-opportunity.test.tsx @@ -1,4 +1,3 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { describe, expect, it } from "vitest"; @@ -7,10 +6,12 @@ import { yieldFixture } from "../fixtures"; import { worker } from "../mocks/worker"; import { renderApp } from "../utils/test-utils"; +type LegacyTokenDto = ReturnType["token"]; + describe("Select opportunity", () => { // This loads cosmos wagmi config, which takes some time, so we need to increase the timeout it("Works as expected", { timeout: 20000 }, async () => { - const token: TokenDto = { + const token: LegacyTokenDto = { network: "ethereum", name: "Ethereum", symbol: "ETH", @@ -118,11 +119,11 @@ describe("Select opportunity", () => { ...mock.status, enter: integrationId !== "ethereum-eth-stakewise-staking", }, - } as YieldDto; + } as ReturnType; }) .map((val) => HttpResponse.json(val)) .unsafeCoerce(); - }) + }), ); const app = await renderApp(); @@ -147,24 +148,24 @@ describe("Select opportunity", () => { await expect .element( selectContainer.getByTestId( - /^select-opportunity__item_ethereum-eth-lido-staking/ - ) + /^select-opportunity__item_ethereum-eth-lido-staking/, + ), ) .toBeInTheDocument(); await expect .element( selectContainer.getByTestId( - /^select-opportunity__item_ethereum-eth-reth-staking/ - ) + /^select-opportunity__item_ethereum-eth-reth-staking/, + ), ) .toBeInTheDocument(); await expect .element( selectContainer.getByTestId( - /^select-opportunity__item_ethereum-eth-stakewise-staking/ - ) + /^select-opportunity__item_ethereum-eth-stakewise-staking/, + ), ) .not.toBeInTheDocument(); diff --git a/packages/widget/tests/use-cases/sk-wallet.test.tsx b/packages/widget/tests/use-cases/sk-wallet.test.tsx index a458beff..cf2440e4 100644 --- a/packages/widget/tests/use-cases/sk-wallet.test.tsx +++ b/packages/widget/tests/use-cases/sk-wallet.test.tsx @@ -13,7 +13,7 @@ import { worker } from "../mocks/worker"; import { renderHook } from "../utils/test-utils"; const renderHookWithExternalProvider = ( - externalProviders: SKExternalProviders + externalProviders: SKExternalProviders, ) => renderHook(useSKWallet, { wrapper: ({ children }) => ( @@ -44,7 +44,7 @@ describe("SK Wallet", () => { http.get("*/v1/yields/enabled/networks", async () => { await delay(); return HttpResponse.json([MiscNetworks.Solana]); - }) + }), ); const solanaWallet = await renderHookWithExternalProvider({ @@ -109,7 +109,7 @@ describe("SK Wallet", () => { structuredTransaction: null, annotatedTransaction: null, providersDetails: [], - } + }, ); }); @@ -121,7 +121,7 @@ describe("SK Wallet", () => { http.get("*/v1/yields/enabled/networks", async () => { await delay(); return HttpResponse.json([MiscNetworks.Ton]); - }) + }), ); const tonWallet = await renderHookWithExternalProvider({ @@ -185,7 +185,7 @@ describe("SK Wallet", () => { structuredTransaction: null, annotatedTransaction: null, providersDetails: [], - } + }, ); }); }); diff --git a/packages/widget/tests/use-cases/staking-flow/setup.ts b/packages/widget/tests/use-cases/staking-flow/setup.ts index b4ac4d6b..118f1f6b 100644 --- a/packages/widget/tests/use-cases/staking-flow/setup.ts +++ b/packages/widget/tests/use-cases/staking-flow/setup.ts @@ -329,7 +329,7 @@ export const setup = async () => { amount: body.arguments?.amount ?? null, amountRaw: body.arguments?.amount ?? null, }, - }) + }), ); }), http.put("*/v1/transactions/:transactionId/submit-hash", async (info) => { @@ -342,7 +342,7 @@ export const setup = async () => { id: transactionId, hash: "transaction_hash", status: "BROADCASTED", - }) + }), ); }), http.get("*/v1/transactions/:transactionId", async (info) => { @@ -354,9 +354,9 @@ export const setup = async () => { "https://snowtrace.dev/tx/0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", hash: "0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", status: "CONFIRMED", - }) + }), ); - }) + }), ); const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; diff --git a/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx b/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx index 354d8f10..c76f71cc 100644 --- a/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx +++ b/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx @@ -38,7 +38,7 @@ describe("Staking flow", () => { await expect .poll( - () => app.getByTestId("select-opportunity").getByText("AVAX").length + () => app.getByTestId("select-opportunity").getByText("AVAX").length, ) .greaterThan(0); @@ -61,7 +61,7 @@ describe("Staking flow", () => { .element( app .getByTestId("estimated-reward__yearly") - .getByText(`0.00508 ${yieldOp.token.symbol}`) + .getByText(`0.00508 ${yieldOp.token.symbol}`), ) .toBeInTheDocument(); @@ -69,12 +69,18 @@ describe("Staking flow", () => { .element( app .getByTestId("estimated-reward__monthly") - .getByText(`0.00042 ${yieldOp.token.symbol}`) + .getByText(`0.00042 ${yieldOp.token.symbol}`), ) .toBeInTheDocument(); const rewardTokenDetails = ( - await renderHook(() => useRewardTokenDetails(Just(yieldOp))) + await renderHook(() => + useRewardTokenDetails( + Just(yieldOp) as unknown as Parameters< + typeof useRewardTokenDetails + >[0], + ), + ) ).result.current.unsafeCoerce(); await expect @@ -82,7 +88,7 @@ describe("Staking flow", () => { .toBeInTheDocument(); await expect .element( - app.getByText(`${rewardTokenDetails.rewardTokens[0].symbol}`).first() + app.getByText(`${rewardTokenDetails.rewardTokens[0].symbol}`).first(), ) .toBeInTheDocument(); @@ -91,7 +97,7 @@ describe("Staking flow", () => { await userEvent.click(app.getByText("Stake").last()); const totalGasFee = new BigNumber( - transactionConstruct.gasEstimate?.amount ?? 0 + transactionConstruct.gasEstimate?.amount ?? 0, ); await expect @@ -100,8 +106,8 @@ describe("Staking flow", () => { .getByTestId("estimated_gas_fee") .getByText( `${formatNumber(totalGasFee, 10)} ${yieldOp.token.symbol}`, - { exact: false } - ) + { exact: false }, + ), ) .toBeInTheDocument(); @@ -139,8 +145,8 @@ describe("Staking flow", () => { await expect .element( app.getByText( - `Successfully staked ${stakeAmount} ${yieldOp.token.symbol}` - ) + `Successfully staked ${stakeAmount} ${yieldOp.token.symbol}`, + ), ) .toBeInTheDocument(); await expect diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts index cc6302cb..1902c8a8 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts +++ b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts @@ -1,4 +1,3 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { avalanche } from "viem/chains"; import { vitest } from "vitest"; @@ -17,6 +16,8 @@ import { import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; +type LegacyTokenDto = ReturnType["token"]; + const setUrl = ({ accountId, balanceId, @@ -48,7 +49,7 @@ const setUrl = ({ export const setup = async () => { const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; - const token: TokenDto = { + const token: LegacyTokenDto = { name: "USDA", symbol: "USDA", decimals: 18, @@ -57,7 +58,7 @@ export const setup = async () => { logoURI: "https://assets.stakek.it/tokens/usda.svg", }; - const rewardToken: TokenDto = { + const rewardToken: LegacyTokenDto = { name: "United Stables", symbol: "U", decimals: 18, @@ -66,7 +67,7 @@ export const setup = async () => { logoURI: "https://assets.stakek.it/tokens/usda.svg", }; - const morphoToken: TokenDto = { + const morphoToken: LegacyTokenDto = { name: "Morpho Token", symbol: "MORPHO", decimals: 18, @@ -137,7 +138,7 @@ export const setup = async () => { const legacyYieldBase = yieldFixture(); const rawYieldBase = yieldApiYieldFixture(); - const legacyYield: YieldDto = { + const legacyYield: ReturnType = { ...legacyYieldBase, id: yieldId, token, @@ -283,7 +284,7 @@ export const setup = async () => { return HttpResponse.json( url.searchParams.has("ledgerWalletAPICompatible") ? legacyYield - : rawYield + : rawYield, ); }), http.get("*/v1/yields/:yieldId/validators", async () => { @@ -305,7 +306,7 @@ export const setup = async () => { ], errors: [], }); - }) + }), ); const requestFn = vitest.fn(async ({ method }: { method: string }) => { diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx index d955411d..8496237b 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx +++ b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx @@ -24,21 +24,21 @@ describe("Trust incentive APY", () => { await expect.element(app.getByText("APY composition")).toBeInTheDocument(); await expect .element( - app.getByTestId("reward-rate-breakdown__native").getByText("4.27%") + app.getByTestId("reward-rate-breakdown__native").getByText("4.27%"), ) .toBeInTheDocument(); await expect .element( app .getByTestId("reward-rate-breakdown__protocol-incentive") - .getByText("0.28%") + .getByText("0.28%"), ) .toBeInTheDocument(); await expect .element( app .getByTestId("reward-rate-breakdown__campaign") - .getByText("Up to 0.20%") + .getByText("Up to 0.20%"), ) .toBeInTheDocument(); @@ -82,21 +82,21 @@ describe("Trust incentive APY", () => { .element( app .getByTestId("personalized-reward-rate-breakdown__native") - .getByText("4.27%") + .getByText("4.27%"), ) .toBeInTheDocument(); await expect .element( app .getByTestId("personalized-reward-rate-breakdown__protocol-incentive") - .getByText("0.28%") + .getByText("0.28%"), ) .toBeInTheDocument(); await expect .element( app .getByTestId("personalized-reward-rate-breakdown__campaign") - .getByText("0.18%") + .getByText("0.18%"), ) .toBeInTheDocument(); diff --git a/packages/widget/tests/use-cases/under-maintenance.test.tsx b/packages/widget/tests/use-cases/under-maintenance.test.tsx index 8384ddae..ccfa9460 100644 --- a/packages/widget/tests/use-cases/under-maintenance.test.tsx +++ b/packages/widget/tests/use-cases/under-maintenance.test.tsx @@ -8,7 +8,7 @@ describe("Under maintenance", () => { worker.use( http.get("*/v2/health", async () => { return HttpResponse.json({ db: "FAIL" }); - }) + }), ); const app = await renderApp(); diff --git a/packages/widget/vite/vite.config.bundle.ts b/packages/widget/vite/vite.config.bundle.ts index b000bb85..4a70e7b2 100644 --- a/packages/widget/vite/vite.config.bundle.ts +++ b/packages/widget/vite/vite.config.bundle.ts @@ -15,5 +15,5 @@ export default defineConfig( outDir: "dist/bundle", sourcemap: false, }, - }) + }), ); diff --git a/packages/widget/vite/vite.config.website.ts b/packages/widget/vite/vite.config.website.ts index 6db14f33..d6e9f0ba 100644 --- a/packages/widget/vite/vite.config.website.ts +++ b/packages/widget/vite/vite.config.website.ts @@ -7,5 +7,5 @@ export default defineConfig( outDir: "dist/website", sourcemap: true, }, - }) + }), ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05be648b..d652dd79 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ importers: .: devDependencies: '@biomejs/biome': - specifier: ^2.3.8 - version: 2.3.8 + specifier: ^2.4.8 + version: 2.4.8 '@changesets/cli': specifier: ^2.29.8 version: 2.29.8(@types/node@25.0.2) @@ -619,59 +619,59 @@ packages: resolution: {integrity: sha512-R0XPZ/IQhU2TtetSFI9vI+7kJOJYNiCncn5ixEBW+/LNaZCo2HK37Mq3pRNzrM4FryuAkyeqY7Ujmj3I3e3t9g==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} - '@biomejs/biome@2.3.8': - resolution: {integrity: sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA==} + '@biomejs/biome@2.4.8': + resolution: {integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.3.8': - resolution: {integrity: sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww==} + '@biomejs/cli-darwin-arm64@2.4.8': + resolution: {integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.3.8': - resolution: {integrity: sha512-lUDQ03D7y/qEao7RgdjWVGCu+BLYadhKTm40HkpJIi6kn8LSv5PAwRlew/DmwP4YZ9ke9XXoTIQDO1vAnbRZlA==} + '@biomejs/cli-darwin-x64@2.4.8': + resolution: {integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.3.8': - resolution: {integrity: sha512-PShR4mM0sjksUMyxbyPNMxoKFPVF48fU8Qe8Sfx6w6F42verbwRLbz+QiKNiDPRJwUoMG1nPM50OBL3aOnTevA==} + '@biomejs/cli-linux-arm64-musl@2.4.8': + resolution: {integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.3.8': - resolution: {integrity: sha512-Uo1OJnIkJgSgF+USx970fsM/drtPcQ39I+JO+Fjsaa9ZdCN1oysQmy6oAGbyESlouz+rzEckLTF6DS7cWse95g==} + '@biomejs/cli-linux-arm64@2.4.8': + resolution: {integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.3.8': - resolution: {integrity: sha512-YGLkqU91r1276uwSjiUD/xaVikdxgV1QpsicT0bIA1TaieM6E5ibMZeSyjQ/izBn4tKQthUSsVZacmoJfa3pDA==} + '@biomejs/cli-linux-x64-musl@2.4.8': + resolution: {integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.3.8': - resolution: {integrity: sha512-QDPMD5bQz6qOVb3kiBui0zKZXASLo0NIQ9JVJio5RveBEFgDgsvJFUvZIbMbUZT3T00M/1wdzwWXk4GIh0KaAw==} + '@biomejs/cli-linux-x64@2.4.8': + resolution: {integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.3.8': - resolution: {integrity: sha512-H4IoCHvL1fXKDrTALeTKMiE7GGWFAraDwBYFquE/L/5r1927Te0mYIGseXi4F+lrrwhSWbSGt5qPFswNoBaCxg==} + '@biomejs/cli-win32-arm64@2.4.8': + resolution: {integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.3.8': - resolution: {integrity: sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w==} + '@biomejs/cli-win32-x64@2.4.8': + resolution: {integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -10762,39 +10762,39 @@ snapshots: multiformats: 13.4.1 uint8arrays: 5.1.0 - '@biomejs/biome@2.3.8': + '@biomejs/biome@2.4.8': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.3.8 - '@biomejs/cli-darwin-x64': 2.3.8 - '@biomejs/cli-linux-arm64': 2.3.8 - '@biomejs/cli-linux-arm64-musl': 2.3.8 - '@biomejs/cli-linux-x64': 2.3.8 - '@biomejs/cli-linux-x64-musl': 2.3.8 - '@biomejs/cli-win32-arm64': 2.3.8 - '@biomejs/cli-win32-x64': 2.3.8 + '@biomejs/cli-darwin-arm64': 2.4.8 + '@biomejs/cli-darwin-x64': 2.4.8 + '@biomejs/cli-linux-arm64': 2.4.8 + '@biomejs/cli-linux-arm64-musl': 2.4.8 + '@biomejs/cli-linux-x64': 2.4.8 + '@biomejs/cli-linux-x64-musl': 2.4.8 + '@biomejs/cli-win32-arm64': 2.4.8 + '@biomejs/cli-win32-x64': 2.4.8 - '@biomejs/cli-darwin-arm64@2.3.8': + '@biomejs/cli-darwin-arm64@2.4.8': optional: true - '@biomejs/cli-darwin-x64@2.3.8': + '@biomejs/cli-darwin-x64@2.4.8': optional: true - '@biomejs/cli-linux-arm64-musl@2.3.8': + '@biomejs/cli-linux-arm64-musl@2.4.8': optional: true - '@biomejs/cli-linux-arm64@2.3.8': + '@biomejs/cli-linux-arm64@2.4.8': optional: true - '@biomejs/cli-linux-x64-musl@2.3.8': + '@biomejs/cli-linux-x64-musl@2.4.8': optional: true - '@biomejs/cli-linux-x64@2.3.8': + '@biomejs/cli-linux-x64@2.4.8': optional: true - '@biomejs/cli-win32-arm64@2.3.8': + '@biomejs/cli-win32-arm64@2.4.8': optional: true - '@biomejs/cli-win32-x64@2.3.8': + '@biomejs/cli-win32-x64@2.4.8': optional: true '@cardano-ogmios/client@6.9.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': From 3ca4c3950d9929029b9c156fb9ccb22a63e15055 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Tue, 24 Mar 2026 17:33:31 +0100 Subject: [PATCH 14/23] feat: move api calls to private-api, formatting --- biome.json | 71 +++++++++------ packages/widget/src/App.tsx | 2 +- packages/widget/src/assets/images/index.ts | 2 +- .../widget/src/common/check-gas-amount.ts | 12 +-- .../widget/src/common/get-token-balances.ts | 8 +- packages/widget/src/common/private-api.ts | 63 +++++++++++++- .../widget/src/components/atoms/box/index.tsx | 2 +- .../components/atoms/collapsible/index.tsx | 2 +- .../src/components/atoms/copy-text/index.tsx | 6 +- .../src/components/atoms/max-button/index.tsx | 2 +- .../components/atoms/number-input/index.tsx | 4 +- .../number-input/use-auto-resize-text.ts | 14 +-- .../components/atoms/select-modal/index.tsx | 4 +- .../hooks/use-variant-network-urls.ts | 2 +- .../hooks/use-variant-token-urls.ts | 4 +- .../token-icon/token-icon-container/index.tsx | 2 +- .../components/atoms/typography/styles.css.ts | 4 +- .../components/atoms/virtual-list/index.tsx | 16 ++-- .../molecules/connect-button/index.tsx | 2 +- .../molecules/global-modals/index.tsx | 2 +- .../src/components/molecules/header/index.tsx | 4 +- .../components/molecules/header/use-header.ts | 2 +- .../components/molecules/help-modal/index.tsx | 4 +- .../select-opportunity-list-item/index.tsx | 14 +-- .../molecules/select-validator/index.tsx | 2 +- .../molecules/select-validator/meta-info.tsx | 4 +- .../select-validator-list.tsx | 4 +- .../molecules/zerion-chain-modal/index.tsx | 4 +- packages/widget/src/domain/index.ts | 8 +- packages/widget/src/domain/types/action.ts | 6 +- .../widget/src/domain/types/chains/index.ts | 2 +- .../widget/src/domain/types/connectors.ts | 2 +- .../src/domain/types/external-providers.ts | 12 +-- .../widget/src/domain/types/pending-action.ts | 14 +-- packages/widget/src/domain/types/positions.ts | 10 +-- packages/widget/src/domain/types/price.ts | 4 +- .../widget/src/domain/types/reward-rate.ts | 8 +- packages/widget/src/domain/types/rewards.ts | 6 +- packages/widget/src/domain/types/stake.ts | 46 +++++----- .../widget/src/domain/types/transaction.ts | 2 +- .../widget/src/domain/types/validators.ts | 2 +- .../domain/types/wallets/generic-wallet.ts | 2 +- packages/widget/src/domain/types/yields.ts | 30 +++---- .../src/hooks/api/use-activity-actions.ts | 16 ++-- .../src/hooks/api/use-default-tokens.ts | 13 +-- .../widget/src/hooks/api/use-multi-yields.ts | 32 +++---- packages/widget/src/hooks/api/use-prices.ts | 43 ++++++---- .../src/hooks/api/use-token-balances-scan.ts | 14 +-- .../widget/src/hooks/api/use-tokens-prices.ts | 2 +- .../src/hooks/api/use-yield-balances-scan.ts | 10 +-- .../get-yield-opportunity.ts | 12 +-- .../src/hooks/api/use-yield-validators.ts | 8 +- .../use-navigate-with-scroll-to-top.ts | 2 +- .../src/hooks/tracking/use-track-page.ts | 2 +- .../src/hooks/use-add-ledger-account.ts | 4 +- .../widget/src/hooks/use-estimated-rewards.ts | 8 +- .../widget/src/hooks/use-gas-warning-check.ts | 10 +-- packages/widget/src/hooks/use-geo-block.ts | 2 +- .../widget/src/hooks/use-handle-deep-links.ts | 14 +-- packages/widget/src/hooks/use-init-params.ts | 6 +- .../widget/src/hooks/use-init-query-params.ts | 10 +-- .../src/hooks/use-invalidate-query-n-times.ts | 4 +- .../src/hooks/use-local-storage-value.ts | 8 +- packages/widget/src/hooks/use-logout.ts | 2 +- .../src/hooks/use-max-min-yield-amount.ts | 10 +-- .../src/hooks/use-position-balance-by-type.ts | 8 +- .../widget/src/hooks/use-position-balances.ts | 2 +- .../widget/src/hooks/use-position-data.ts | 2 +- .../widget/src/hooks/use-positions-data.ts | 8 +- .../widget/src/hooks/use-provider-details.ts | 34 ++++---- .../widget/src/hooks/use-region-code-names.ts | 12 +-- .../get-reward-token-symbols.tsx | 2 +- .../hooks/use-reward-token-details/index.ts | 6 +- .../widget/src/hooks/use-rewards-summary.ts | 20 ++--- packages/widget/src/hooks/use-rich-errors.ts | 4 +- .../src/hooks/use-staked-or-liquid-balance.ts | 6 +- packages/widget/src/hooks/use-summary.tsx | 42 ++++----- .../src/hooks/use-sync-element-height.ts | 2 +- .../widget/src/hooks/use-under-maintenance.ts | 2 +- .../widget/src/hooks/use-validators-config.ts | 4 +- .../widget/src/hooks/use-yield-meta-info.tsx | 4 +- packages/widget/src/main.tsx | 2 +- .../containers/animation-layout.tsx | 2 +- .../activity/action-list-item/index.tsx | 6 +- .../activity/activity-details.page.tsx | 4 +- .../activity/activity.page.tsx | 4 +- .../common/components/header.tsx | 2 +- .../utila-select-validator-section.tsx | 2 +- .../components/positions-list-item.tsx | 16 ++-- .../positions/hooks/use-position-list-item.ts | 14 +-- .../components/position-details-actions.tsx | 14 +-- .../components/position-details-info.tsx | 6 +- .../components/provider-details.tsx | 4 +- .../position-details/index.tsx | 2 +- .../components/positions-list-item.tsx | 18 ++-- .../hooks/use-activity-complete.hook.ts | 14 +-- .../pages/complete/hooks/use-complete.hook.ts | 4 +- .../src/pages/complete/pages/common.page.tsx | 12 +-- .../complete/pages/pending-complete.page.tsx | 10 +-- .../complete/pages/stake-complete.page.tsx | 12 +-- .../complete/pages/unstake-complete.page.tsx | 10 +-- .../widget/src/pages/complete/state/index.tsx | 2 +- .../pages/components/footer-outlet/index.tsx | 4 +- .../components/layout/layout-context.tsx | 6 +- .../src/pages/components/meta-info/index.tsx | 4 +- .../components/action-list-item/index.tsx | 4 +- .../components/list-item-bullet/index.tsx | 4 +- .../hooks/use-action-list-item.ts | 14 +-- .../state/activity-page.context.tsx | 14 +-- .../details/details-page/components/tabs.tsx | 2 +- .../details/details-page/details.page.tsx | 2 +- .../components/select-provider/index.tsx | 6 +- .../components/select-token-section/index.tsx | 10 +-- .../select-token-list-item.tsx | 6 +- .../select-token-section/select-token.tsx | 4 +- .../select-validator-section/index.tsx | 4 +- .../select-validator-trigger.tsx | 2 +- .../select-opportunity.tsx | 6 +- .../select-yield-reward-details.tsx | 14 +-- .../select-yield-section/staked-via.tsx | 2 +- .../use-animate-yield-percent.ts | 4 +- .../src/pages/details/earn-page/earn.page.tsx | 2 +- .../earn-page/state/earn-page-context.tsx | 79 ++++++++--------- .../state/earn-page-state-context.tsx | 38 ++++---- .../earn-page/state/use-amount-validation.ts | 8 +- .../earn-page/state/use-get-init-yield.ts | 6 +- .../state/use-get-token-balances-map.ts | 4 +- .../details/earn-page/state/use-init-token.ts | 14 +-- .../details/earn-page/state/use-init-yield.ts | 14 +-- .../state/use-pending-action-deep-link.ts | 36 ++++---- .../state/use-stake-enter-request-dto.ts | 4 +- .../earn-page/state/use-token-balance.ts | 8 +- .../earn-page/state/use-track-state-events.ts | 4 +- .../pages/details/earn-page/state/utils.ts | 4 +- .../components/positions-list-item.tsx | 14 +-- .../hooks/use-position-list-item.ts | 22 ++--- .../positions-page/hooks/use-positions.ts | 28 +++--- .../components/amount-block.tsx | 10 +-- .../components/provider-details.tsx | 4 +- .../components/static-action-block.tsx | 4 +- .../hooks/use-pending-actions.ts | 32 +++---- .../hooks/use-position-details.ts | 20 ++--- .../hooks/use-stake-exit-request-dto.ts | 12 +-- .../hooks/use-unstake-machine.ts | 37 ++++---- .../hooks/use-validator-addresses-handling.ts | 10 +-- .../src/pages/position-details/hooks/utils.ts | 6 +- .../position-details.page.tsx | 18 ++-- .../pages/position-details/state/index.tsx | 54 ++++++------ .../review/hooks/use-action-review.hook.ts | 40 ++++----- .../widget/src/pages/review/hooks/use-fees.ts | 20 ++--- .../review/hooks/use-pending-review.hook.ts | 30 +++---- .../review/hooks/use-stake-review.hook.ts | 38 ++++---- .../review/hooks/use-unstake-review.hook.ts | 22 +++-- .../pages/review/pages/action-review.page.tsx | 4 +- .../review/pages/pending-review.page.tsx | 4 +- .../pages/review/pages/stake-review.page.tsx | 4 +- .../review/pages/unstake-review.page.tsx | 4 +- .../steps/hooks/use-steps-machine.hook.ts | 62 ++++++------- .../src/pages/steps/hooks/use-steps.hook.ts | 14 +-- .../pages/steps/pages/activity-steps.page.tsx | 6 +- .../pages/steps/pages/pending-steps.page.tsx | 6 +- .../pages/steps/pages/stake-steps.page.tsx | 6 +- .../widget/src/pages/steps/pages/tx-state.tsx | 2 +- .../pages/steps/pages/unstake-steps.page.tsx | 6 +- .../src/providers/activity-provider/index.tsx | 4 +- .../src/providers/api/get-enabled-networks.ts | 6 +- .../cosmos/chains/get-chain-registry.ts | 8 +- .../src/providers/cosmos/chains/index.ts | 2 +- .../widget/src/providers/cosmos/config.ts | 24 +++--- .../providers/cosmos/cosmos-connector-meta.ts | 2 +- .../src/providers/cosmos/cosmos-connector.ts | 16 ++-- .../providers/cosmos/wallet-connect/client.ts | 2 +- .../cosmos/wallet-connect/main-wallet.ts | 2 +- .../cosmos/wallet-connect/registry.ts | 2 +- .../src/providers/cosmos/wallet-manager.ts | 10 +-- .../src/providers/enter-stake-store/index.tsx | 4 +- .../widget/src/providers/ethereum/config.ts | 8 +- .../ethereum/finery-wallet-list/index.ts | 8 +- .../widget/src/providers/ethereum/utils.ts | 4 +- .../src/providers/exit-stake-store/index.tsx | 4 +- .../src/providers/external-provider/index.ts | 22 ++--- .../widget/src/providers/ledger/config.ts | 8 +- .../src/providers/ledger/ledger-connector.ts | 44 +++++----- .../ledger/ledger-live-connector-meta.ts | 2 +- packages/widget/src/providers/ledger/utils.ts | 14 +-- .../providers/misc/cardano-connector-meta.ts | 2 +- .../src/providers/misc/cardano-connector.ts | 4 +- packages/widget/src/providers/misc/config.ts | 26 +++--- .../providers/misc/solana-connector-meta.ts | 2 +- .../src/providers/misc/solana-connector.ts | 8 +- .../src/providers/misc/ton-connector-meta.ts | 2 +- .../src/providers/misc/ton-connector.ts | 12 +-- .../src/providers/misc/tron-connector-meta.ts | 2 +- .../src/providers/misc/tron-connector.ts | 4 +- .../src/providers/mount-animation/index.tsx | 10 +-- .../providers/pending-action-store/index.tsx | 4 +- .../src/providers/query-client/index.tsx | 2 +- packages/widget/src/providers/rainbow-kit.tsx | 6 +- .../widget/src/providers/rainbow/index.tsx | 6 +- .../src/providers/root-element/index.tsx | 4 +- packages/widget/src/providers/safe/config.ts | 2 +- .../src/providers/safe/safe-connector-meta.ts | 2 +- .../src/providers/safe/safe-connector.ts | 8 +- .../widget/src/providers/settings/index.tsx | 10 +-- .../widget/src/providers/sk-wallet/index.tsx | 86 +++++++++---------- .../sk-wallet/use-additional-addresses.ts | 10 +-- .../sk-wallet/use-connector-chains.ts | 2 +- .../src/providers/sk-wallet/use-cosmos-cw.ts | 4 +- .../src/providers/sk-wallet/use-init.ts | 10 +-- .../sk-wallet/use-ledger-accounts.ts | 2 +- .../use-ledger-current-account-id.ts | 4 +- .../sk-wallet/use-ledger-disabled-chains.ts | 2 +- .../sk-wallet/use-sync-external-provider.ts | 8 +- .../src/providers/stake-history/index.tsx | 4 +- .../widget/src/providers/substrate/config.ts | 14 +-- .../substrate/substrate-connector-meta.ts | 2 +- .../substrate/substrate-connector.ts | 32 +++---- .../widget/src/providers/theme-wrapper.tsx | 8 +- .../widget/src/providers/tracking/index.tsx | 12 +-- packages/widget/src/providers/wagmi/index.ts | 36 ++++---- packages/widget/src/providers/wagmi/utils.ts | 2 +- .../yield-api-client-provider/actions.ts | 37 ++------ .../yield-api-client-provider/index.tsx | 6 +- .../request-helpers.ts | 2 +- packages/widget/src/services/local-storage.ts | 14 +-- packages/widget/src/styles/theme/atoms.css.ts | 4 +- .../widget/src/styles/theme/contract.css.ts | 2 +- .../widget/src/styles/theme/global.css.ts | 14 +-- .../widget/src/styles/tokens/breakpoints.ts | 2 +- packages/widget/src/translation/index.ts | 8 +- packages/widget/src/types/purify-extend.d.ts | 2 +- .../widget/src/utils/create-state-context.ts | 4 +- packages/widget/src/utils/date.ts | 2 +- packages/widget/src/utils/extend-purify.ts | 2 +- packages/widget/src/utils/formatters.ts | 8 +- packages/widget/src/utils/index.ts | 18 ++-- packages/widget/src/utils/mappers.ts | 4 +- packages/widget/src/utils/maybe-window.ts | 2 +- packages/widget/src/utils/memoize.ts | 2 +- packages/widget/src/utils/text.ts | 2 +- packages/widget/src/worker.ts | 4 +- packages/widget/tests/fixtures/index.ts | 14 +-- .../deep-links-flow/deep-links-flow.test.tsx | 12 +-- .../deep-links-flow/param-validation.test.tsx | 18 ++-- .../tests/use-cases/deep-links-flow/setup.ts | 8 +- .../external-provider.test.tsx | 22 ++--- .../use-cases/external-provider/setup.ts | 12 +-- .../gas-warning-flow.test.tsx | 6 +- .../tests/use-cases/gas-warning-flow/setup.ts | 14 +-- .../widget/tests/use-cases/geo-block.test.tsx | 4 +- .../use-cases/renders-initial-page.test.tsx | 6 +- .../use-cases/select-opportunity.test.tsx | 14 +-- .../widget/tests/use-cases/sk-wallet.test.tsx | 10 +-- .../tests/use-cases/staking-flow/setup.ts | 8 +- .../staking-flow/staking-flow.test.tsx | 22 ++--- .../use-cases/trust-incentive-apy/setup.ts | 4 +- .../trust-incentive-apy.test.tsx | 12 +-- .../use-cases/under-maintenance.test.tsx | 2 +- packages/widget/vite/vite.config.bundle.ts | 2 +- packages/widget/vite/vite.config.website.ts | 2 +- 260 files changed, 1381 insertions(+), 1338 deletions(-) diff --git a/biome.json b/biome.json index 027a6627..ec464ce6 100644 --- a/biome.json +++ b/biome.json @@ -1,53 +1,70 @@ { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true - }, "files": { - "ignoreUnknown": false, "includes": [ "**", "!**/node_modules", "!**/dist", + "!**/.next", + "!**/next", + "!**/.turbo", + "!**/node_modules", + "!**/dist", + "!**/.next", "!**/.turbo", - "!**/.vscode", - "!**/routeTree.gen.ts", - "!**/client-factory.ts", - "!**/api-schemas.ts", - "!**/opensrc" + "!**/region-iso-3166-codes.ts", + "!**/mockServiceWorker.js", + "!**/package.json" ] }, "formatter": { "enabled": true, - "indentStyle": "space" + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 80 }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { "enabled": true, "rules": { "recommended": true, - "complexity": { - "noBannedTypes": "off" - } - } - }, - "css": { - "parser": { - "tailwindDirectives": true + "style": { + "noNonNullAssertion": "off", + "noParameterAssign": "error", + "useAsConstAssertion": "error", + "useDefaultParameterLast": "error", + "useEnumInitializers": "error", + "useSelfClosingElements": "error", + "useSingleVarDeclarator": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error" + }, + "suspicious": { + "noArrayIndexKey": "off", + "useIterableCallbackReturn": "off" + }, + "a11y": { "noSvgWithoutTitle": "off" }, + "correctness": { + "useJsxKeyInIterable": "off", + "useUniqueElementIds": "off" + }, + "complexity": { "noForEach": "off", "useIndexOf": "off" } } }, "javascript": { "formatter": { - "quoteStyle": "double" + "semicolons": "always", + "arrowParentheses": "always", + "trailingCommas": "es5" } }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "on" - } + "html": { + "formatter": { + "enabled": true, + "indentStyle": "space" } } } diff --git a/packages/widget/src/App.tsx b/packages/widget/src/App.tsx index 2923bd25..655fa278 100644 --- a/packages/widget/src/App.tsx +++ b/packages/widget/src/App.tsx @@ -43,7 +43,7 @@ export const SKApp = (props: SKAppProps) => { : { variant: props.variant ?? "default" }; const [router] = useState(() => - createMemoryRouter([{ path: "*", Component: Root }]), + createMemoryRouter([{ path: "*", Component: Root }]) ); return ( diff --git a/packages/widget/src/assets/images/index.ts b/packages/widget/src/assets/images/index.ts index 2e4c9fb0..2334a5ac 100644 --- a/packages/widget/src/assets/images/index.ts +++ b/packages/widget/src/assets/images/index.ts @@ -17,5 +17,5 @@ export const preloadImages = () => Object.values(images).forEach((src) => { const img = new Image(); img.src = src; - }), + }) ); diff --git a/packages/widget/src/common/check-gas-amount.ts b/packages/widget/src/common/check-gas-amount.ts index e82a07fa..c96cbbdf 100644 --- a/packages/widget/src/common/check-gas-amount.ts +++ b/packages/widget/src/common/check-gas-amount.ts @@ -1,10 +1,10 @@ -import { tokenGetTokenBalances } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { EitherAsync, Left, List, Maybe, Right } from "purify-ts"; import { equalTokens } from "../domain"; import type { ActionDtoWithGasEstimate } from "../domain/types/action"; import type { AddressWithTokenDto } from "../domain/types/addresses"; import type { TokenDto } from "../domain/types/tokens"; +import { tokenGetTokenBalances } from "./private-api"; type CheckGasAmountIfStake = | { isStake: true; stakeToken: TokenDto; stakeAmount: BigNumber } @@ -19,10 +19,10 @@ export const checkGasAmount = ({ gasEstimate: ActionDtoWithGasEstimate["gasEstimate"]; } & CheckGasAmountIfStake) => EitherAsync.liftEither( - Maybe.fromNullable(gasEstimate).toEither(new GasTokenMissingError()), + Maybe.fromNullable(gasEstimate).toEither(new GasTokenMissingError()) ).chain((gasEstimate) => EitherAsync(() => - tokenGetTokenBalances({ addresses: [addressWithTokenDto] }), + tokenGetTokenBalances({ addresses: [addressWithTokenDto] }) ) .mapLeft(() => new GetGasTokenError()) .chain((res) => @@ -32,7 +32,7 @@ export const checkGasAmount = ({ ...val, amount: new BigNumber(val.amount ?? 0), })) - .toEither(new GasTokenMissingError()), + .toEither(new GasTokenMissingError()) ).chain(async (gasTokenBalance) => { const amount = rest.isStake && equalTokens(gasTokenBalance.token, rest.stakeToken) @@ -44,9 +44,9 @@ export const checkGasAmount = ({ } return Right(null); - }), + }) ) - .chainLeft(async (e) => Right(e)), + .chainLeft(async (e) => Right(e)) ); export class NotEnoughGasTokenError extends Error { diff --git a/packages/widget/src/common/get-token-balances.ts b/packages/widget/src/common/get-token-balances.ts index 4082dae2..2a739960 100644 --- a/packages/widget/src/common/get-token-balances.ts +++ b/packages/widget/src/common/get-token-balances.ts @@ -26,7 +26,7 @@ export const getTokenBalances = ({ enabledYieldsOnly: tokensForEnabledYieldsOnly, }), EitherAsync.liftEither( - Right({ additionalAddresses, address, network }), + Right({ additionalAddresses, address, network }) ).chain(async (params) => { if (!params.address || !params.network) { return Right([]); @@ -48,7 +48,7 @@ export const getTokenBalances = ({ tokenBalances.map((b) => ({ defaultTokens: d, tokenBalancesScan: b, - })), - ), - ), + })) + ) + ) ).mapLeft(() => new Error("could not get tokens")); diff --git a/packages/widget/src/common/private-api.ts b/packages/widget/src/common/private-api.ts index 335cfa54..1e2b0dd8 100644 --- a/packages/widget/src/common/private-api.ts +++ b/packages/widget/src/common/private-api.ts @@ -1,8 +1,18 @@ import { + type BalanceResponseDto, + type BalancesRequestDto, customFetch, + type PriceRequestDto, + type PriceResponseDto, type TokenBalanceScanDto, type TokenBalanceScanResponseDto, + type TokenGetTokensParams, + type TokenWithAvailableYieldsDto, + type TransactionVerificationMessageDto, + type TransactionVerificationMessageRequestDto, type YieldDto, + type YieldRewardsSummaryRequestDto, + type YieldRewardsSummaryResponseDto, } from "@stakekit/api-hooks"; /** @@ -11,7 +21,7 @@ import { */ export const tokenTokenBalancesScan = ( tokenBalanceScanDto: TokenBalanceScanDto, - signal?: AbortSignal, + signal?: AbortSignal ) => { return customFetch({ url: "/v1/tokens/balances/scan", @@ -29,7 +39,7 @@ export const tokenTokenBalancesScan = ( export const yieldYieldOpportunity = ( integrationId: string, params?: { ledgerWalletAPICompatible?: boolean }, - signal?: AbortSignal, + signal?: AbortSignal ) => { return customFetch({ url: `/v1/yields/${integrationId}`, @@ -39,3 +49,52 @@ export const yieldYieldOpportunity = ( signal, }); }; + +export const tokenGetTokens = ( + params?: TokenGetTokensParams, + signal?: AbortSignal +) => + customFetch({ + url: "/v1/tokens", + method: "GET", + params, + signal, + }); + +export const tokenGetTokenPrices = (priceRequestDto: PriceRequestDto) => + customFetch({ + url: "/v1/tokens/prices", + method: "POST", + headers: { "Content-Type": "application/json" }, + data: priceRequestDto, + }); + +export const tokenGetTokenBalances = (balancesRequestDto: BalancesRequestDto) => + customFetch({ + url: "/v1/tokens/balances", + method: "POST", + headers: { "Content-Type": "application/json" }, + data: balancesRequestDto, + }); + +export const yieldGetSingleYieldRewardsSummary = ( + integrationId: string, + yieldRewardsSummaryRequestDto: YieldRewardsSummaryRequestDto +) => + customFetch({ + url: `/v1/yields/${integrationId}/rewards-summary`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: yieldRewardsSummaryRequestDto, + }); + +export const transactionGetTransactionVerificationMessageForNetwork = ( + network: string, + transactionVerificationMessageRequestDto: TransactionVerificationMessageRequestDto +) => + customFetch({ + url: `/v1/transactions/verification/${network}`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: transactionVerificationMessageRequestDto, + }); diff --git a/packages/widget/src/components/atoms/box/index.tsx b/packages/widget/src/components/atoms/box/index.tsx index 50aea9d3..1ade0b55 100644 --- a/packages/widget/src/components/atoms/box/index.tsx +++ b/packages/widget/src/components/atoms/box/index.tsx @@ -44,5 +44,5 @@ export const Box = forwardRef( ref, ...nativeProps, }); - }, + } ); diff --git a/packages/widget/src/components/atoms/collapsible/index.tsx b/packages/widget/src/components/atoms/collapsible/index.tsx index 686f2861..7bbd5926 100644 --- a/packages/widget/src/components/atoms/collapsible/index.tsx +++ b/packages/widget/src/components/atoms/collapsible/index.tsx @@ -42,7 +42,7 @@ export const CollapsibleRoot = ({ children, ...controlledProps }: Props) => { collapsed: internalState[0], onClick: () => internalState[1]((prev) => !prev), }, - [controlledProps.onClick, controlledProps.collapsed, internalState], + [controlledProps.onClick, controlledProps.collapsed, internalState] ); return ( diff --git a/packages/widget/src/components/atoms/copy-text/index.tsx b/packages/widget/src/components/atoms/copy-text/index.tsx index e4eb06d8..0704a0b4 100644 --- a/packages/widget/src/components/atoms/copy-text/index.tsx +++ b/packages/widget/src/components/atoms/copy-text/index.tsx @@ -20,7 +20,7 @@ type CopyTextContextType = { }; const CopyTextContext = createContext( - undefined, + undefined ); const useCopyText = () => { @@ -48,7 +48,7 @@ export const Provider = ({ MaybeWindow.ifJust((w) => w.navigator.clipboard.writeText(text)); setShowCopySuccess(true); }, - [text], + [text] ); useEffect(() => { @@ -63,7 +63,7 @@ export const Provider = ({ const value = useMemo( () => ({ showCopySuccess, onClick }), - [onClick, showCopySuccess], + [onClick, showCopySuccess] ); return ( diff --git a/packages/widget/src/components/atoms/max-button/index.tsx b/packages/widget/src/components/atoms/max-button/index.tsx index d50c7a51..a65cf552 100644 --- a/packages/widget/src/components/atoms/max-button/index.tsx +++ b/packages/widget/src/components/atoms/max-button/index.tsx @@ -30,7 +30,7 @@ export const MaxButton = ({ className={clsx( pressAnimation, combineRecipeWithVariant({ rec: container, variant }), - className, + className )} {...rest} > diff --git a/packages/widget/src/components/atoms/number-input/index.tsx b/packages/widget/src/components/atoms/number-input/index.tsx index 5a30e5fa..0acc9106 100644 --- a/packages/widget/src/components/atoms/number-input/index.tsx +++ b/packages/widget/src/components/atoms/number-input/index.tsx @@ -121,11 +121,11 @@ export const NumberInput = memo( {localState} , - rootElement, + rootElement )} ); - }, + } ); const stringToBigNumber = (str: string) => diff --git a/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts b/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts index 4088c130..e0cc8f0f 100644 --- a/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts +++ b/packages/widget/src/components/atoms/number-input/use-auto-resize-text.ts @@ -54,7 +54,7 @@ const scale = ({ const descendingFontSizes = getDescendingFontSizes(inputEl); let currentFontSize = Number.parseFloat( - w.getComputedStyle(spanEl).fontSize, + w.getComputedStyle(spanEl).fontSize ); for (const fs of descendingFontSizes) { @@ -72,8 +72,8 @@ const convertRemToPixels = (rem: number) => rem * Number.parseFloat( MaybeDocument.map( - (doc) => getComputedStyle(doc.documentElement).fontSize, - ).orDefault("0"), + (doc) => getComputedStyle(doc.documentElement).fontSize + ).orDefault("0") ); const getDescendingFontSizes = (el: HTMLElement) => @@ -89,8 +89,8 @@ const getDescendingFontSizes = (el: HTMLElement) => Number.parseFloat( w .getComputedStyle(el) - .getPropertyValue(fs.replace(/(var\()|(\))/g, "")), - ), - ), - ), + .getPropertyValue(fs.replace(/(var\()|(\))/g, "")) + ) + ) + ) ).orDefault([]); diff --git a/packages/widget/src/components/atoms/select-modal/index.tsx b/packages/widget/src/components/atoms/select-modal/index.tsx index 83ce8970..ff681e9f 100644 --- a/packages/widget/src/components/atoms/select-modal/index.tsx +++ b/packages/widget/src/components/atoms/select-modal/index.tsx @@ -55,7 +55,7 @@ export type SelectModalProps = SelectModalWithoutStateProps & { }; const SelectModalContext = createContext( - undefined, + undefined ); const useSelectModalContext = () => { @@ -209,7 +209,7 @@ export const SelectModal = ({ state, ...props }: SelectModalProps) => { isOpen, setOpen: (val) => setOpen(val), }, - [isOpen, state], + [isOpen, state] ); return ( diff --git a/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts b/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts index 3845a437..3746987b 100644 --- a/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts +++ b/packages/widget/src/components/atoms/token-icon/token-icon-container/hooks/use-variant-network-urls.ts @@ -35,6 +35,6 @@ export const useVariantNetworkUrls = (network: Networks) => { return useMemo( () => getVariantNetworkUrl({ chainIconMapping, network }), - [chainIconMapping, network], + [chainIconMapping, network] ); }; 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 ade5c6d3..8f948a0b 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 @@ -10,7 +10,7 @@ import { useSettings } from "../../../../../providers/settings"; export const useVariantTokenUrls = ( token: TokenDto | YieldTokenDto, - metadata?: YieldMetadata, + metadata?: YieldMetadata ): { mainUrl: string | undefined; fallbackUrl: string | undefined; @@ -23,7 +23,7 @@ export const useVariantTokenUrls = ( if (metadata) { const mainUrl = Maybe.fromFalsy(variant === "zerion") .filter(() => - skETHIconUrlsSuffix.some((v) => metadata.logoURI.endsWith(v)), + skETHIconUrlsSuffix.some((v) => metadata.logoURI.endsWith(v)) ) .map(() => zerionETHIcon) .orDefault(metadata.logoURI); 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 916af561..c5465e79 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 @@ -25,7 +25,7 @@ export const TokenIconContainer = ({ }: TokenIconContainerProps) => { const { mainUrl, fallbackUrl, name, providerIcon } = useVariantTokenUrls( token, - metadata, + metadata ); const networkLogoUri = useVariantNetworkUrls(token.network as Networks); diff --git a/packages/widget/src/components/atoms/typography/styles.css.ts b/packages/widget/src/components/atoms/typography/styles.css.ts index 24d4c7fe..47048ac7 100644 --- a/packages/widget/src/components/atoms/typography/styles.css.ts +++ b/packages/widget/src/components/atoms/typography/styles.css.ts @@ -34,7 +34,7 @@ export const heading = recipe({ { fontSize: typeof vars.heading.h1.mobile.fontSize } >; } - >, + > ), }, weight: { @@ -96,7 +96,7 @@ export const textStyles = recipe({ { fontSize: typeof vars.text.small.mobile.fontSize } >; } - >, + > ), }, }, diff --git a/packages/widget/src/components/atoms/virtual-list/index.tsx b/packages/widget/src/components/atoms/virtual-list/index.tsx index 21b76f42..eb88c416 100644 --- a/packages/widget/src/components/atoms/virtual-list/index.tsx +++ b/packages/widget/src/components/atoms/virtual-list/index.tsx @@ -74,7 +74,7 @@ export const VirtualList = ({ List.head([...virtualItems].reverse()) .filter((item) => item.index >= data.length - 1) .isJust(), - [virtualItems, data.length], + [virtualItems, data.length] ); const fetchNextPageRef = useSavedRef(fetchNextPage); @@ -143,7 +143,7 @@ export const GroupedVirtualList = ({ const rowVirtualizer = useVirtualizer({ count: groupCounts.reduce( (acc, numChildren) => acc + numChildren, - groupCounts.length, + groupCounts.length ), getScrollElement: () => innerRef.current, estimateSize, @@ -182,17 +182,17 @@ export const GroupedVirtualList = ({ type: "child", index: acc.childIndex + i, parentIndex: parentIndex, - }) satisfies ChildResult, - ), + }) satisfies ChildResult + ) ); acc.childIndex += numChildren; return acc; }, - { resultArray: [] as ResultsArray[], childIndex: 0 }, + { resultArray: [] as ResultsArray[], childIndex: 0 } ), - [groupCounts], + [groupCounts] ); const isEndReached = useMemo( @@ -200,7 +200,7 @@ export const GroupedVirtualList = ({ List.head([...virtualItems].reverse()) .filter((item) => item.index >= resultArray.length - 1) .isJust(), - [virtualItems, resultArray.length], + [virtualItems, resultArray.length] ); useEffect(() => { @@ -257,7 +257,7 @@ export const GroupedVirtualList = ({ const useIsTabletOrBigger = () => { const [windowWidth] = useState(() => - MaybeWindow.map((w) => w.innerWidth).orDefault(0), + MaybeWindow.map((w) => w.innerWidth).orDefault(0) ); return windowWidth >= breakpoints.tablet; diff --git a/packages/widget/src/components/molecules/connect-button/index.tsx b/packages/widget/src/components/molecules/connect-button/index.tsx index 435c7b4b..1ad0af2c 100644 --- a/packages/widget/src/components/molecules/connect-button/index.tsx +++ b/packages/widget/src/components/molecules/connect-button/index.tsx @@ -32,7 +32,7 @@ export const ConnectButton = (props: ComponentProps) => { {t( isLedgerLiveAccountPlaceholder ? "init.ledger_add_account" - : "init.connect_wallet", + : "init.connect_wallet" )} ); diff --git a/packages/widget/src/components/molecules/global-modals/index.tsx b/packages/widget/src/components/molecules/global-modals/index.tsx index ae5ece0a..b737ecc7 100644 --- a/packages/widget/src/components/molecules/global-modals/index.tsx +++ b/packages/widget/src/components/molecules/global-modals/index.tsx @@ -9,7 +9,7 @@ import { TosModal } from "../tos-modal"; export const GlobalModals = () => { const geoBlock = useGeoBlock(); const regionCodeName = useRegionCodeName( - geoBlock ? geoBlock.regionCode : undefined, + geoBlock ? geoBlock.regionCode : undefined ); const [hideGeoBlock, setHideGeoBlock] = useState(false); diff --git a/packages/widget/src/components/molecules/header/index.tsx b/packages/widget/src/components/molecules/header/index.tsx index 08c14742..1ad9a1ce 100644 --- a/packages/widget/src/components/molecules/header/index.tsx +++ b/packages/widget/src/components/molecules/header/index.tsx @@ -63,7 +63,7 @@ export const Header = () => { {Maybe.fromFalsy( - !wagmiConfig.isLoading && wagmiConfig.data && variant !== "zerion", + !wagmiConfig.isLoading && wagmiConfig.data && variant !== "zerion" ) .map(() => ( @@ -74,7 +74,7 @@ export const Header = () => { aria-hidden={!mounted} > {Maybe.fromFalsy( - (isConnected || isConnecting) && chain && account, + (isConnected || isConnecting) && chain && account ) .map(() => ( { const showDisconnect = useMemo( () => Maybe.fromNullable(connector).map(shouldShowDisconnect).orDefault(false), - [connector], + [connector] ); const wagmiConfig = useWagmiConfig(); diff --git a/packages/widget/src/components/molecules/help-modal/index.tsx b/packages/widget/src/components/molecules/help-modal/index.tsx index 3b491ec0..678db7c1 100644 --- a/packages/widget/src/components/molecules/help-modal/index.tsx +++ b/packages/widget/src/components/molecules/help-modal/index.tsx @@ -36,7 +36,7 @@ export const HelpModal = ({ modal, customTrigger }: HelpModalProps) => { const { t, i18n } = useTranslation(); const getContent = ( - modal: ModalType, + modal: ModalType ): { title: string; description: string | ReactNode; @@ -167,7 +167,7 @@ export const HelpModal = ({ modal, customTrigger }: HelpModalProps) => { title: t("help_modals.get_in_touch.button"), onClick: () => MaybeWindow.ifJust((w) => - w.open("https://twitter.com/yield_xyz", "_blank"), + w.open("https://twitter.com/yield_xyz", "_blank") ), }, description: "", diff --git a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx index d49e079b..8ac70612 100644 --- a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx +++ b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx @@ -40,7 +40,7 @@ export const SelectOpportunityListItem = ({ const { t } = useTranslation(); const campaignRate = getRewardRateBreakdown( - getYieldRewardRateDetails(item), + getYieldRewardRateDetails(item) ).find((rewardRate) => rewardRate.key === "campaign"); const totalRateFormatted = getRewardRateFormatted({ @@ -107,13 +107,13 @@ export const SelectOpportunityListItem = ({ .map((tvl) => tvl.reduce( (acc, curr) => acc.plus(curr.value), - BigNumber(0), - ), + BigNumber(0) + ) ) .map( (tvl) => - `TVL: ${formatNumber(fromWei(tvl, item.token.decimals), 0)} ${item.token.symbol}`, - ), + `TVL: ${formatNumber(fromWei(tvl, item.token.decimals), 0)} ${item.token.symbol}` + ) ) .orDefault(item.token.symbol)} @@ -130,8 +130,8 @@ export const SelectOpportunityListItem = ({ {Maybe.fromNullable(metadata.commission) .map((commission) => APToPercentage( - commission.reduce((acc, curr) => acc + curr.value, 0), - ), + commission.reduce((acc, curr) => acc + curr.value, 0) + ) ) .map((commission) => ( !v.isNaN()) .map( - (v) => `${formatNumber(v, 0)} ${stakedBalanceToken.symbol}`, + (v) => `${formatNumber(v, 0)} ${stakedBalanceToken.symbol}` ) .orDefault("-"), } @@ -189,6 +189,6 @@ export const useMetaInfo = ({ subnetName, marketCap, tokenSymbol, - ], + ] ); }; diff --git a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx index 5a961dc0..0ae23bad 100644 --- a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx +++ b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx @@ -191,7 +191,7 @@ export const SelectValidatorList = ({ {t( status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive", + : "details.validators_inactive" )} @@ -241,7 +241,7 @@ const ValidatorMeta = memo((props: Parameters[0]) => { {Object.entries(metaInfo) .filter( (val): val is [keyof typeof metaInfo, NonNullable<(typeof val)[1]>] => - !!val[1], + !!val[1] ) .map(([key, val]) => { return ( diff --git a/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx b/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx index dc06eade..8c337c0a 100644 --- a/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx +++ b/packages/widget/src/components/molecules/zerion-chain-modal/index.tsx @@ -12,7 +12,7 @@ export const ZerionChainModal = () => { const chainIds = useMemo( () => connectorChains.map((c) => c.id), - [connectorChains], + [connectorChains] ); const switchChain = connector?.switchChain; @@ -30,7 +30,7 @@ export const ZerionChainModal = () => { chainIds, selectedChainId: chain.id, onSwitchChain, - }), + }) ) .map((elem) => ( diff --git a/packages/widget/src/domain/index.ts b/packages/widget/src/domain/index.ts index fd536543..e8052f30 100644 --- a/packages/widget/src/domain/index.ts +++ b/packages/widget/src/domain/index.ts @@ -48,9 +48,9 @@ export const getMaxAmount = ({ return BigNumber.max( BigNumber.min( integrationMaxLimit.orDefault(BigNumber(Number.POSITIVE_INFINITY)), - availableAmount.minus(gasEstimateTotal), + availableAmount.minus(gasEstimateTotal) ), - new BigNumber(0), + new BigNumber(0) ); }; @@ -68,7 +68,7 @@ export const getValidStakeSessionTx = (stakeDto: ActionDto) => { ...stakeDto, transactions: stakeDto.transactions.filter( ( - tx, + tx ): tx is Override< TransactionDto, { @@ -77,7 +77,7 @@ export const getValidStakeSessionTx = (stakeDto: ActionDto) => { Exclude >; } - > => tx.status !== "SKIPPED", + > => tx.status !== "SKIPPED" ), }; diff --git a/packages/widget/src/domain/types/action.ts b/packages/widget/src/domain/types/action.ts index 9dcde6f1..bf790748 100644 --- a/packages/widget/src/domain/types/action.ts +++ b/packages/widget/src/domain/types/action.ts @@ -125,7 +125,7 @@ export const getActionInputToken = ({ }; export const getActionValidatorAddresses = ( - actionDto: ActionDto, + actionDto: ActionDto ): string[] | null => actionDto.rawArguments?.validatorAddresses ?? (actionDto.rawArguments?.validatorAddress @@ -136,7 +136,7 @@ export const getActionCurrentStepIndex = (actionDto: ActionDto) => { const idx = actionDto.transactions.findIndex( (transaction) => transaction.status !== TransactionStatus.CONFIRMED && - transaction.status !== TransactionStatus.SKIPPED, + transaction.status !== TransactionStatus.SKIPPED ); if (idx >= 0) { @@ -199,7 +199,7 @@ export const getTransactionGasEstimate = ({ export const getActionGasFeeToken = ( yieldDto: Yield, - gasFeeToken?: TokenDto, + gasFeeToken?: TokenDto ): TokenDto => gasFeeToken ?? getYieldGasFeeToken(yieldDto); export type ActionDtoWithGasEstimate = { diff --git a/packages/widget/src/domain/types/chains/index.ts b/packages/widget/src/domain/types/chains/index.ts index 4e13681d..164ca685 100644 --- a/packages/widget/src/domain/types/chains/index.ts +++ b/packages/widget/src/domain/types/chains/index.ts @@ -35,7 +35,7 @@ export const isTronChain = (chain: string): chain is SupportedMiscChains => { }; export const isBittensorChain = ( - chain: string, + chain: string ): chain is SupportedSubstrateChains => { return chain === SubstrateNetworks.Bittensor; }; diff --git a/packages/widget/src/domain/types/connectors.ts b/packages/widget/src/domain/types/connectors.ts index 41c597bb..a404db8a 100644 --- a/packages/widget/src/domain/types/connectors.ts +++ b/packages/widget/src/domain/types/connectors.ts @@ -10,7 +10,7 @@ export type ConnectorWithFilteredChains = { }; export const isConnectorWithFilteredChains = ( - connector: Connector, + connector: Connector ): connector is Connector & ConnectorWithFilteredChains => { return !!(connector as unknown as ConnectorWithFilteredChains) .$filteredChains; diff --git a/packages/widget/src/domain/types/external-providers.ts b/packages/widget/src/domain/types/external-providers.ts index a3194230..6b9629a0 100644 --- a/packages/widget/src/domain/types/external-providers.ts +++ b/packages/widget/src/domain/types/external-providers.ts @@ -9,13 +9,13 @@ export class ExternalProvider { sendTransaction(tx: SKTx, txMeta: SKTxMeta) { return EitherAsync.liftEither( Maybe.fromNullable( - this.variantProvider.current.provider.sendTransaction, - ).toEither(new Error("Invalid provider type")), + this.variantProvider.current.provider.sendTransaction + ).toEither(new Error("Invalid provider type")) ) .chain((_sendTransaction) => EitherAsync(() => _sendTransaction(tx, txMeta)).mapLeft( - () => new Error("Failed to send transaction, unknown error"), - ), + () => new Error("Failed to send transaction, unknown error") + ) ) .chain((res) => { if (typeof res === "string") { @@ -32,7 +32,7 @@ export class ExternalProvider { switchChain({ chainId }: { chainId: number }) { return EitherAsync(() => - this.variantProvider.current.provider.switchChain(chainId), + this.variantProvider.current.provider.switchChain(chainId) ).mapLeft((e) => { console.error(e); return new Error("Failed to switch chain"); @@ -41,7 +41,7 @@ export class ExternalProvider { signMessage(messageHash: string) { return EitherAsync(() => - this.variantProvider.current.provider.signMessage(messageHash), + this.variantProvider.current.provider.signMessage(messageHash) ).mapLeft((e) => { console.error(e); return new Error("Failed to sign message"); diff --git a/packages/widget/src/domain/types/pending-action.ts b/packages/widget/src/domain/types/pending-action.ts index 80a92cd6..65fc18a8 100644 --- a/packages/widget/src/domain/types/pending-action.ts +++ b/packages/widget/src/domain/types/pending-action.ts @@ -23,19 +23,19 @@ export type PendingActionAmountConfig = { }; export const isPendingActionAmountRequired = ( - pendingAction: AnyPendingActionDto, + pendingAction: AnyPendingActionDto ) => !!getPendingActionAmountConfig(pendingAction)?.required; export const isPendingActionValidatorAddressRequired = ( - pendingAction: AnyPendingActionDto, + pendingAction: AnyPendingActionDto ) => !!getPendingActionArgument(pendingAction, "validatorAddress")?.required; export const isPendingActionValidatorAddressesRequired = ( - pendingAction: AnyPendingActionDto, + pendingAction: AnyPendingActionDto ) => !!getPendingActionArgument(pendingAction, "validatorAddresses")?.required; export const getPendingActionAmountConfig = ( - pendingAction: AnyPendingActionDto, + pendingAction: AnyPendingActionDto ): PendingActionAmountConfig | null => { const amountArg = getPendingActionArgument(pendingAction, "amount"); @@ -56,14 +56,14 @@ export const getPendingActionAmountConfig = ( const getPendingActionArgument = ( pendingAction: AnyPendingActionDto, - name: PendingActionArgName, + name: PendingActionArgName ) => { const v2Field = ( pendingAction as YieldPendingActionDto ).arguments?.fields?.find( ( - field: NonNullable["fields"][number], - ) => field.name === name, + field: NonNullable["fields"][number] + ) => field.name === name ); if (v2Field) { diff --git a/packages/widget/src/domain/types/positions.ts b/packages/widget/src/domain/types/positions.ts index a855f35d..ecc5786f 100644 --- a/packages/widget/src/domain/types/positions.ts +++ b/packages/widget/src/domain/types/positions.ts @@ -49,7 +49,7 @@ export type PositionsData = Map< >; export const getPositionBalanceDataKey = ( - balance: YieldBalanceDto, + balance: YieldBalanceDto ): BalanceDataKey => { if (Array.isArray(balance.validators) && balance.validators.length > 1) { return "validators"; @@ -64,10 +64,10 @@ export const getPositionBalanceDataKey = ( export const getPositionTotalAmount = ( balances: YieldBalanceDto[], - baseToken: TokenDto, + baseToken: TokenDto ) => { const baseTokenBalance = balances.find((b) => - equalTokens(b.token, baseToken), + equalTokens(b.token, baseToken) ); const baseTokenPriceInUsd = (() => { @@ -95,7 +95,7 @@ export const getPositionTotalAmount = ( if (baseTokenPriceInUsd && !baseTokenPriceInUsd.isZero()) { return { amount: acc.amount.plus( - balanceAmountUsd.dividedBy(baseTokenPriceInUsd), + balanceAmountUsd.dividedBy(baseTokenPriceInUsd) ), amountUsd: acc.amountUsd.plus(balanceAmountUsd), }; @@ -106,6 +106,6 @@ export const getPositionTotalAmount = ( amountUsd: acc.amountUsd.plus(balanceAmountUsd), }; }, - { amount: new BigNumber(0), amountUsd: new BigNumber(0) }, + { 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 f43e8c02..cbdb4801 100644 --- a/packages/widget/src/domain/types/price.ts +++ b/packages/widget/src/domain/types/price.ts @@ -45,7 +45,7 @@ export const getTokenPriceInUSD = ({ prices .getByToken(baseToken) .chainNullable((v) => v.price) - .orDefault(0), + .orDefault(0) ); const pricePerShareBN = BigNumber(pricePerShare); @@ -56,7 +56,7 @@ export const getTokenPriceInUSD = ({ prices .getByToken(token) .chainNullable((v) => v.price) - .orDefault(0), + .orDefault(0) ); return amountBN.times(tokenPrice); diff --git a/packages/widget/src/domain/types/reward-rate.ts b/packages/widget/src/domain/types/reward-rate.ts index c4c53dc3..4d4b3fe4 100644 --- a/packages/widget/src/domain/types/reward-rate.ts +++ b/packages/widget/src/domain/types/reward-rate.ts @@ -24,7 +24,7 @@ const breakdownOrder: RewardRateBreakdownKey[] = [ ]; export const getRewardTypeFromRateType = ( - rateType: string | null | undefined, + rateType: string | null | undefined ): RewardTypes => { const normalized = rateType?.toLowerCase(); @@ -36,7 +36,7 @@ export const getRewardTypeFromRateType = ( }; const getBreakdownKey = ( - yieldSource: YieldRewardDto["yieldSource"], + yieldSource: YieldRewardDto["yieldSource"] ): RewardRateBreakdownKey => yieldSource === "campaign_incentive" ? "campaign" @@ -45,14 +45,14 @@ const getBreakdownKey = ( : "native"; export const getYieldRewardRateDetails = ( - yieldDto: Yield | null | undefined, + yieldDto: Yield | null | undefined ): YieldRewardRateDto | undefined => yieldDto?.rewardRate; export const getRewardRateBreakdown = ( rewardRate: YieldRewardRateDto | null | undefined, opts?: { showUpToCampaign?: boolean; - }, + } ): RewardRateBreakdownItem[] => { if (!rewardRate?.components?.length) { return []; diff --git a/packages/widget/src/domain/types/rewards.ts b/packages/widget/src/domain/types/rewards.ts index c643f54c..84fbc6d2 100644 --- a/packages/widget/src/domain/types/rewards.ts +++ b/packages/widget/src/domain/types/rewards.ts @@ -59,14 +59,14 @@ const enabledRewardsSummaryYieldIds = { const enabledRewardsSummaryYieldIdsSet = new Set( Object.values(enabledRewardsSummaryYieldIds).flatMap((v) => - v.map((v) => v.id), - ), + v.map((v) => v.id) + ) ); export type EnabledRewardsSummaryYieldId = (typeof enabledRewardsSummaryYieldIds)[keyof typeof enabledRewardsSummaryYieldIds][number]["id"]; export const isValidYieldIdForRewardsSummary = ( - yieldId: string, + yieldId: string ): yieldId is EnabledRewardsSummaryYieldId => enabledRewardsSummaryYieldIdsSet.has(yieldId as EnabledRewardsSummaryYieldId); diff --git a/packages/widget/src/domain/types/stake.ts b/packages/widget/src/domain/types/stake.ts index 46501b32..b68b90c6 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -55,8 +55,8 @@ export const getInitialToken = (args: { (tokenSymbolCompare && tokenNetworkCompare) || tokenStringCompare ); }, - [...args.tokenBalances, ...args.defaultTokens], - ), + [...args.tokenBalances, ...args.defaultTokens] + ) ) /** * TB based on preferred token @@ -64,19 +64,19 @@ export const getInitialToken = (args: { .altLazy(() => Maybe.fromNullable(args.network) .chain((n) => - Maybe.fromNullable(args.preferredTokenYieldsPerNetwork?.[n]), + Maybe.fromNullable(args.preferredTokenYieldsPerNetwork?.[n]) ) .altLazy(() => Maybe.fromNullable(args.preferredTokenYieldsPerNetwork).chainNullable( - (v) => Object.values(v)[0], - ), + (v) => Object.values(v)[0] + ) ) .chain((preferredTokens) => List.find( (val) => !!preferredTokens[tokenString(val.token)], - [...args.tokenBalances, ...args.defaultTokens], - ), - ), + [...args.tokenBalances, ...args.defaultTokens] + ) + ) ) /** * TB based on first token with available yields and amount > 0 @@ -100,8 +100,8 @@ export const canBeInitialYield = (args: { .chain((queryParams) => Maybe.fromFalsy( !!queryParams.yieldId && - queryParams.yieldId.toLowerCase() === args.yieldDto.id.toLowerCase(), - ), + queryParams.yieldId.toLowerCase() === args.yieldDto.id.toLowerCase() + ) ) .altLazy(() => Maybe.fromFalsy( @@ -109,8 +109,8 @@ export const canBeInitialYield = (args: { tokenBalanceAmount: args.tokenBalanceAmount, yieldDto: args.yieldDto, positionsData: args.positionsData, - }), - ), + }) + ) ) .isJust(); @@ -124,7 +124,7 @@ const balanceValidForYield = ({ positionsData: PositionsData; }) => tokenBalanceAmount.isGreaterThanOrEqualTo( - getMinStakeAmount(yieldDto, positionsData), + getMinStakeAmount(yieldDto, positionsData) ); export const getInitSelectedValidators = (args: { @@ -138,15 +138,15 @@ export const getInitSelectedValidators = (args: { (val) => val.name?.toLowerCase() === initV.toLowerCase() || val.address === initV, - args.validators, - ), + args.validators + ) ) .altLazy(() => List.head(args.validators)) .map((v) => new Map([[v.address, v]])) .orDefault(new Map()); export const isForceMaxAmount = ( - args: { minimum?: number | null; maximum?: number | null } | null | undefined, + args: { minimum?: number | null; maximum?: number | null } | null | undefined ) => args?.minimum === -1 && args?.maximum === -1; const yieldsWithEnterMinBasedOnPosition = new Map>([ @@ -159,25 +159,25 @@ export const isNetworkWithEnterMinBasedOnPosition = (network: Networks) => const isYieldWithEnterMinBasedOnPosition = (yieldDto: Yield) => Maybe.fromNullable( yieldsWithEnterMinBasedOnPosition.get( - getYieldGasFeeToken(yieldDto).network as Networks, - ), + getYieldGasFeeToken(yieldDto).network as Networks + ) ) .filter((set) => set.has(yieldDto.id)) .isJust(); export const getMinStakeAmount = ( yieldDto: Yield, - positionsData: PositionsData, + positionsData: PositionsData ) => { const integrationMin = new BigNumber( - getYieldActionArg(yieldDto, "enter", "amount")?.minimum ?? 0, + getYieldActionArg(yieldDto, "enter", "amount")?.minimum ?? 0 ); if (isYieldWithEnterMinBasedOnPosition(yieldDto)) { const hasStaked = Maybe.fromNullable(positionsData.get(yieldDto.id)) .map((val) => [...val.balanceData.values()]) .map((val) => - val.some((v) => v.balances.some((b) => b.type === "active")), + val.some((v) => v.balances.some((b) => b.type === "active")) ) .orDefault(false); @@ -193,10 +193,10 @@ export const getMinStakeAmount = ( export const getMinUnstakeAmount = ( yieldDto: Yield, - pricePerShare: string | null, + pricePerShare: string | null ) => { const integrationMin = new BigNumber( - getYieldActionArg(yieldDto, "exit", "amount")?.minimum ?? 0, + getYieldActionArg(yieldDto, "exit", "amount")?.minimum ?? 0 ); const pricePerShareBN = new BigNumber(pricePerShare ?? 0); diff --git a/packages/widget/src/domain/types/transaction.ts b/packages/widget/src/domain/types/transaction.ts index 13d268e6..0cfabcf8 100644 --- a/packages/widget/src/domain/types/transaction.ts +++ b/packages/widget/src/domain/types/transaction.ts @@ -127,7 +127,7 @@ export const unsignedTonTransactionCodec = oneOf([ address: string, amount: string, payload: string, - }), + }) ), ]); diff --git a/packages/widget/src/domain/types/validators.ts b/packages/widget/src/domain/types/validators.ts index 9f396371..213448d2 100644 --- a/packages/widget/src/domain/types/validators.ts +++ b/packages/widget/src/domain/types/validators.ts @@ -5,7 +5,7 @@ export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; export type ValidatorDto = LegacyValidatorDto; export const toValidatorDto = ( - validatorDto: YieldValidatorDto | ValidatorDto, + validatorDto: YieldValidatorDto | ValidatorDto ): ValidatorDto => { const legacyValidator = validatorDto as ValidatorDto; const rewardRate = diff --git a/packages/widget/src/domain/types/wallets/generic-wallet.ts b/packages/widget/src/domain/types/wallets/generic-wallet.ts index 18094865..94590353 100644 --- a/packages/widget/src/domain/types/wallets/generic-wallet.ts +++ b/packages/widget/src/domain/types/wallets/generic-wallet.ts @@ -62,7 +62,7 @@ export type SKWallet = { getTransactionReceipt?(txHash: string): Promise<{ transactionHash?: string }>; sendTransaction( tx: SKTx, - txMeta: SKTxMeta, + txMeta: SKTxMeta ): Promise< | string | { type: "success"; txHash: string } diff --git a/packages/widget/src/domain/types/yields.ts b/packages/widget/src/domain/types/yields.ts index 56dd1bab..eb1f5db2 100644 --- a/packages/widget/src/domain/types/yields.ts +++ b/packages/widget/src/domain/types/yields.ts @@ -66,7 +66,7 @@ export const filterValidators = ({ yieldId?: Yield["id"]; }) => { const valConfig = Maybe.fromNullable( - validatorsConfig.get(network as SupportedSKChains), + validatorsConfig.get(network as SupportedSKChains) ) .altLazy(() => Maybe.fromNullable(validatorsConfig.get("*"))) .extractNullable(); @@ -122,14 +122,14 @@ const secondsToDays = (seconds: number | undefined) => { const isSameToken = ( left: Pick, - right: Pick, + right: Pick ) => left.network === right.network && left.symbol === right.symbol && (left.address?.toLowerCase() ?? "") === (right.address?.toLowerCase() ?? ""); const mapMechanicsType = ( - type: Yield["mechanics"]["type"], + type: Yield["mechanics"]["type"] ): Exclude< ExtendedYieldType, "liquid-staking" | "native_staking" | "pooled_staking" @@ -149,7 +149,7 @@ const mapMechanicsType = ( }; const getBaseYieldType = ( - yieldDto: Yield, + yieldDto: Yield ): LegacyYieldType | "liquid-staking" => { if ( yieldDto.mechanics.type === "staking" && @@ -166,7 +166,7 @@ const getBaseYieldType = ( const getFallbackActionArg = ( yieldDto: Yield, type: YieldActionType, - name: YieldArgumentName, + name: YieldArgumentName ) => { const legacyArgs = yieldDto.__fallback__.args?.[type]?.args as | Record @@ -187,10 +187,10 @@ const getFallbackActionArg = ( export const getYieldActionArg = ( yieldDto: Yield, type: YieldActionType, - name: YieldArgumentName, + name: YieldArgumentName ): YieldArgumentConfig | null => { const field = yieldDto.mechanics.arguments?.[type]?.fields?.find( - (item) => item.name === name, + (item) => item.name === name ); const legacyField = getFallbackActionArg(yieldDto, type, name); @@ -214,7 +214,7 @@ export const getYieldActionArg = ( export const isYieldActionArgRequired = ( yieldDto: Yield, type: YieldActionType, - name: YieldArgumentName, + name: YieldArgumentName ) => !!getYieldActionArg(yieldDto, type, name)?.required; export const getYieldRewardRate = (yieldDto: Yield) => @@ -249,7 +249,7 @@ const uniqTokens = (tokens: (YieldTokenDto | null | undefined)[]) => { export const getYieldRewardTokens = (yieldDto: Yield) => { const derived = uniqTokens( - yieldDto.rewardRate?.components?.map((component) => component.token) ?? [], + yieldDto.rewardRate?.components?.map((component) => component.token) ?? [] ); if (derived.length) { @@ -345,7 +345,7 @@ export const getExtendedYieldType = (yieldDto: Yield) => export const getYieldTypeLabels = ( yieldDto: Yield, - t: TFunction, + t: TFunction ): YieldTypeLabelsMap[keyof YieldTypeLabelsMap] => { const map = { staking: { @@ -425,12 +425,12 @@ const isNativeStaking = (yieldDto: Yield) => Maybe.fromFalsy(isEthereumStaking(yieldDto)) .chain(() => Maybe.fromFalsy( - isYieldActionArgRequired(yieldDto, "enter", "amount"), + isYieldActionArgRequired(yieldDto, "enter", "amount") ).chain(() => Maybe.fromNullable( - getYieldActionArg(yieldDto, "enter", "amount")?.minimum, - ), - ), + getYieldActionArg(yieldDto, "enter", "amount")?.minimum + ) + ) ) .map(BigNumber) .filter((v) => v.isEqualTo(32)) @@ -462,7 +462,7 @@ const zeroRewardRateYieldIdWhitelist = new Set([ ]); export const isNonZeroRewardRateYield = ( - yieldDto: Pick, + yieldDto: Pick ) => (yieldDto.rewardRate?.total ?? 0) > 0 || zeroRewardRateYieldIdWhitelist.has(yieldDto.id); diff --git a/packages/widget/src/hooks/api/use-activity-actions.ts b/packages/widget/src/hooks/api/use-activity-actions.ts index 2c0e4f5a..0ee67967 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -30,7 +30,7 @@ export const useActivityActions = () => { fetchClient: yieldApiFetchClient, limit: PAGE_SIZE, offset: pageParam, - }), + }) ) .mapLeft(() => new Error("Could not get action list")) .map((actionList) => ({ @@ -39,7 +39,7 @@ export const useActivityActions = () => { (action) => action.status !== ActionStatus.CREATED && (!network || - action.transactions.some((tx) => tx.network === network)), + action.transactions.some((tx) => tx.network === network)) ), })) .chain(async (actionList) => @@ -55,8 +55,8 @@ export const useActivityActions = () => { actionData: action as ActionDto, yieldData, })) - .chainLeft(() => EitherAsync(() => Promise.resolve(null))), - ), + .chainLeft(() => EitherAsync(() => Promise.resolve(null))) + ) ) .map((res) => res.filter((x) => x !== null)) .map((res) => @@ -65,10 +65,10 @@ export const useActivityActions = () => { !!getActionInputToken({ actionDto: x.actionData, yieldDto: x.yieldData, - }), - ), + }) + ) ) - .map((data) => ({ ...actionList, data })), + .map((data) => ({ ...actionList, data })) ) ).unsafeCoerce(); }, @@ -81,7 +81,7 @@ export const useActivityActions = () => { const allItems = useMemo( () => query.data?.pages.flatMap((page) => page.data), - [query.data], + [query.data] ); return { diff --git a/packages/widget/src/hooks/api/use-default-tokens.ts b/packages/widget/src/hooks/api/use-default-tokens.ts index 6c97fb34..4377fd7c 100644 --- a/packages/widget/src/hooks/api/use-default-tokens.ts +++ b/packages/widget/src/hooks/api/use-default-tokens.ts @@ -1,12 +1,15 @@ -import { getTokenGetTokensQueryKey, tokenGetTokens } from "@stakekit/api-hooks"; import type { QueryClient } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; +import { tokenGetTokens } from "../../common/private-api"; import type { TokenBalanceScanResponseDto } from "../../domain/types/token-balance"; import type { TokenGetTokensParams } from "../../domain/types/tokens"; import { useSettings } from "../../providers/settings"; import { useSKWallet } from "../../providers/sk-wallet"; +const getTokenGetTokensQueryKey = (params?: TokenGetTokensParams) => + ["/v1/tokens", ...(params ? [params] : [])] as const; + export const useDefaultTokens = () => { const { network } = useSKWallet(); const { tokensForEnabledYieldsOnly } = useSettings(); @@ -25,7 +28,7 @@ export const useDefaultTokens = () => { }; export const getDefaultTokens = ( - params: Parameters[0] & { queryClient: QueryClient }, + params: Parameters[0] & { queryClient: QueryClient } ) => EitherAsync(() => params.queryClient.fetchQuery({ @@ -33,7 +36,7 @@ export const getDefaultTokens = ( network: params.network ?? undefined, }), queryFn: async () => (await queryFn(params)).unsafeCoerce(), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("could not get multi yields"); @@ -47,7 +50,7 @@ const queryFn = ({ tokenGetTokens({ network, enabledYieldsOnly: enabledYieldsOnly || undefined, - }), + }) ).map((val) => - val.map((v) => ({ ...v, amount: "0" })), + val.map((v) => ({ ...v, amount: "0" })) ); diff --git a/packages/widget/src/hooks/api/use-multi-yields.ts b/packages/widget/src/hooks/api/use-multi-yields.ts index 9d693fbc..ae586763 100644 --- a/packages/widget/src/hooks/api/use-multi-yields.ts +++ b/packages/widget/src/hooks/api/use-multi-yields.ts @@ -48,7 +48,7 @@ const multiYieldsStore = createStore({ on: { "yield-opportunity": ( context, - event: { data: { key: string; yieldDto: Yield } }, + event: { data: { key: string; yieldDto: Yield } } ) => { const newMap = new Map(context.data); const prev = newMap.get(event.data.key) ?? new Map(); @@ -107,7 +107,7 @@ export const useMultiYields = ( opts?: { select?: (val: Yield[]) => T; enabled?: boolean; - }, + } ) => { const { network, isConnected, isLedgerLive } = useSKWallet(); const yieldApiFetchClient = useYieldApiFetchClient(); @@ -131,20 +131,20 @@ export const useMultiYields = ( ...argsRef.current, yieldIds, validatorsConfig, - }).pipe(toArray()), + }).pipe(toArray()) ), select: opts?.select, }); }; export const getFirstEligibleYield = ( - params: Parameters[0], + params: Parameters[0] ) => EitherAsync(() => params.queryClient.fetchQuery({ queryKey: getFirstEligibleYieldQueryKey(params.yieldIds), queryFn: () => firstValueFrom(firstEligibleYield$(params)), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("could not get first eligible yield"); @@ -167,9 +167,9 @@ const multipleYields$ = (args: { yieldId: v, queryClient: args.queryClient, yieldApiFetchClient: args.yieldApiFetchClient, - }), - ), - ), + }) + ) + ) ).pipe( map((v) => (v.isRight() ? v.extract() : null)), filter( @@ -182,8 +182,8 @@ const multipleYields$ = (args: { network: args.network, isLedgerLive: args.isLedgerLive, }).length > 0 - ), - ), + ) + ) ); const firstEligibleYield$ = (args: { @@ -211,12 +211,12 @@ const firstEligibleYield$ = (args: { const preferredYieldId = Maybe.fromNullable( args.preferredTokenYieldsPerNetwork?.[ y.token.network as SupportedSKChains - ]?.[tokenString(y.token)], + ]?.[tokenString(y.token)] ) .altLazy(() => Maybe.fromNullable(args.preferredTokenYieldsPerNetwork).chainNullable( - (v) => Object.values(v)[0][tokenString(y.token)], - ), + (v) => Object.values(v)[0][tokenString(y.token)] + ) ) .extractNullable(); @@ -232,7 +232,7 @@ const firstEligibleYield$ = (args: { }); }), take(1), - defaultIfEmpty(null), + defaultIfEmpty(null) ); return new Observable((subscriber) => { @@ -272,7 +272,7 @@ const defaultFiltered = createSelector( if (!isConnected) return defaultFilter; return network === o.token.network && defaultFilter; - }), + }) ); const getFirstEligibleYieldQueryKey = (yieldIds: string[]) => [ @@ -288,5 +288,5 @@ export const getCachedFirstEligibleYield = ({ yieldIds: string[]; }) => Maybe.fromNullable( - queryClient.getQueryData(getFirstEligibleYieldQueryKey(yieldIds)), + queryClient.getQueryData(getFirstEligibleYieldQueryKey(yieldIds)) ); diff --git a/packages/widget/src/hooks/api/use-prices.ts b/packages/widget/src/hooks/api/use-prices.ts index 6ce4ccd9..12615397 100644 --- a/packages/widget/src/hooks/api/use-prices.ts +++ b/packages/widget/src/hooks/api/use-prices.ts @@ -1,6 +1,8 @@ -import { useTokenGetTokenPrices } from "@stakekit/api-hooks"; +import { useQuery } from "@tanstack/react-query"; import { useCallback } from "react"; import { createSelector } from "reselect"; +import { tokenGetTokenPrices } from "../../common/private-api"; +import type { StakeKitErrorDto } from "../../domain/types/errors"; import type { PriceRequestDto, PriceResponseDto, @@ -18,19 +20,22 @@ const defaultParam: PriceRequestDto = { const pricesSelector = createSelector( (val: PriceResponseDto) => val, - (val) => priceResponseDtoToPrices(val), + (val) => priceResponseDtoToPrices(val) ); type PriceRequestInput = Omit & { tokenList: (PriceRequestDto["tokenList"][number] | YieldTokenDto)[]; }; +const getTokenGetTokenPricesQueryKey = (priceRequestDto: PriceRequestDto) => + ["/v1/tokens/prices", priceRequestDto] as const; + export const usePrices = ( priceRequestDto: PriceRequestInput | null | undefined, opts?: { enabled?: boolean; select?: (val: Prices) => T; - }, + } ) => { const requestDto = priceRequestDto ? ({ @@ -43,21 +48,21 @@ export const usePrices = ( } satisfies PriceRequestDto) : defaultParam; - return useTokenGetTokenPrices(requestDto, { - query: { - enabled: !!priceRequestDto && opts?.enabled, - select: useCallback( - (res: PriceResponseDto): T => { - const mapped = pricesSelector(res); - - if (opts?.select) { - return opts.select(mapped); - } - - return mapped as T; - }, - [opts?.select], - ), - }, + return useQuery({ + queryKey: getTokenGetTokenPricesQueryKey(requestDto), + queryFn: () => tokenGetTokenPrices(requestDto), + enabled: !!priceRequestDto && opts?.enabled, + select: useCallback( + (res: PriceResponseDto): T => { + const mapped = pricesSelector(res); + + if (opts?.select) { + return opts.select(mapped); + } + + return mapped as T; + }, + [opts?.select] + ), }); }; diff --git a/packages/widget/src/hooks/api/use-token-balances-scan.ts b/packages/widget/src/hooks/api/use-token-balances-scan.ts index 9b0b05f2..3df8ac21 100644 --- a/packages/widget/src/hooks/api/use-token-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-token-balances-scan.ts @@ -38,9 +38,9 @@ export const useTokenBalancesScan = () => { addresses: { address: "", additionalAddresses: undefined }, network: "ethereum", }, - }, + } ), - [additionalAddresses, address, isLedgerLiveAccountPlaceholder, network], + [additionalAddresses, address, isLedgerLiveAccountPlaceholder, network] ); return useQuery({ @@ -57,13 +57,13 @@ export const useTokenBalancesScan = () => { }; export const getTokenBalancesScan = ( - params: Parameters[0] & { queryClient: QueryClient }, + params: Parameters[0] & { queryClient: QueryClient } ) => EitherAsync(() => params.queryClient.fetchQuery({ queryKey: getTokenTokenBalancesScanQueryKey(params.tokenBalanceScanDto), queryFn: async () => (await queryFn(params)).unsafeCoerce(), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("could not get multi yields"); @@ -78,7 +78,7 @@ const queryFn = ({ (e) => { console.log(e); return new Error("could not get token balances"); - }, + } ); export const useInvalidateTokenBalances = () => { @@ -91,12 +91,12 @@ export const useInvalidateTokenBalances = () => { getTokenTokenBalancesScanQueryKey({} as TokenBalanceScanDto)[0], ], }), - [queryClient], + [queryClient] ); }; const getTokenTokenBalancesScanQueryKey = ( - tokenBalanceScanDto: TokenBalanceScanDto, + tokenBalanceScanDto: TokenBalanceScanDto ) => { return ["/v1/tokens/balances/scan", tokenBalanceScanDto] as const; }; diff --git a/packages/widget/src/hooks/api/use-tokens-prices.ts b/packages/widget/src/hooks/api/use-tokens-prices.ts index 23b143eb..4e007972 100644 --- a/packages/widget/src/hooks/api/use-tokens-prices.ts +++ b/packages/widget/src/hooks/api/use-tokens-prices.ts @@ -28,7 +28,7 @@ export const useTokensPrices = ({ tokenList: [val.token, val.baseToken, val.gasFeeToken], })) .extractNullable(), - [baseToken, gasFeeToken, token], + [baseToken, gasFeeToken, token] ); return usePrices(priceRequestDto); 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 a1f79c41..91798551 100644 --- a/packages/widget/src/hooks/api/use-yield-balances-scan.ts +++ b/packages/widget/src/hooks/api/use-yield-balances-scan.ts @@ -22,7 +22,7 @@ export const useYieldBalancesScan = (opts?: { const lastActionTimestamp = useMemo( () => actionHistoryData.map((v) => v.timestamp).extractNullable(), - [actionHistoryData], + [actionHistoryData] ); const param = useMemo( @@ -48,9 +48,9 @@ export const useYieldBalancesScan = (opts?: { dto: { queries: [{ address: "", network: "ethereum" }], }, - }, + } ), - [address, network], + [address, network] ); const res = yieldApi.useQuery( @@ -71,7 +71,7 @@ export const useYieldBalancesScan = (opts?: { return items as T; }, - }, + } ); /** @@ -97,7 +97,7 @@ export const useInvalidateYieldBalances = () => { queryClient.invalidateQueries({ queryKey: getYieldYieldBalancesScanQueryKey(), }), - [queryClient], + [queryClient] ); }; diff --git a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts index 14f3d9a1..2a4a760e 100644 --- a/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts +++ b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts @@ -22,14 +22,14 @@ const getKey = (params: Params) => [ export const getYieldOpportunity = ( params: Params & { queryClient: QueryClient; - }, + } ) => EitherAsync(() => params.queryClient.fetchQuery({ queryKey: getKey(params), staleTime, queryFn: () => queryFn(params), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get yield opportunity"); @@ -38,7 +38,7 @@ export const getYieldOpportunity = ( export const queryFn = async ( params: Params & { signal?: AbortSignal; - }, + } ) => (await fn(params)).unsafeCoerce(); const fn = ({ @@ -59,12 +59,12 @@ const fn = ({ }, }, signal, - }), + }) ), yieldYieldOpportunity( yieldId, { ledgerWalletAPICompatible: isLedgerLive }, - signal, + signal ), ]); @@ -82,7 +82,7 @@ const fn = ({ name: y.metadata.name.replace(/staking/i, ""), }, } satisfies Yield) - : y, + : y ) .mapLeft((e) => { console.log(e); diff --git a/packages/widget/src/hooks/api/use-yield-validators.ts b/packages/widget/src/hooks/api/use-yield-validators.ts index 7d97b176..655a5bc9 100644 --- a/packages/widget/src/hooks/api/use-yield-validators.ts +++ b/packages/widget/src/hooks/api/use-yield-validators.ts @@ -45,20 +45,20 @@ export const getYieldValidatorsQueryFn = async ({ }, }, signal, - }), + }) ); const firstPage = await fetchPage(0); const remainingOffsets = Array.from( { length: Math.ceil(firstPage.total / PAGE_SIZE) - 1 }, - (_, index) => (index + 1) * PAGE_SIZE, + (_, index) => (index + 1) * PAGE_SIZE ); const remainingPages = await Promise.all( remainingOffsets.map((offset) => - fetchPage(offset).catch(() => ({ items: [] })), - ), + fetchPage(offset).catch(() => ({ items: [] })) + ) ); const validators = [firstPage, ...remainingPages] diff --git a/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts b/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts index 1ecdfb03..f38b0b5d 100644 --- a/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts +++ b/packages/widget/src/hooks/navigation/use-navigate-with-scroll-to-top.ts @@ -14,6 +14,6 @@ export const useNavigateWithScrollToTop = () => { } return navigate(to, options); }, - [navigate, disableAutoScrollToTop], + [navigate, disableAutoScrollToTop] ); }; diff --git a/packages/widget/src/hooks/tracking/use-track-page.ts b/packages/widget/src/hooks/tracking/use-track-page.ts index 316ca690..8cf73921 100644 --- a/packages/widget/src/hooks/tracking/use-track-page.ts +++ b/packages/widget/src/hooks/tracking/use-track-page.ts @@ -5,7 +5,7 @@ import { useSavedRef } from "../use-saved-ref"; export const useTrackPage = ( pageName: TrackPageKey, - properties?: Properties, + properties?: Properties ) => { const { trackPageView } = useTracking(); diff --git a/packages/widget/src/hooks/use-add-ledger-account.ts b/packages/widget/src/hooks/use-add-ledger-account.ts index 29e5e40a..e239c600 100644 --- a/packages/widget/src/hooks/use-add-ledger-account.ts +++ b/packages/widget/src/hooks/use-add-ledger-account.ts @@ -16,10 +16,10 @@ export const useAddLedgerAccount = () => { await EitherAsync.liftEither( connector && isLedgerLiveConnector(connector) ? Right(connector) - : Left(new Error("Only Ledger Live is supported")), + : Left(new Error("Only Ledger Live is supported")) ) .chain((ledgerLiveConnector) => - ledgerLiveConnector.requestAndSwitchAccount(chain), + ledgerLiveConnector.requestAndSwitchAccount(chain) ) .ifRight(closeChainModal) ).unsafeCoerce(); diff --git a/packages/widget/src/hooks/use-estimated-rewards.ts b/packages/widget/src/hooks/use-estimated-rewards.ts index 26efd015..1bcfb894 100644 --- a/packages/widget/src/hooks/use-estimated-rewards.ts +++ b/packages/widget/src/hooks/use-estimated-rewards.ts @@ -49,7 +49,7 @@ export const useEstimatedRewards = ({ rewardRateAverage: val.providersDetails .reduce( (acc, val) => acc.plus(new BigNumber(val.rewardRate ?? 0)), - new BigNumber(0), + new BigNumber(0) ) .dividedBy(val.providersDetails.length), })) @@ -60,7 +60,7 @@ export const useEstimatedRewards = ({ }), yearly: val.rewardRateAverage.isGreaterThan(0) ? formatNumber( - correctAmount.times(val.rewardRateAverage).decimalPlaces(5), + correctAmount.times(val.rewardRateAverage).decimalPlaces(5) ) : "-", monthly: val.rewardRateAverage.isGreaterThan(0) @@ -68,10 +68,10 @@ export const useEstimatedRewards = ({ correctAmount .times(val.rewardRateAverage) .dividedBy(12) - .decimalPlaces(5), + .decimalPlaces(5) ) : "-", })), - [providersDetails, selectedStake, correctAmount], + [providersDetails, selectedStake, correctAmount] ); }; diff --git a/packages/widget/src/hooks/use-gas-warning-check.ts b/packages/widget/src/hooks/use-gas-warning-check.ts index 99120b7f..e593fb19 100644 --- a/packages/widget/src/hooks/use-gas-warning-check.ts +++ b/packages/widget/src/hooks/use-gas-warning-check.ts @@ -20,7 +20,7 @@ export const useGasWarningCheck = ( } & ( | { isStake: true; stakeAmount: BigNumber; stakeToken: TokenDto } | { isStake: false } - ), + ) ) => { const requestData = useMemo( () => @@ -35,7 +35,7 @@ export const useGasWarningCheck = ( } : { isStake: props.isStake }, })), - [props], + [props] ); return useQuery({ @@ -45,7 +45,7 @@ export const useGasWarningCheck = ( queryFn: async () => { return ( await EitherAsync.liftEither( - requestData.toEither(new Error("Request data is missing")), + requestData.toEither(new Error("Request data is missing")) ) .chain((val) => checkGasAmount({ @@ -64,12 +64,12 @@ export const useGasWarningCheck = ( tokenAddress: val.gasFeeToken.address, }, ...val.stakeData, - }), + }) ) .map( (val) => val instanceof NotEnoughGasTokenError || - val instanceof GasTokenMissingError, + val instanceof GasTokenMissingError ) ).unsafeCoerce(); }, diff --git a/packages/widget/src/hooks/use-geo-block.ts b/packages/widget/src/hooks/use-geo-block.ts index 9a89b427..4d02881c 100644 --- a/packages/widget/src/hooks/use-geo-block.ts +++ b/packages/widget/src/hooks/use-geo-block.ts @@ -68,5 +68,5 @@ export const useGeoBlock = () => }; }, []), useCallback(() => _isGeoBlocked, []), - useCallback(() => _isGeoBlocked, []), + useCallback(() => _isGeoBlocked, []) ); diff --git a/packages/widget/src/hooks/use-handle-deep-links.ts b/packages/widget/src/hooks/use-handle-deep-links.ts index 103ed9c9..82b1ce27 100644 --- a/packages/widget/src/hooks/use-handle-deep-links.ts +++ b/packages/widget/src/hooks/use-handle-deep-links.ts @@ -23,10 +23,10 @@ export const useHandleDeepLinks = () => { useEffect(() => { initQueryParams .filter((val) => - Boolean(val.yieldId && val.balanceId && !val.pendingaction && appReady), + Boolean(val.yieldId && val.balanceId && !val.pendingaction && appReady) ) .ifJust((val) => - navigateRef.current(`positions/${val.yieldId}/${val.balanceId}`), + navigateRef.current(`positions/${val.yieldId}/${val.balanceId}`) ); }, [initQueryParams, navigateRef, appReady]); @@ -35,12 +35,12 @@ export const useHandleDeepLinks = () => { Maybe.fromNullable(pendingActionDeepLinkCheck.data) .filter( (val): val is Extract => - appReady && val.type === "positionDetails", + appReady && val.type === "positionDetails" ) .ifJust((val) => navigateRef.current( - `positions/${val.yieldOp.id}/${val.balanceId}/select-validator/${val.pendingAction.type}`, - ), + `positions/${val.yieldOp.id}/${val.balanceId}/select-validator/${val.pendingAction.type}` + ) ); }, [navigateRef, pendingActionDeepLinkCheck.data, appReady]); @@ -49,7 +49,7 @@ export const useHandleDeepLinks = () => { Maybe.fromNullable(pendingActionDeepLinkCheck.data) .filter( (val): val is Extract => - appReady && val.type === "review", + appReady && val.type === "review" ) .ifJust((val) => { pendignActionStore.send({ @@ -67,7 +67,7 @@ export const useHandleDeepLinks = () => { }, }); navigateRef.current( - `positions/${val.yieldOp.id}/${val.balanceId}/pending-action/review`, + `positions/${val.yieldOp.id}/${val.balanceId}/pending-action/review` ); }); }, [ diff --git a/packages/widget/src/hooks/use-init-params.ts b/packages/widget/src/hooks/use-init-params.ts index 06392cc2..db7f1147 100644 --- a/packages/widget/src/hooks/use-init-params.ts +++ b/packages/widget/src/hooks/use-init-params.ts @@ -39,7 +39,7 @@ export const useInitParams = (opts?: { }; export const getInitParams = ( - params: Parameters[0] & { queryClient: QueryClient }, + params: Parameters[0] & { queryClient: QueryClient } ) => EitherAsync(() => params.queryClient.fetchQuery({ @@ -47,7 +47,7 @@ export const getInitParams = ( staleTime, gcTime: cacheTime, queryFn: () => queryFn(params), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("could not get init query params"); @@ -70,7 +70,7 @@ const fn = ({ EitherAsync.liftEither( getAndValidateInitParams({ externalProviderInitToken: externalProviders?.initToken, - }).toEither(new Error("missing query params")), + }).toEither(new Error("missing query params")) ).chain((val) => { const yId = val.yieldId; diff --git a/packages/widget/src/hooks/use-init-query-params.ts b/packages/widget/src/hooks/use-init-query-params.ts index edc78302..8174f161 100644 --- a/packages/widget/src/hooks/use-init-query-params.ts +++ b/packages/widget/src/hooks/use-init-query-params.ts @@ -17,7 +17,7 @@ export const useInitQueryParams = () => { getAndValidateInitParams({ externalProviderInitToken: externalProviders?.initToken, }), - [externalProviders?.initToken], + [externalProviders?.initToken] ); }; @@ -28,7 +28,7 @@ const pendingActionCodec = Codec.custom({ .chain((v) => /^[A-Z_]+$/.test(v) ? Right(v as YieldPendingActionType) - : Left("invalid pending action"), + : Left("invalid pending action") ), encode: (val) => val, }); @@ -48,7 +48,7 @@ const safeParamCodec = Codec.custom({ string .decode(val) .chain((v) => - safeString.test(v) ? Right(v) : Left("invalid string value"), + safeString.test(v) ? Right(v) : Left("invalid string value") ), encode: (val) => val, }); @@ -90,8 +90,8 @@ export const getAndValidateInitParams = ({ safeParamCodec.decode(url.searchParams.get("token")).chain((val) => val.includes("-") ? Right(val.split("-").slice(0, -1).join("-")) // network is first part of TokenString - : Left("invalid TokenString"), - ), + : Left("invalid TokenString") + ) ) .chain(skSupportedChainsCodec.decode) .toMaybe() diff --git a/packages/widget/src/hooks/use-invalidate-query-n-times.ts b/packages/widget/src/hooks/use-invalidate-query-n-times.ts index 811c8ca2..0b3eb9ef 100644 --- a/packages/widget/src/hooks/use-invalidate-query-n-times.ts +++ b/packages/widget/src/hooks/use-invalidate-query-n-times.ts @@ -35,8 +35,8 @@ export const useInvalidateQueryNTimes = ({ EitherAsync(async () => { await waitForMs(waitMs); await queryClient.invalidateQueries({ queryKey }); - }).chainLeft(async () => Right(null)), - ), + }).chainLeft(async () => Right(null)) + ) ); return null; diff --git a/packages/widget/src/hooks/use-local-storage-value.ts b/packages/widget/src/hooks/use-local-storage-value.ts index 082898e0..61da7b7a 100644 --- a/packages/widget/src/hooks/use-local-storage-value.ts +++ b/packages/widget/src/hooks/use-local-storage-value.ts @@ -6,12 +6,12 @@ import { } from "../services/local-storage"; export const useLocalStorageValue = ( - key: K, + key: K ) => { const [init] = useState(() => getStorageItem(key) .mapLeft(() => null) - .extract(), + .extract() ); const value = useRef(init); @@ -26,9 +26,9 @@ export const useLocalStorageValue = ( return () => removeListener(); }, - [key], + [key] ), useCallback(() => value.current, []), - useCallback(() => null, []), + useCallback(() => null, []) ); }; diff --git a/packages/widget/src/hooks/use-logout.ts b/packages/widget/src/hooks/use-logout.ts index cae0d6f0..49052555 100644 --- a/packages/widget/src/hooks/use-logout.ts +++ b/packages/widget/src/hooks/use-logout.ts @@ -10,7 +10,7 @@ export const useLogout = () => { await EitherAsync(disconnect) .chain(() => EitherAsync(() => indexedDB.databases())) .ifRight((dbs) => - dbs.forEach((db) => db.name && indexedDB.deleteDatabase(db.name)), + dbs.forEach((db) => db.name && indexedDB.deleteDatabase(db.name)) ); return null; diff --git a/packages/widget/src/hooks/use-max-min-yield-amount.ts b/packages/widget/src/hooks/use-max-min-yield-amount.ts index 5b36bffd..2db678e9 100644 --- a/packages/widget/src/hooks/use-max-min-yield-amount.ts +++ b/packages/widget/src/hooks/use-max-min-yield-amount.ts @@ -35,7 +35,7 @@ export const useMaxMinYieldAmount = ({ .chainNullable((y) => type === "enter" ? getMinStakeAmount(y, positionsData) - : getMinUnstakeAmount(y, pricePerShare), + : getMinUnstakeAmount(y, pricePerShare) ) .map((a) => new BigNumber(a)), [ @@ -45,7 +45,7 @@ export const useMaxMinYieldAmount = ({ yieldOpportunity, positionsData, pricePerShare, - ], + ] ); const maxIntegrationAmount = useMemo(() => { @@ -64,12 +64,12 @@ export const useMaxMinYieldAmount = ({ gasEstimateTotal: new BigNumber(0), integrationMaxLimit: maxIntegrationAmount, }), - [maxIntegrationAmount, availableAmount], + [maxIntegrationAmount, availableAmount] ); const minEnterOrExitAmount = useMemo( () => minIntegrationAmount.orDefault(new BigNumber(0)), - [minIntegrationAmount], + [minIntegrationAmount] ); return useMemo( @@ -88,6 +88,6 @@ export const useMaxMinYieldAmount = ({ minEnterOrExitAmount, maxIntegrationAmount, isForceMax, - ], + ] ); }; 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 4aa2e87c..cb7e7af4 100644 --- a/packages/widget/src/hooks/use-position-balance-by-type.ts +++ b/packages/widget/src/hooks/use-position-balance-by-type.ts @@ -18,9 +18,9 @@ export const usePositionBalanceByType = ({ positionBalancesData.map((val) => getPositionBalanceByTypeWithUsd({ pvd: val.balances, - }), + }) ), - [positionBalancesData], + [positionBalancesData] ); }; @@ -38,7 +38,7 @@ export const getPositionBalanceByTypeWithUsd = createSelector( if (amount.isZero() || amount.isNaN()) return acc; const tokenPriceInUsd = new BigNumber( - String(cur.amountUsd ?? 0).replace(/,/g, ""), + String(cur.amountUsd ?? 0).replace(/,/g, "") ); const prev = acc.get(cur.type); @@ -46,5 +46,5 @@ export const getPositionBalanceByTypeWithUsd = createSelector( acc.set(cur.type, [...(prev ?? []), { ...cur, tokenPriceInUsd }]); return acc; - }, new Map() as PositionBalancesByType), + }, new Map() as PositionBalancesByType) ); diff --git a/packages/widget/src/hooks/use-position-balances.ts b/packages/widget/src/hooks/use-position-balances.ts index f2619e1b..613e8641 100644 --- a/packages/widget/src/hooks/use-position-balances.ts +++ b/packages/widget/src/hooks/use-position-balances.ts @@ -31,7 +31,7 @@ export const usePositionBalances = ({ rewardRate: val.positionData.rewardRate, }; }), - [balanceId, data], + [balanceId, data] ); return { data: value, ...rest }; diff --git a/packages/widget/src/hooks/use-position-data.ts b/packages/widget/src/hooks/use-position-data.ts index 6d9e53b1..ae5c85fc 100644 --- a/packages/widget/src/hooks/use-position-data.ts +++ b/packages/widget/src/hooks/use-position-data.ts @@ -15,7 +15,7 @@ export const usePositionData = ({ id: Maybe.fromNullable(integrationId), data: Maybe.fromNullable(data), }).chainNullable((val) => val.data.get(val.id)), - [integrationId, data], + [integrationId, data] ); return { data: val, ...rest }; diff --git a/packages/widget/src/hooks/use-positions-data.ts b/packages/widget/src/hooks/use-positions-data.ts index 6989a80e..f88c60e3 100644 --- a/packages/widget/src/hooks/use-positions-data.ts +++ b/packages/widget/src/hooks/use-positions-data.ts @@ -18,7 +18,7 @@ export const usePositionsData = () => { const val = useMemo>( () => data ?? new Map(), - [data], + [data] ); return { data: val, ...rest }; @@ -34,8 +34,8 @@ const positionsDataSelector = createSelector( balanceData: [...val.balances] .sort((a, b) => getPositionBalanceDataKey(a).localeCompare( - getPositionBalanceDataKey(b), - ), + getPositionBalanceDataKey(b) + ) ) .reduce((acc, b) => { const key = getPositionBalanceDataKey(b); @@ -70,7 +70,7 @@ const positionsDataSelector = createSelector( }); return acc; - }, new Map() as PositionsData), + }, new Map() as PositionsData) ); const getBalanceValidatorAddresses = (balance: YieldBalanceDto) => diff --git a/packages/widget/src/hooks/use-provider-details.ts b/packages/widget/src/hooks/use-provider-details.ts index 0aaea3a6..7fbd7aa4 100644 --- a/packages/widget/src/hooks/use-provider-details.ts +++ b/packages/widget/src/hooks/use-provider-details.ts @@ -73,7 +73,7 @@ export const getProviderDetails = ({ rewardRate, rewardType, address: validatorAddress.extract(), - }), + }) ); }); @@ -82,21 +82,21 @@ export const getProviderDetails = ({ .chain>((addr) => List.find( (v) => v.address === addr || v.providerId === addr, - validatorsData ?? [], + validatorsData ?? [] ).map((validator) => { const { rewardRate, rewardType } = Maybe.fromRecord({ _: Maybe.fromFalsy(isYieldWithProviderOptions(yieldDto)), selectedProviderYieldId, }) .chain(({ selectedProviderYieldId }) => - yields.chain(List.find((v) => v.id === selectedProviderYieldId)), + yields.chain(List.find((v) => v.id === selectedProviderYieldId)) ) .map((v) => getYieldRewardRate(v) + getYieldRewardRate(v)) .map<{ rewardRate: number | undefined; rewardType: RewardTypes }>( (res) => ({ rewardRate: res, rewardType: getYieldRewardType(yieldDto), - }), + }) ) .orDefault({ rewardRate: validator.apr, @@ -120,9 +120,9 @@ export const getProviderDetails = ({ website: validator.website, preferred: validator.preferred, }; - }), + }) ) - .altLazy(() => def), + .altLazy(() => def) ); }; @@ -138,14 +138,14 @@ export const useProvidersDetails = ({ validatorsData?: Maybe; }) => { const yields = useMultiYields( - integrationData.map(getYieldProviderYieldIds).orDefault([]), + integrationData.map(getYieldProviderYieldIds).orDefault([]) ); const shouldFetchValidators = validatorsAddresses .filter((val): val is string[] => !(val instanceof Map)) .map((val) => val.length > 0) .chain((val) => - validatorsData?.isJust() ? Maybe.of(false) : Maybe.of(val), + validatorsData?.isJust() ? Maybe.of(false) : Maybe.of(val) ) .orDefault(false); @@ -164,15 +164,15 @@ export const useProvidersDetails = ({ validatorsAddresses.chain((val) => val instanceof Map ? Maybe.of([...val.values()]) - : Maybe.fromNullable(yieldValidators.data), - ), + : Maybe.fromNullable(yieldValidators.data) + ) ) ?? validatorsAddresses.chain((val) => val instanceof Map ? Maybe.of([...val.values()]) - : Maybe.fromNullable(yieldValidators.data), + : Maybe.fromNullable(yieldValidators.data) ), - [validatorsAddresses, validatorsData, yieldValidators.data], + [validatorsAddresses, validatorsData, yieldValidators.data] ); return useMemo>[]>>( @@ -190,8 +190,8 @@ export const useProvidersDetails = ({ selectedProviderYieldId, validatorsData: resolvedValidatorsData.extractNullable() ?? undefined, - }), - ), + }) + ) ).chain((val) => val.length ? Maybe.of(val) @@ -200,8 +200,8 @@ export const useProvidersDetails = ({ validatorAddress: Maybe.empty(), yields: Maybe.fromNullable(yields.data), selectedProviderYieldId, - }).map((v) => [v]), - ), + }).map((v) => [v]) + ) ), [ integrationData, @@ -209,6 +209,6 @@ export const useProvidersDetails = ({ yields.data, selectedProviderYieldId, resolvedValidatorsData, - ], + ] ); }; diff --git a/packages/widget/src/hooks/use-region-code-names.ts b/packages/widget/src/hooks/use-region-code-names.ts index 970b85fc..2ed62088 100644 --- a/packages/widget/src/hooks/use-region-code-names.ts +++ b/packages/widget/src/hooks/use-region-code-names.ts @@ -11,8 +11,8 @@ export const useRegionCodeName = (regionCode: Nullable) => { ( await EitherAsync.liftEither( Maybe.fromNullable(regionCode).toEither( - new Error("missing regionCode"), - ), + new Error("missing regionCode") + ) ).chain((region) => EitherAsync(() => import("../utils/region-iso-3166-codes")) .mapLeft(() => new Error("Failed to load region-iso-3166-codes")) @@ -20,10 +20,10 @@ export const useRegionCodeName = (regionCode: Nullable) => { EitherAsync.liftEither( Maybe.fromNullable( val.countries[region as keyof typeof val.countries] - .subdivisionName, - ).toEither(new Error("region not found")), - ), - ), + .subdivisionName + ).toEither(new Error("region not found")) + ) + ) ) ).unsafeCoerce(), }); diff --git a/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx b/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx index 29ae9c6e..2d37b8a8 100644 --- a/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx +++ b/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx @@ -31,7 +31,7 @@ export const getRewardTokenSymbols = (rewardTokens: TokenDto[]) => str: val.symbol, })} - ), + ) ); const maybeAddComma = ({ diff --git a/packages/widget/src/hooks/use-reward-token-details/index.ts b/packages/widget/src/hooks/use-reward-token-details/index.ts index 0dfee4ab..dbab7cb1 100644 --- a/packages/widget/src/hooks/use-reward-token-details/index.ts +++ b/packages/widget/src/hooks/use-reward-token-details/index.ts @@ -8,7 +8,7 @@ import type { ExtraData } from "../../pages/details/earn-page/state/types"; import { getRewardTokenSymbols } from "./get-reward-token-symbols"; export const useRewardTokenDetails = ( - yieldOpportunity: ExtraData["selectedStake"], + yieldOpportunity: ExtraData["selectedStake"] ) => { return useMemo( () => @@ -17,7 +17,7 @@ export const useRewardTokenDetails = ( Maybe.fromNullable(getYieldProviderDetails(y)).map((p) => ({ p, rt: getYieldRewardTokens(y), - })), + })) ) .map(({ p, rt }) => ({ logoUri: p.logoURI ?? null, @@ -25,6 +25,6 @@ export const useRewardTokenDetails = ( symbols: getRewardTokenSymbols(rt), providerName: p.name ?? null, })), - [yieldOpportunity], + [yieldOpportunity] ); }; diff --git a/packages/widget/src/hooks/use-rewards-summary.ts b/packages/widget/src/hooks/use-rewards-summary.ts index 6467126e..ac206ede 100644 --- a/packages/widget/src/hooks/use-rewards-summary.ts +++ b/packages/widget/src/hooks/use-rewards-summary.ts @@ -1,10 +1,10 @@ -import { - type AddressesDto, - type YieldRewardsSummaryResponseDto, - yieldGetSingleYieldRewardsSummary, +import type { + AddressesDto, + YieldRewardsSummaryResponseDto, } from "@stakekit/api-hooks"; import { type QueryClient, useQuery } from "@tanstack/react-query"; import { useMemo } from "react"; +import { yieldGetSingleYieldRewardsSummary } from "../common/private-api"; import { type EnabledRewardsSummaryYieldId, isValidYieldIdForRewardsSummary, @@ -15,11 +15,11 @@ import { useSKWallet } from "../providers/sk-wallet"; export const useMultiRewardsSummary = ( yieldIds: Yield["id"][], - opts?: { select?: (val: RewardsSummaryResult) => T }, + opts?: { select?: (val: RewardsSummaryResult) => T } ) => { const filteredYieldIds = useMemo( () => yieldIds.filter(isValidYieldIdForRewardsSummary), - [yieldIds], + [yieldIds] ); const { address, additionalAddresses } = useSKWallet(); @@ -46,8 +46,8 @@ export const useMultiRewardsSummary = ( address: address!, additionalAddresses: additionalAddresses ?? undefined, }, - }).then((res) => ({ yieldId: id, data: res })), - ), + }).then((res) => ({ yieldId: id, data: res })) + ) ).then((res) => res.reduce( (acc, next) => { @@ -57,8 +57,8 @@ export const useMultiRewardsSummary = ( {} as Record< EnabledRewardsSummaryYieldId, YieldRewardsSummaryResponseDto - >, - ), + > + ) ), }); }; diff --git a/packages/widget/src/hooks/use-rich-errors.ts b/packages/widget/src/hooks/use-rich-errors.ts index 9ab246e0..71d8076e 100644 --- a/packages/widget/src/hooks/use-rich-errors.ts +++ b/packages/widget/src/hooks/use-rich-errors.ts @@ -36,7 +36,7 @@ export const handleRichErrorResponse = ({ export const attachRichErrorsInterceptor = ( apiClient: AxiosInstance, - i18n: i18n, + i18n: i18n ) => apiClient.interceptors.response.use(undefined, (error) => { handleRichErrorResponse({ @@ -58,7 +58,7 @@ export const useRichErrors = () => { }; }, []), useCallback(() => $richError.value, []), - useCallback(() => $richError.value, []), + useCallback(() => $richError.value, []) ); const resetError = () => $richError.next(null); 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 c6286180..544a81eb 100644 --- a/packages/widget/src/hooks/use-staked-or-liquid-balance.ts +++ b/packages/widget/src/hooks/use-staked-or-liquid-balance.ts @@ -3,13 +3,13 @@ import { useMemo } from "react"; import type { PositionBalancesByType } from "../domain/types/positions"; export const useStakedOrLiquidBalance = ( - positionBalancesByType: Maybe, + positionBalancesByType: Maybe ) => { return useMemo( () => positionBalancesByType.chain((pbbt) => - Maybe.fromNullable(pbbt.get("active")), + Maybe.fromNullable(pbbt.get("active")) ), - [positionBalancesByType], + [positionBalancesByType] ); }; diff --git a/packages/widget/src/hooks/use-summary.tsx b/packages/widget/src/hooks/use-summary.tsx index f6393629..1bfe5065 100644 --- a/packages/widget/src/hooks/use-summary.tsx +++ b/packages/widget/src/hooks/use-summary.tsx @@ -66,13 +66,13 @@ export const SummaryProvider = ({ const yieldIds = useMemo( () => [...new Set(positionsData.data.map((p) => p.integrationId)).values()], - [positionsData.data], + [positionsData.data] ); const multiYieldsMapQuery = useMultiYields(yieldIds, { select: useCallback( (val: Yield[]) => new Map(val.map((y) => [y.id, y])), - [], + [] ), }); @@ -88,7 +88,7 @@ export const SummaryProvider = ({ return acc; }, {} as RewardsSummaryResult), - [], + [] ), }); @@ -107,7 +107,7 @@ export const SummaryProvider = ({ const positionTotalAmount = getPositionTotalAmount( p.balancesWithAmount, - getBaseToken(yieldDto), + getBaseToken(yieldDto) ); const yields = [...multiYieldsMapQuery.data.values()]; @@ -131,7 +131,7 @@ export const SummaryProvider = ({ const allPositionsSum = allPositions.reduce( (acc, p) => acc.plus(p.usdAmount), - new BigNumber(0), + new BigNumber(0) ); return { @@ -149,7 +149,7 @@ export const SummaryProvider = ({ currency: config.currency, tokenList: useMemo( () => Object.values(rewardsSummaryQuery.data ?? {}).map((v) => v.token), - [rewardsSummaryQuery.data], + [rewardsSummaryQuery.data] ), }, { @@ -166,7 +166,7 @@ export const SummaryProvider = ({ } const rewardsPositions = Object.entries( - rewardsSummaryQuery.data, + rewardsSummaryQuery.data ).flatMap(([integrationId, rewardSummary]) => { const yieldDto = multiYieldsMapQuery.data.get(integrationId); @@ -200,17 +200,17 @@ export const SummaryProvider = ({ const rewardsPositionsTotalSum = rewardsPositions.reduce( (acc, p) => acc.plus(p.total), - new BigNumber(0), + new BigNumber(0) ); const rewardsPositionsLastMonthSum = rewardsPositions.reduce( (acc, p) => acc.plus(p.lastMonth), - new BigNumber(0), + new BigNumber(0) ); const rewardsPositionsLastWeekSum = rewardsPositions.reduce( (acc, p) => acc.plus(p.lastWeek), - new BigNumber(0), + new BigNumber(0) ); return { @@ -220,9 +220,9 @@ export const SummaryProvider = ({ rewardsPositionsLastWeekSum, }; }, - [multiYieldsMapQuery.data, rewardsSummaryQuery.data], + [multiYieldsMapQuery.data, rewardsSummaryQuery.data] ), - }, + } ); const averageApyQuery = useMemo(() => { @@ -241,7 +241,7 @@ export const SummaryProvider = ({ const positionTotalAmount = getPositionTotalAmount( p.balancesWithAmount, - getBaseToken(yieldDto), + getBaseToken(yieldDto) ); const usdAmount = positionTotalAmount.amountUsd; @@ -251,7 +251,7 @@ export const SummaryProvider = ({ if (rewardRate > 0 && usdAmount.gt(0)) { return { totalWeightedApy: acc.totalWeightedApy.plus( - usdAmount.times(rewardRate * 100), + usdAmount.times(rewardRate * 100) ), totalValue: acc.totalValue.plus(usdAmount), }; @@ -262,7 +262,7 @@ export const SummaryProvider = ({ { totalWeightedApy: new BigNumber(0), totalValue: new BigNumber(0), - }, + } ); const data = totalValue.gt(0) @@ -280,7 +280,7 @@ export const SummaryProvider = ({ const tokenList = useMemo( () => tokenBalancesScan.data?.map((tb) => tb.token), - [tokenBalancesScan.data], + [tokenBalancesScan.data] ); const availableBalanceSumQuery = usePrices( @@ -305,14 +305,14 @@ export const SummaryProvider = ({ baseToken: tb.token, token: tb.token, prices, - }), + }) ), - BigNumber(0), + BigNumber(0) ); }, - [tokenBalancesScan.data], + [tokenBalancesScan.data] ), - }, + } ); const value = useMemo( @@ -327,7 +327,7 @@ export const SummaryProvider = ({ rewardsPositionsQuery, averageApyQuery, availableBalanceSumQuery, - ], + ] ); return ( diff --git a/packages/widget/src/hooks/use-sync-element-height.ts b/packages/widget/src/hooks/use-sync-element-height.ts index a0ee47aa..8cf8748d 100644 --- a/packages/widget/src/hooks/use-sync-element-height.ts +++ b/packages/widget/src/hooks/use-sync-element-height.ts @@ -2,7 +2,7 @@ import { useEffect, useRef } from "react"; import { useSavedRef } from "./use-saved-ref"; export const useSyncElementHeight = ( - setCurrentHeight: (height: number) => void, + setCurrentHeight: (height: number) => void ) => { const containerRef = useRef(null); const setCurrentHeightRef = useSavedRef(setCurrentHeight); diff --git a/packages/widget/src/hooks/use-under-maintenance.ts b/packages/widget/src/hooks/use-under-maintenance.ts index b262bd89..d2c04b70 100644 --- a/packages/widget/src/hooks/use-under-maintenance.ts +++ b/packages/widget/src/hooks/use-under-maintenance.ts @@ -34,7 +34,7 @@ export const useUnderMaintenance = () => { class YieldApiResponseError extends Error { constructor( readonly status: number, - override readonly cause?: unknown, + override readonly cause?: unknown ) { super("Yield API health request failed"); } diff --git a/packages/widget/src/hooks/use-validators-config.ts b/packages/widget/src/hooks/use-validators-config.ts index b31f8575..5323a1dd 100644 --- a/packages/widget/src/hooks/use-validators-config.ts +++ b/packages/widget/src/hooks/use-validators-config.ts @@ -18,8 +18,8 @@ export const useValidatorsConfig = (): ValidatorsConfig => { mergePreferredWithDefault: val.mergePreferredWithDefault ?? true, preferredOnly: val.preferredOnly ?? false, }, - ]), + ]) ) satisfies ValidatorsConfig, - [validatorsConfig], + [validatorsConfig] ); }; diff --git a/packages/widget/src/hooks/use-yield-meta-info.tsx b/packages/widget/src/hooks/use-yield-meta-info.tsx index f4cac646..3b703acf 100644 --- a/packages/widget/src/hooks/use-yield-meta-info.tsx +++ b/packages/widget/src/hooks/use-yield-meta-info.tsx @@ -35,9 +35,9 @@ export const useYieldMetaInfo = ({ t("details.selected_validators", { providerName: v.name ?? v.address, count: validators.length - 1, - }), + }) ), - [validators, t], + [validators, t] ); return useMemo(() => { diff --git a/packages/widget/src/main.tsx b/packages/widget/src/main.tsx index c6caa568..fb00655d 100644 --- a/packages/widget/src/main.tsx +++ b/packages/widget/src/main.tsx @@ -69,7 +69,7 @@ const enableMocking = async () => { enableMocking() .then(() => { ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - , + ); }) .catch((e) => { diff --git a/packages/widget/src/navigation/containers/animation-layout.tsx b/packages/widget/src/navigation/containers/animation-layout.tsx index c8993abf..ef27efcc 100644 --- a/packages/widget/src/navigation/containers/animation-layout.tsx +++ b/packages/widget/src/navigation/containers/animation-layout.tsx @@ -49,7 +49,7 @@ export const AnimationLayout = ({ children }: PropsWithChildren) => { } return { duration: 0.6, delay: 0.3 }; }) - .map((transition) => ({ height, transition })), + .map((transition) => ({ height, transition })) ) .unsafeCoerce(); diff --git a/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx index bde6629c..39ece53c 100644 --- a/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx +++ b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx @@ -94,7 +94,7 @@ export const ActionListItem = ({ count: Math.max(val.length - 1, 1), })} - )), + )) ) .extractNullable()} @@ -125,7 +125,7 @@ export const ActionListItem = ({ ), - , + )} ); @@ -171,7 +171,7 @@ const Badge = ({ noWrap, badgeText({ type: badgeColor ? "white" : "regular", - }), + }) )} > {badgeLabel} diff --git a/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx b/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx index 915a411c..287faf66 100644 --- a/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx +++ b/packages/widget/src/pages-dashboard/activity/activity-details.page.tsx @@ -13,12 +13,12 @@ export const ActivityDetailsPage = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction, + (state) => state.context.selectedAction ).extractNullable(); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield, + (state) => state.context.selectedYield ).extractNullable(); if (!selectedYield || !selectedAction) { diff --git a/packages/widget/src/pages-dashboard/activity/activity.page.tsx b/packages/widget/src/pages-dashboard/activity/activity.page.tsx index 01ae4075..1b0c93a2 100644 --- a/packages/widget/src/pages-dashboard/activity/activity.page.tsx +++ b/packages/widget/src/pages-dashboard/activity/activity.page.tsx @@ -138,7 +138,7 @@ const _ActivityPage = () => { const selectedAction = useSelector( activityStore, - (state) => state.context.selectedAction, + (state) => state.context.selectedAction ); // biome-ignore lint: false @@ -171,7 +171,7 @@ const _ActivityPage = () => { selectedAction: val.actionData, selectedYield: val.yieldData, }), - }), + }) ); }, [selectedAction, activityStore, value.activityActions.allItems]); diff --git a/packages/widget/src/pages-dashboard/common/components/header.tsx b/packages/widget/src/pages-dashboard/common/components/header.tsx index d8c6ced5..0b16ffcd 100644 --- a/packages/widget/src/pages-dashboard/common/components/header.tsx +++ b/packages/widget/src/pages-dashboard/common/components/header.tsx @@ -44,7 +44,7 @@ export const Header = () => { gap="2" > {Maybe.fromFalsy( - (isConnected || isConnecting) && chain && account, + (isConnected || isConnecting) && chain && account ) .map(() => ( <> diff --git a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx index 94101b52..a3d7a23f 100644 --- a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx +++ b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-section.tsx @@ -33,7 +33,7 @@ export const UtilaSelectValidatorSection = () => { const multiSelect = isYieldActionArgRequired( val.selectedStake, "enter", - "validatorAddresses", + "validatorAddresses" ); return ( 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 994c16a9..3a637618 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 @@ -66,7 +66,7 @@ export const PositionsListItem = memo( ), - , + )} @@ -83,7 +83,7 @@ export const PositionsListItem = memo( `position_details.labels.${label.type as PositionDetailsLabelType}.details`, label.params as | Record - | undefined, + | undefined )} > {t( - `position_details.labels.${label.type as PositionDetailsLabelType}.label`, + `position_details.labels.${label.type as PositionDetailsLabelType}.label` )} @@ -128,7 +128,7 @@ export const PositionsListItem = memo( ? inactiveValidator === "jailed" ? "details.validators_jailed" : "details.validators_inactive" - : "positions.claim_rewards", + : "positions.claim_rewards" )} @@ -149,7 +149,7 @@ export const PositionsListItem = memo( count: Math.max(val.length - 1, 1), })} - )), + )) ) .extractNullable()} @@ -186,7 +186,7 @@ export const PositionsListItem = memo( )) .orDefault( - -, + - )} @@ -224,10 +224,10 @@ export const PositionsListItem = memo( )} ), - , + )} ); - }, + } ); 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 cc07b1a5..f58a28b5 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 @@ -10,7 +10,7 @@ import type { usePositions } from "../../../../pages/details/positions-page/hook import { defaultFormattedNumber } from "../../../../utils"; export const usePositionListItem = ( - item: ReturnType["positionsData"]["data"][number], + item: ReturnType["positionsData"]["data"][number] ) => { const { integrationData, @@ -27,9 +27,9 @@ export const usePositionListItem = ( const rewardsSummary = useMemo( () => Maybe.fromNullable(rewardsSummaryQuery.data?.data).filter((val) => - BigNumber(val.rewards.total).gt(0), + BigNumber(val.rewards.total).gt(0) ), - [rewardsSummaryQuery.data], + [rewardsSummaryQuery.data] ); const prices = usePrices({ @@ -45,7 +45,7 @@ export const usePositionListItem = ( rewardsSummary .map((val) => BigNumber(val.rewards.total)) .map(defaultFormattedNumber), - [rewardsSummary], + [rewardsSummary] ); const totalAmountPriceFormatted = useMemo( @@ -53,7 +53,7 @@ export const usePositionListItem = ( totalAmountUsd .filter((v) => v.isGreaterThan(0)) .map(defaultFormattedNumber), - [totalAmountUsd], + [totalAmountUsd] ); const rewardsAmountPriceFormatted = useMemo( @@ -70,10 +70,10 @@ export const usePositionListItem = ( pricePerShare: null, token: val.rewardsSummary.token, prices: val.prices, - }), + }) ) .map(defaultFormattedNumber), - [rewardsSummary, baseToken, prices], + [rewardsSummary, baseToken, prices] ); return { diff --git a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx index 3ef3d56d..2ac719d2 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx @@ -12,7 +12,7 @@ import type { YieldPendingActionType } from "../../../providers/yield-api-client import { container } from "./styles.css"; export const positionDetailsActionsHasContent = ( - val: ReturnType, + val: ReturnType ) => Maybe.fromRecord({ integrationData: val.integrationData, @@ -27,7 +27,7 @@ export const positionDetailsActionsHasContent = ( canChangeUnstakeAmount: val.canChangeUnstakeAmount, unstakeToken: val.unstakeToken, }).map(() => true), - ]).length > 0, + ]).length > 0 ) .isJust(); @@ -105,7 +105,7 @@ export const PositionDetailsActions = () => { label={t( `position_details.pending_action_button.${ val.pendingActionDto.type.toLowerCase() as Lowercase - }`, + }` )} onMaxClick={null} formattedAmount={val.formattedAmount} @@ -118,8 +118,8 @@ export const PositionDetailsActions = () => { onPendingActionClick={onPendingActionClick} yieldId={v.integrationData.id} /> - ), - ), + ) + ) ) .extractNullable()} @@ -152,14 +152,14 @@ export const PositionDetailsActions = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${getYieldMetadata(v.integrationData).type}`, + `position_details.unstake_label.${getYieldMetadata(v.integrationData).type}` )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} yieldDto={v.integrationData} validators={providersDetails.orDefault([])} /> - ), + ) ) .extractNullable()} 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 6d0138af..8c7da23f 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 @@ -58,11 +58,11 @@ export const PositionDetailsInfo = () => { {...p} key={p.address ?? idx} stakeType={t( - `position_details.stake_type.${getYieldMetadata(val.integrationData).type}`, + `position_details.stake_type.${getYieldMetadata(val.integrationData).type}` )} integrationData={val.integrationData} /> - )), + )) ) .extractNullable()} @@ -97,7 +97,7 @@ export const PositionDetailsInfo = () => { integrationData={val.integrationData} yieldBalance={yb} /> - )), + )) )} diff --git a/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx b/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx index d8c87427..6f9d8066 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx @@ -90,7 +90,7 @@ export const ProviderDetails = ({ {t( providerDetails.status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive", + : "details.validators_inactive" )} @@ -129,7 +129,7 @@ const ValidatorMeta = memo((props: Parameters[0]) => { {Object.entries(metaInfo) .filter( (val): val is [keyof typeof metaInfo, NonNullable<(typeof val)[1]>] => - !!val[1], + !!val[1] ) .map(([key, val]) => { return ( diff --git a/packages/widget/src/pages-dashboard/position-details/index.tsx b/packages/widget/src/pages-dashboard/position-details/index.tsx index 470d0b9f..87d7c2c9 100644 --- a/packages/widget/src/pages-dashboard/position-details/index.tsx +++ b/packages/widget/src/pages-dashboard/position-details/index.tsx @@ -17,7 +17,7 @@ import { headerContainer, posistionDetailsInfoContainer } from "./styles.css"; const PositionDetailsPageComponent = () => { const shouldShowActions = positionDetailsActionsHasContent( - usePositionDetails(), + usePositionDetails() ); const location = useSKLocation(); diff --git a/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx b/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx index a46e1375..cfe52a67 100644 --- a/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx +++ b/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx @@ -65,7 +65,7 @@ export const PositionsListItem = memo( ), - , + )} @@ -84,7 +84,7 @@ export const PositionsListItem = memo( `position_details.labels.${label.type as PositionDetailsLabelType}.details`, label.params as | Record - | undefined, + | undefined )} > {t( - `position_details.labels.${label.type as PositionDetailsLabelType}.label`, + `position_details.labels.${label.type as PositionDetailsLabelType}.label` )} @@ -129,7 +129,7 @@ export const PositionsListItem = memo( ? inactiveValidator === "jailed" ? "details.validators_jailed" : "details.validators_inactive" - : "positions.claim_rewards", + : "positions.claim_rewards" )} @@ -150,7 +150,7 @@ export const PositionsListItem = memo( count: Math.max(val.length - 1, 1), })} - )), + )) ) .extractNullable()} @@ -179,13 +179,13 @@ export const PositionsListItem = memo( {rewardsAmountPriceFormatted .map((v) => <>{v}$) .orDefault( - -, + - )} )) .orDefault( - -, + - )} @@ -223,10 +223,10 @@ export const PositionsListItem = memo( )} ), - , + )} ); - }, + } ); diff --git a/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts b/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts index 7e80d02c..2ac7d253 100644 --- a/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts +++ b/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts @@ -20,7 +20,7 @@ export const useActivityComplete = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction, + (state) => state.context.selectedAction ).unsafeCoerce(); const amount = useMemo( @@ -28,12 +28,12 @@ export const useActivityComplete = () => { Maybe.fromNullable(selectedAction.amount) .map(defaultFormattedNumber) .unsafeCoerce(), - [selectedAction], + [selectedAction] ); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield, + (state) => state.context.selectedYield ); const yieldType = useYieldType(selectedYield).map((v) => v.type); @@ -44,14 +44,14 @@ export const useActivityComplete = () => { getActionInputToken({ actionDto: selectedAction, yieldDto: selectedYield.extractNullable() ?? undefined, - }), + }) ), - [selectedAction, selectedYield], + [selectedAction, selectedYield] ) as Maybe; const metadata = useMemo( () => selectedYield.map(getYieldMetadata), - [selectedYield], + [selectedYield] ); const network = inputToken.mapOrDefault((y) => y.symbol, ""); @@ -59,7 +59,7 @@ export const useActivityComplete = () => { const providerDetails = useProvidersDetails({ integrationData: selectedYield, validatorsAddresses: Maybe.of( - getActionValidatorAddresses(selectedAction) ?? [], + getActionValidatorAddresses(selectedAction) ?? [] ), selectedProviderYieldId: Maybe.of(selectedAction.yieldId), }); diff --git a/packages/widget/src/pages/complete/hooks/use-complete.hook.ts b/packages/widget/src/pages/complete/hooks/use-complete.hook.ts index c423b800..f30093b2 100644 --- a/packages/widget/src/pages/complete/hooks/use-complete.hook.ts +++ b/packages/widget/src/pages/complete/hooks/use-complete.hook.ts @@ -66,8 +66,8 @@ export const useComplete = () => { onClick: () => onClickRef.current(), hide: !!activityReviewMatch, }), - [onClickRef, t, activityReviewMatch, isLedgerLive], - ), + [onClickRef, t, activityReviewMatch, isLedgerLive] + ) ); return { diff --git a/packages/widget/src/pages/complete/pages/common.page.tsx b/packages/widget/src/pages/complete/pages/common.page.tsx index 9f604a33..da259f6f 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -124,7 +124,7 @@ export const CompletePageComponent = ({ ? "ethena_usde" : undefined, }), - "", + "" ), amount, tokenNetwork: network, @@ -136,9 +136,9 @@ export const CompletePageComponent = ({ context: isEthenaUsdeStaking(integrationId) ? "ethena_usde" : undefined, - }, + } ), - }, + } )} @@ -172,7 +172,7 @@ export const CompletePageComponent = ({ {t("complete.via", { providerName: v.name })} - )), + )) ) .extractNullable() : null} @@ -206,8 +206,8 @@ export const CompletePageComponent = ({ context: isEthenaUsdeStaking(integrationId) ? "ETHENA_USDE" : undefined, - } as never, - ) as unknown as string, + } as never + ) as unknown as string ) .map(capitalizeFirstLowerRest) .extract(), diff --git a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx index fdb344cc..a403837c 100644 --- a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx @@ -22,17 +22,17 @@ export const PendingCompletePage = () => { const pendingRequest = useSelector( usePendingActionStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const integrationData = useMemo( () => Maybe.of(pendingRequest.integrationData), - [pendingRequest.integrationData], + [pendingRequest.integrationData] ); const token = useMemo( () => Maybe.of(pendingRequest.interactedToken), - [pendingRequest.interactedToken], + [pendingRequest.interactedToken] ); useTrackPage("pendingActionCompelete"); @@ -40,7 +40,7 @@ export const PendingCompletePage = () => { const providerDetails = useProvidersDetails({ integrationData, validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [], + p.type === "validators" ? p.validatorsAddresses : [] ), selectedProviderYieldId: Maybe.empty(), }); @@ -52,7 +52,7 @@ export const PendingCompletePage = () => { Maybe.fromNullable(pendingRequest.requestDto.arguments?.amount) .map((val) => new BigNumber(val ?? 0)) .mapOrDefault((v) => formatNumber(v), ""), - [pendingRequest.requestDto.arguments?.amount], + [pendingRequest.requestDto.arguments?.amount] ); const yieldType = useYieldType(integrationData).map((v) => v.type); diff --git a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx index 51330d54..973c5978 100644 --- a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx @@ -15,17 +15,17 @@ export const StakeCompletePage = () => { const enterRequest = useSelector( useEnterStakeStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const selectedStake = useMemo( () => Maybe.of(enterRequest.selectedStake), - [enterRequest.selectedStake], + [enterRequest.selectedStake] ); const selectedToken = useMemo( () => Maybe.of(enterRequest.selectedToken), - [enterRequest.selectedToken], + [enterRequest.selectedToken] ); const metadata = selectedStake.map(getYieldMetadata); @@ -35,16 +35,16 @@ export const StakeCompletePage = () => { const amount = useMemo( () => formatNumber( - new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0), + new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0) ), - [enterRequest.requestDto.arguments?.amount], + [enterRequest.requestDto.arguments?.amount] ); const yieldType = useYieldType(selectedStake).map((v) => v.type); const selectedProviderYieldId = useMemo( () => Maybe.fromNullable(enterRequest.requestDto.arguments?.providerId), - [enterRequest.requestDto.arguments?.providerId], + [enterRequest.requestDto.arguments?.providerId] ); const providerDetails = useProvidersDetails({ diff --git a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx index 890c64f3..61e9202d 100644 --- a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx @@ -20,12 +20,12 @@ export const UnstakeCompletePage = () => { const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const integrationData = useMemo( () => Maybe.of(exitRequest.integrationData), - [exitRequest.integrationData], + [exitRequest.integrationData] ); useTrackPage("unstakeComplete"); @@ -33,21 +33,21 @@ export const UnstakeCompletePage = () => { const providerDetails = useProvidersDetails({ integrationData, validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [], + p.type === "validators" ? p.validatorsAddresses : [] ), selectedProviderYieldId: Maybe.empty(), }); const token = useMemo( () => Maybe.of(exitRequest.unstakeToken), - [exitRequest.unstakeToken], + [exitRequest.unstakeToken] ); const metadata = integrationData.map(getYieldMetadata); const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( () => formatNumber(exitRequest.requestDto.arguments?.amount ?? 0), - [exitRequest.requestDto.arguments?.amount], + [exitRequest.requestDto.arguments?.amount] ); const yieldType = useYieldType(integrationData).map((v) => v.type); diff --git a/packages/widget/src/pages/complete/state/index.tsx b/packages/widget/src/pages/complete/state/index.tsx index 2b7bc856..c5455d3b 100644 --- a/packages/widget/src/pages/complete/state/index.tsx +++ b/packages/widget/src/pages/complete/state/index.tsx @@ -36,7 +36,7 @@ export const useCompleteCommonContext = () => { if (!context) { throw new Error( - "useCompleteCommonContext must be used within a CompleteCommonContextProvider", + "useCompleteCommonContext must be used within a CompleteCommonContextProvider" ); } diff --git a/packages/widget/src/pages/components/footer-outlet/index.tsx b/packages/widget/src/pages/components/footer-outlet/index.tsx index 5000eecc..b5f26391 100644 --- a/packages/widget/src/pages/components/footer-outlet/index.tsx +++ b/packages/widget/src/pages/components/footer-outlet/index.tsx @@ -51,7 +51,7 @@ const AnimatedFooterButton = (props: NonNullable) => { const { state } = useMountAnimation(); const [initAnimationFinished, setInitAnimationFinished] = useState( - state.layout, + state.layout ); const { disableInitLayoutAnimation } = useSettings(); @@ -86,7 +86,7 @@ const AnimatedFooterButton = (props: NonNullable) => { animate: {}, initial: { opacity: 0, translateY: "-40px" }, }; - }), + }) ) .unsafeCoerce(); diff --git a/packages/widget/src/pages/components/layout/layout-context.tsx b/packages/widget/src/pages/components/layout/layout-context.tsx index 42703ee6..f62cbe8f 100644 --- a/packages/widget/src/pages/components/layout/layout-context.tsx +++ b/packages/widget/src/pages/components/layout/layout-context.tsx @@ -34,12 +34,12 @@ export const CurrentLayoutProvider = ({ children }: PropsWithChildren) => { setState({ pathname, height }); }, - [currentPathnameRef], + [currentPathnameRef] ); const value = useMemo( () => ({ state, setState: _setState }), - [_setState, state], + [_setState, state] ); return ( @@ -54,7 +54,7 @@ export const useCurrentLayout = () => { if (!value) { throw new Error( - "useCurrentLayout must be used within a CurrentLayoutContextProvider", + "useCurrentLayout must be used within a CurrentLayoutContextProvider" ); } diff --git a/packages/widget/src/pages/components/meta-info/index.tsx b/packages/widget/src/pages/components/meta-info/index.tsx index 23e6a5e6..406da3ca 100644 --- a/packages/widget/src/pages/components/meta-info/index.tsx +++ b/packages/widget/src/pages/components/meta-info/index.tsx @@ -54,7 +54,7 @@ export const MetaInfo = ({ { text: lockupPeriod, icon: }, ].filter( (val): val is { text: string | ReactNode; icon: JSX.Element } => - !!val.text, + !!val.text ), [ campaign, @@ -65,7 +65,7 @@ export const MetaInfo = ({ withdrawnTime, extra, lockupPeriod, - ], + ] ); return isLoading ? ( diff --git a/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx b/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx index 00c166ab..393a6000 100644 --- a/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx +++ b/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx @@ -88,7 +88,7 @@ export const ActionListItem = ({ count: Math.max(val.length - 1, 1), })} - )), + )) ) .extractNullable()} @@ -114,7 +114,7 @@ export const ActionListItem = ({ ), - , + )} ); diff --git a/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx b/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx index 51f19939..baf11638 100644 --- a/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx +++ b/packages/widget/src/pages/details/activity-page/components/list-item-bullet/index.tsx @@ -36,8 +36,8 @@ const ListItemBullet = ({ height="3" borderRadius="full" background="tokenSelectBackground" - />, - ), + /> + ) ) .extractNullable(); diff --git a/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts b/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts index 86fe0cc6..aee691ad 100644 --- a/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts +++ b/packages/widget/src/pages/details/activity-page/hooks/use-action-list-item.ts @@ -28,13 +28,13 @@ export const useActionListItem = (action: ActionYieldDto) => { const integrationData = useMemo( () => Maybe.fromNullable(action.yieldData), - [action.yieldData], + [action.yieldData] ); const providersDetails = useProvidersDetails({ integrationData, validatorsAddresses: Maybe.of( - getActionValidatorAddresses(action.actionData) ?? [], + getActionValidatorAddresses(action.actionData) ?? [] ), selectedProviderYieldId: Maybe.empty(), }); @@ -45,7 +45,7 @@ export const useActionListItem = (action: ActionYieldDto) => { .map((t) => t.replaceAll("_", " ")) .map(capitalizeFirstLetters) .extract(), - [action], + [action] ); const actionOlderThan7Days = useMemo( @@ -53,7 +53,7 @@ export const useActionListItem = (action: ActionYieldDto) => { Maybe.of(action.actionData.createdAt) .map(dateOlderThen7Days) .orDefault(false), - [action], + [action] ); const badgeLabel = useMemo( @@ -70,7 +70,7 @@ export const useActionListItem = (action: ActionYieldDto) => { .map((status) => status.replaceAll("_", " ")) .map(capitalizeFirstLetters) .extract(), - [action, t, actionOlderThan7Days], + [action, t, actionOlderThan7Days] ); const badgeColor = useMemo( @@ -87,13 +87,13 @@ export const useActionListItem = (action: ActionYieldDto) => { }) .map((status) => BADGE_BG_MAP[status]) .extract(), - [action, actionOlderThan7Days], + [action, actionOlderThan7Days] ); const amount = useMemo( () => Maybe.fromNullable(action.actionData.amount).map(defaultFormattedNumber), - [action], + [action] ); return { diff --git a/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx b/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx index 8cb109c1..3f52dc33 100644 --- a/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx +++ b/packages/widget/src/pages/details/activity-page/state/activity-page.context.tsx @@ -50,7 +50,7 @@ export const ActivityPageContextProvider = ({ const urls = data.actionData.transactions .map((val) => ({ type: val.type, url: val.explorerUrl })) .filter( - (val): val is { type: TransactionType; url: string } => !!val.url, + (val): val is { type: TransactionType; url: string } => !!val.url ); const path = @@ -90,26 +90,26 @@ export const ActivityPageContextProvider = ({ const actions = useMemo( () => Maybe.fromNullable(activityActions.allItems), - [activityActions.allItems], + [activityActions.allItems] ); const groupedDates = useMemo( () => actions.map((action) => action.map((a) => a.actionData.createdAt)), - [actions], + [actions] ); const [labels, counts] = useMemo( () => groupDateStrings(groupedDates.extract() ?? [], i18n), - [groupedDates, i18n], + [groupedDates, i18n] ); const bulletLines = useMemo( () => counts.reduce( (acc, val) => acc.concat(createSubArray(val)), - [], + [] ), - [counts], + [counts] ); const value = { @@ -132,7 +132,7 @@ export const useActivityPageContext = () => { if (!context) { throw new Error( - "useActivityPageContext must be used within a ActivityPageContext", + "useActivityPageContext must be used within a ActivityPageContext" ); } diff --git a/packages/widget/src/pages/details/details-page/components/tabs.tsx b/packages/widget/src/pages/details/details-page/components/tabs.tsx index b9c58484..9ff6a51e 100644 --- a/packages/widget/src/pages/details/details-page/components/tabs.tsx +++ b/packages/widget/src/pages/details/details-page/components/tabs.tsx @@ -106,7 +106,7 @@ export const AnimatedTabs = (props: TabsProps) => { .map((val) => ({ animate: { ...animateTo, transition: val.transition }, initial: val.initial, - })), + })) ) .unsafeCoerce(); diff --git a/packages/widget/src/pages/details/details-page/details.page.tsx b/packages/widget/src/pages/details/details-page/details.page.tsx index 951e7eed..9a71e50f 100644 --- a/packages/widget/src/pages/details/details-page/details.page.tsx +++ b/packages/widget/src/pages/details/details-page/details.page.tsx @@ -15,7 +15,7 @@ export const Details = () => { return acc; }, 0), - [positionsData.data], + [positionsData.data] ); return ( diff --git a/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx index e7a6299c..e0b4a595 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx @@ -39,7 +39,7 @@ export const SelectProvider = () => { selectedProviderYieldId, yields: Maybe.fromNullable(yields.data), }).chainNullable((val) => - val.yields.find((v) => v.id === val.selectedProviderYieldId), + val.yields.find((v) => v.id === val.selectedProviderYieldId) ); const providerSelection = Maybe.fromRecord({ @@ -51,8 +51,8 @@ export const SelectProvider = () => { (provider) => ({ ...val, provider, - }), - ), + }) + ) ); return appLoading ? ( diff --git a/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx index 36f508f6..1fed6f5d 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-token-section/index.tsx @@ -77,7 +77,7 @@ export const SelectTokenSection = () => { !!showUSDeBanner && isLedgerLive && val.selectedTokenAvailableAmount.amount.isZero() && - isUSDeToken(val.selectedToken), + isUSDeToken(val.selectedToken) ) .isJust(); @@ -130,7 +130,7 @@ export const SelectTokenSection = () => { rec: selectTokenSection, variant, state: submitted && stakeAmountIsZero ? "danger" : "default", - }), + }) )} > {variant === "zerion" && ( @@ -173,7 +173,7 @@ export const SelectTokenSection = () => { combineRecipeWithVariant({ rec: selectTokenBalance, variant, - }), + }) )} > {formattedPrice} @@ -197,7 +197,7 @@ export const SelectTokenSection = () => { combineRecipeWithVariant({ rec: selectTokenBalance, variant, - }), + }) )} > {selectedTokenAvailableAmount @@ -229,7 +229,7 @@ export const SelectTokenSection = () => { )} - ), + ) ) .extractNullable()} diff --git a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx index 6179902f..1fcf3faf 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token-list-item.tsx @@ -25,12 +25,12 @@ export const SelectTokenListItem = memo( const formattedAmount = useMemo( () => defaultFormattedNumber(amount), - [amount], + [amount] ); const amountGreaterThanZero = useMemo( () => amount.isGreaterThan(0), - [amount], + [amount] ); const trackEvent = useTrackEvent(); @@ -80,5 +80,5 @@ export const SelectTokenListItem = memo( ); - }, + } ); diff --git a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx index 1e232e56..c737003e 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-token-section/select-token.tsx @@ -47,7 +47,7 @@ export const SelectToken = () => { tokenBalancesData.map((v) => v.filtered).extract() ?? [], })) .extractNullable(), - [selectedToken, tokenBalancesData], + [selectedToken, tokenBalancesData] ); if (!data) return null; @@ -75,7 +75,7 @@ export const SelectToken = () => { combineRecipeWithVariant({ variant, rec: selectTokenButton, - }), + }) )} > { const multiSelect = isYieldActionArgRequired( val.selectedStake, "enter", - "validatorAddresses", + "validatorAddresses" ); return ( @@ -48,7 +48,7 @@ export const SelectValidatorSection = () => { selectedValidatorsArr={selectedValidatorsArr} multiSelect={multiSelect} isWithProviderOptions={isYieldWithProviderOptions( - val.selectedStake, + val.selectedStake )} /> } diff --git a/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx b/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx index 7e0fe1a1..9557786c 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx @@ -101,7 +101,7 @@ export const SelectValidatorTrigger = ({ {t( sv.status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive", + : "details.validators_inactive" )} diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-opportunity.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-opportunity.tsx index a28f95db..eab66def 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-opportunity.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-opportunity.tsx @@ -47,10 +47,10 @@ export const SelectOpportunity = () => { groups: val.map((v) => v.title), groupCounts: val.map((v) => v.itemsLength), }; - }), + }) ) .extractNullable(), - [selectedStake, selectedStakeData], + [selectedStake, selectedStakeData] ); const { variant } = useSettings(); @@ -73,7 +73,7 @@ export const SelectOpportunity = () => { rec: selectOpportunityButton, variant, }), - pressAnimation, + pressAnimation )} data-testid="select-opportunity" > diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx index 37397c25..21e7b24e 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx @@ -25,16 +25,16 @@ export const SelectYieldRewardDetails = () => { useEarnPageContext(); const rewardRateDetails = selectedStake.chainNullable( - getYieldRewardRateDetails, + getYieldRewardRateDetails ); const earnYearly = estimatedRewards.mapOrDefault( (e) => `${e.yearly} ${rewardsTokenSymbol}`, - "", + "" ); const earnMonthly = estimatedRewards.mapOrDefault( (e) => `${e.monthly} ${rewardsTokenSymbol}`, - "", + "" ); return ( @@ -163,7 +163,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }), + }) )} > {t(variant === "zerion" ? "details.rewards.yearly" : "shared.yearly")} @@ -174,7 +174,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }), + }) )} > {earnYearly} @@ -195,7 +195,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }), + }) )} > {t("shared.monthly")} @@ -206,7 +206,7 @@ const DefaultEarnYearlyOrMonthly = ({ combineRecipeWithVariant({ rec: selectYieldRewardsText, variant, - }), + }) )} > {earnMonthly} diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx index dba429aa..1def972c 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx @@ -23,7 +23,7 @@ export const StakedVia = () => { !isYieldActionArgRequired(val, "enter", "validatorAddress") && !isYieldActionArgRequired(val, "enter", "validatorAddresses") && getYieldProviderDetails(val) - ), + ) ) .chainNullable((val) => getYieldProviderDetails(val)) .map((val) => ( diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts b/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts index 5e78bef6..b9e392ca 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts @@ -4,7 +4,7 @@ import { config } from "../../../../../config"; import type { EarnPageContextType } from "../../state/types"; export const useAnimateYieldPercent = ( - estimatedRewards: EarnPageContextType["estimatedRewards"], + estimatedRewards: EarnPageContextType["estimatedRewards"] ) => { const perReward = estimatedRewards .map((val) => { @@ -33,7 +33,7 @@ export const useAnimateYieldPercent = ( const transformedMotionValue = useTransform( rewardPercMotionValue, - (val) => `${val.toFixed(2)}%`, + (val) => `${val.toFixed(2)}%` ); return typeof perReward === "string" || config.env.isTestMode diff --git a/packages/widget/src/pages/details/earn-page/earn.page.tsx b/packages/widget/src/pages/details/earn-page/earn.page.tsx index 7a3d3600..51b91ae8 100644 --- a/packages/widget/src/pages/details/earn-page/earn.page.tsx +++ b/packages/widget/src/pages/details/earn-page/earn.page.tsx @@ -111,7 +111,7 @@ export const AnimatedEarnPage = () => { .map((val) => ({ animate: { ...animateTo, transition: val.transition }, initial: val.initial, - })), + })) ) .unsafeCoerce(); diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx index 4fa66c66..5b2677c8 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx @@ -74,7 +74,7 @@ import { usePendingActionDeepLink } from "./use-pending-action-deep-link"; import { useStakeEnterRequestDto } from "./use-stake-enter-request-dto"; const EarnPageContext = createContext( - undefined, + undefined ); export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { @@ -109,7 +109,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const yieldType = useYieldType(selectedStake).mapOrDefault( (y) => y.title, - "", + "" ); const initYieldRes = useInitYield({ selectedToken }); @@ -127,7 +127,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { selectedStake .map(getYieldRewardTokens) .map((val) => val.filter((rt) => rt.isPoints)), - [selectedStake], + [selectedStake] ); const pricesState = useTokensPrices({ @@ -159,10 +159,10 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { token: val.selectedToken, prices: val.prices, pricePerShare: null, - }), + }) ) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""), - [baseToken, pricesState.data, selectedToken, stakeAmount], + [baseToken, pricesState.data, selectedToken, stakeAmount] ); const selectedTokenAvailableAmount = useMemo( @@ -173,7 +173,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { fullFormattedAmount: formatNumber(am), amount: am, })), - [availableAmount, symbol], + [availableAmount, symbol] ); const [stakeSearch, setStakeSearch] = useState(""); @@ -184,7 +184,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const deferredValidatorSearch = useDeferredValue(validatorSearch); const multiYields = useStreamMultiYields( - useMemo(() => availableYields.orDefault([]), [availableYields]), + useMemo(() => availableYields.orDefault([]), [availableYields]) ); const tokenBalancesScan = useTokenBalancesScan(); @@ -213,7 +213,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { tbSet: new Set(), tbWithAmount: [] as TokenBalanceScanResponseDto[], tbWithoutAmount: [] as TokenBalanceScanResponseDto[], - }, + } ); return [ @@ -225,34 +225,32 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { .chain((tb) => Maybe.of(deferredTokenSearch) .chain((val) => - val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty(), + val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty() ) .map((lowerSearch) => ({ all: tb, filtered: tb.filter( (t) => t.token.name.toLowerCase().includes(lowerSearch) || - t.token.symbol.toLowerCase().includes(lowerSearch), + t.token.symbol.toLowerCase().includes(lowerSearch) ), })) - .alt(Maybe.of({ all: tb, filtered: tb })), + .alt(Maybe.of({ all: tb, filtered: tb })) ), - [defaultTokens.data, deferredTokenSearch, tokenBalancesScan.data], + [defaultTokens.data, deferredTokenSearch, tokenBalancesScan.data] ); const selectedStakeData = useMemo>( () => Maybe.of(multiYields) .map((val) => - [...val].sort( - (a, b) => getYieldRewardRate(b) - getYieldRewardRate(a), - ), + [...val].sort((a, b) => getYieldRewardRate(b) - getYieldRewardRate(a)) ) .map((val) => val.filter(isNonZeroRewardRateYield)) .chain((yieldDtos) => Maybe.of(deferredStakeSearch) .chain((val) => - val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty(), + val.length >= 1 ? Maybe.of(val.toLowerCase()) : Maybe.empty() ) .map((lowerSearch) => ({ all: yieldDtos, @@ -264,15 +262,15 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { getYieldRewardTokens(d).some( (rt) => rt.name.toLowerCase().includes(lowerSearch) || - rt.symbol.toLowerCase().includes(lowerSearch), - ), + rt.symbol.toLowerCase().includes(lowerSearch) + ) ), })) - .alt(Maybe.of({ all: yieldDtos, filteredDtos: yieldDtos })), + .alt(Maybe.of({ all: yieldDtos, filteredDtos: yieldDtos })) ) .map(({ all, filteredDtos }) => { const sorted = [...filteredDtos].sort( - (a, b) => getYieldTypesSortRank(a) - getYieldTypesSortRank(b), + (a, b) => getYieldTypesSortRank(a) - getYieldTypesSortRank(b) ); const groupsWithCounts = [ @@ -299,7 +297,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { title: ReturnType["title"]; items: Yield[]; } - >(), + >() ) .values(), ].reduce( @@ -313,10 +311,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { return acc; }, - new Map< - ExtendedYieldType, - { itemsLength: number; title: string } - >(), + new Map() ); return { @@ -325,7 +320,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { groupsWithCounts, }; }), - [deferredStakeSearch, multiYields, t], + [deferredStakeSearch, multiYields, t] ); const yieldValidators = useYieldValidators({ @@ -366,7 +361,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { isYieldIntegrationAggregator(stake) || isYieldActionArgRequired(stake, "enter", "validatorAddress") || isYieldActionArgRequired(stake, "enter", "validatorAddresses") - ), + ) ) .orDefault(false); @@ -408,20 +403,20 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { return validators.filter( (validator) => validator.name?.toLowerCase().includes(searchInput) || - validator.address.toLowerCase().includes(searchInput), + validator.address.toLowerCase().includes(searchInput) ); }) .map((validators) => { if (variant === "utila" || variant === "porto") { return [...validators].sort( - (a, b) => (b.apr ?? 0) - (a.apr ?? 0), + (a, b) => (b.apr ?? 0) - (a.apr ?? 0) ); } return validators; - }), + }) ), - [deferredValidatorSearch, selectedStake, variant, yieldValidators.data], + [deferredValidatorSearch, selectedStake, variant, yieldValidators.data] ); const onYieldSearch: SelectModalProps["onSearch"] = (val) => @@ -436,7 +431,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const onTokenBalanceSelect = useCallback( (tokenBalance: TokenBalanceScanResponseDto) => dispatch({ type: "token/select", data: tokenBalance.token }), - [dispatch], + [dispatch] ); const onYieldSelect = (yieldId: string) => { @@ -449,7 +444,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { selectedStake.ifJust((ss) => isYieldActionArgRequired(ss, "enter", "validatorAddresses") ? dispatch({ type: "validator/multiselect", data: item }) - : dispatch({ type: "validator/select", data: item }), + : dispatch({ type: "validator/select", data: item }) ); const onValidatorRemove = (item: ValidatorDto) => @@ -563,7 +558,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { selectedStake .filter(() => maxIntegrationAmount.isJust() && !isForceMax) .map(() => maxEnterOrExitAmount.toNumber()), - [maxEnterOrExitAmount, maxIntegrationAmount, isForceMax, selectedStake], + [maxEnterOrExitAmount, maxIntegrationAmount, isForceMax, selectedStake] ); const stakeMinAmount = useMemo( @@ -572,7 +567,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { .filter(() => minIntegrationAmount.isJust() && !isForceMax) .map(() => minEnterOrExitAmount.toNumber()) .filter((val) => new BigNumber(val).isGreaterThan(0)), - [minEnterOrExitAmount, minIntegrationAmount, isForceMax, selectedStake], + [minEnterOrExitAmount, minIntegrationAmount, isForceMax, selectedStake] ); const onSelectOpportunityClose = () => setStakeSearch(""); @@ -585,7 +580,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const { state } = useMountAnimation(); const yieldOpportunityLoading = useYieldOpportunity( - selectedStake.extract()?.id, + selectedStake.extract()?.id ).isLoading; const appLoading = @@ -608,7 +603,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const buttonCTAText = useYieldType(selectedStake).mapOrDefault( (v) => v.cta, - "", + "" ); const providersDetails = useProvidersDetails({ @@ -649,9 +644,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { stakeToken: val.selectedToken, yieldDto: val.selectedStake, }), - false, + false ), - [selectedStake, selectedToken], + [selectedStake, selectedToken] ); const selectTokenIsLoading = @@ -698,7 +693,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { label: t( isLedgerLiveAccountPlaceholder ? "init.ledger_add_account" - : "init.connect_wallet", + : "init.connect_wallet" ), onClick: () => connectClickRef.current(), }, @@ -714,8 +709,8 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { isFetching, t, hasNotYieldsForToken, - ], - ), + ] + ) ); const value = { diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx index 05c19945..ebe0629b 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-state-context.tsx @@ -29,10 +29,10 @@ import { useTrackStateEvents } from "./use-track-state-events"; import { onYieldSelectState } from "./utils"; const EarnPageStateContext = createContext<(State & ExtraData) | undefined>( - undefined, + undefined ); const EarnPageDispatchContext = createContext | undefined>( - undefined, + undefined ); const EarnPageStateUsageBoundary = createContext(false); @@ -69,7 +69,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { return Maybe.fromFalsy( state.selectedToken .map((v) => !equalTokens(v, action.data)) - .orDefault(true), + .orDefault(true) ) .chain(() => getInitYield({ selectedToken: action.data }) @@ -77,9 +77,9 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { onYieldSelectState({ yieldDto: val, positionsData: positionsData.data, - }), + }) ) - .alt(Maybe.of(null)), + .alt(Maybe.of(null)) ) .map((val) => ({ ...getInitialState(), @@ -91,15 +91,13 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { case "yield/select": { return Maybe.fromFalsy( - state.selectedStakeId - .map((v) => v !== action.data.id) - .orDefault(true), + state.selectedStakeId.map((v) => v !== action.data.id).orDefault(true) ) .map(() => onYieldSelectState({ yieldDto: action.data, positionsData: positionsData.data, - }), + }) ) .map((val) => ({ ...getInitialState(), @@ -194,13 +192,13 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { const initTokenRes = useInitToken(); const initToken = useMemo( () => Maybe.fromNullable(initTokenRes.data), - [initTokenRes.data], + [initTokenRes.data] ); const initYieldRes = useInitYield({ selectedToken }); const initYield = useMemo( () => Maybe.fromNullable(initYieldRes.data), - [initYieldRes.data], + [initYieldRes.data] ); const { availableAmount, availableYields } = useTokenBalance({ @@ -219,7 +217,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { const selectedStake = useMemo( () => Maybe.fromNullable(yieldOpportunity.data), - [yieldOpportunity.data], + [yieldOpportunity.data] ); /** @@ -227,17 +225,17 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { */ const stakeAmount = useMemo( () => (isForceMax ? maxEnterOrExitAmount : _stakeAmount), - [isForceMax, maxEnterOrExitAmount, _stakeAmount], + [isForceMax, maxEnterOrExitAmount, _stakeAmount] ); const setToken = useCallback( (token: TokenDto) => dispatch({ type: "token/select", data: token }), - [], + [] ); const setYield = useCallback( (yieldDto: Yield) => dispatch({ type: "yield/select", data: yieldDto }), - [], + [] ); /** @@ -273,10 +271,10 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { .filter( (it) => isNetworkWithEnterMinBasedOnPosition(it.network as Networks) && - positionsData.isPending, + positionsData.isPending ) .isJust(), - [initToken, positionsData], + [initToken, positionsData] ); /** @@ -315,7 +313,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { */ useEffect(() => { initToken.ifNothing(() => - selectedToken.ifJust(() => dispatch({ type: "state/reset" })), + selectedToken.ifJust(() => dispatch({ type: "state/reset" })) ); }, [initToken, selectedToken]); @@ -326,7 +324,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { onMaxClick: () => dispatch({ type: "stakeAmount/max", data: maxEnterOrExitAmount }), }), - [maxEnterOrExitAmount], + [maxEnterOrExitAmount] ); const { @@ -377,7 +375,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { availableYields, hasNotYieldsForToken, selectedProviderYieldId, - ], + ] ); return ( diff --git a/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts b/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts index 7ec45c98..7554172d 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-amount-validation.ts @@ -18,7 +18,7 @@ export const useAmountValidation = ({ availableAmount .map(() => stakeAmount.isLessThan(minEnterOrExitAmount)) .orDefault(false), - [availableAmount, stakeAmount, minEnterOrExitAmount], + [availableAmount, stakeAmount, minEnterOrExitAmount] ); const stakeAmountGreaterThanMax = useMemo( @@ -26,12 +26,12 @@ export const useAmountValidation = ({ availableAmount .map(() => stakeAmount.isGreaterThan(maxEnterOrExitAmount)) .orDefault(false), - [availableAmount, stakeAmount, maxEnterOrExitAmount], + [availableAmount, stakeAmount, maxEnterOrExitAmount] ); const stakeAmountIsZero = useMemo( () => availableAmount.map(() => stakeAmount.isZero()).orDefault(false), - [stakeAmount, availableAmount], + [stakeAmount, availableAmount] ); const stakeAmountGreaterThanAvailableAmount = useMemo( @@ -39,7 +39,7 @@ export const useAmountValidation = ({ availableAmount .map((val) => stakeAmount.isGreaterThan(val)) .orDefault(false), - [availableAmount, stakeAmount], + [availableAmount, stakeAmount] ); return { diff --git a/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts index 33af9918..96b29523 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts @@ -13,13 +13,13 @@ export const useGetInitYield = () => { return useCallback( ({ selectedToken }: { selectedToken: TokenDto }) => Maybe.fromNullable( - tokenBalancesMap.get(tokenString(selectedToken)), + tokenBalancesMap.get(tokenString(selectedToken)) ).chain((val) => getCachedFirstEligibleYield({ queryClient, yieldIds: val.availableYields, - }), + }) ), - [queryClient, tokenBalancesMap], + [queryClient, tokenBalancesMap] ); }; diff --git a/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts b/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts index b9fc35de..8752cc3d 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-get-token-balances-map.ts @@ -15,8 +15,8 @@ export const useGetTokenBalancesMap = () => new Map([ ...(defaultTokens ?? []).map((v) => [tokenString(v.token), v] as const), ...(tokenBalancesScan ?? []).map( - (v) => [tokenString(v.token), v] as const, + (v) => [tokenString(v.token), v] as const ), ]), - [], + [] ); diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts index 07e237ca..9e46d597 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts @@ -73,12 +73,12 @@ export const useInitToken = () => { defaultTokens: val.defaultTokens, tokenBalances: val.tokenBalancesScan, initQueryParams: Maybe.fromNullable(initParams), - }).toEither(new Error("could not get initial token")), + }).toEither(new Error("could not get initial token")) ).chain((token) => EitherAsync.liftEither( Maybe.fromNullable( - getTokenBalancesMap(val).get(tokenString(token)), - ).toEither(new Error("could not get token balance")), + getTokenBalancesMap(val).get(tokenString(token)) + ).toEither(new Error("could not get token balance")) ) .chain((tokenBalance) => getFirstEligibleYield({ @@ -94,11 +94,11 @@ export const useInitToken = () => { validatorsConfig, preferredTokenYieldsPerNetwork: preferredTokenYieldsPerNetwork ?? null, - }), + }) ) - .map(() => token), - ), - ), + .map(() => token) + ) + ) ) ).unsafeCoerce(), }); diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts index f5ed69ac..b00f3a3f 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts @@ -53,7 +53,7 @@ export const useInitYield = ({ queryFn: async () => ( await EitherAsync.liftEither( - selectedToken.toEither(new Error("no token selected")), + selectedToken.toEither(new Error("no token selected")) ).chain((token) => getTokenBalances({ additionalAddresses, @@ -65,9 +65,9 @@ export const useInitYield = ({ .chain((val) => EitherAsync.liftEither( Maybe.fromNullable( - getTokenBalancesMap(val).get(tokenString(token)), - ).toEither(new Error("could not get token balance")), - ), + getTokenBalancesMap(val).get(tokenString(token)) + ).toEither(new Error("could not get token balance")) + ) ) .chain((val) => getInitParams({ @@ -89,9 +89,9 @@ export const useInitYield = ({ validatorsConfig, preferredTokenYieldsPerNetwork: preferredTokenYieldsPerNetwork ?? null, - }), - ), - ), + }) + ) + ) ) ).unsafeCoerce(), }); 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 a0b549be..2971ac19 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 @@ -39,8 +39,8 @@ export const usePendingActionDeepLink = () => { ( await EitherAsync.liftEither( Maybe.fromNullable(address).toEither( - new Error("missing wagmi config"), - ), + new Error("missing wagmi config") + ) ).chain((addr) => fn({ isLedgerLive, @@ -49,7 +49,7 @@ export const usePendingActionDeepLink = () => { queryClient, yieldApiFetchClient, externalProviders, - }), + }) ) ).unsafeCoerce(), }); @@ -79,11 +79,11 @@ const fn = ({ const initQueryParams = Maybe.of(val) .filter( ( - v, + v ): v is Override< typeof val, { yieldId: string; pendingaction: string } - > => !!v.yieldId && !!v.pendingaction, + > => !!v.yieldId && !!v.pendingaction ) .toEither(new Error("missing yieldId or pendingaction")); @@ -101,14 +101,14 @@ const fn = ({ body: { address, }, - }), + }) ) .chain((response) => EitherAsync.liftEither( Maybe.fromNullable(response.data).toEither( - new Error("could not get yield balances"), - ), - ), + new Error("could not get yield balances") + ) + ) ) .map((val) => ({ yieldId: initQueryParams.yieldId, @@ -117,7 +117,7 @@ const fn = ({ singleYieldBalances: val.balances, address: address, additionalAddresses: additionalAddresses ?? undefined, - })), + })) ) .chain((data) => EitherAsync.liftEither( @@ -127,14 +127,14 @@ const fn = ({ data.validatorAddress && balance.validator?.address !== data.validatorAddress && !balance.validators?.some( - (validator) => validator.address === data.validatorAddress, + (validator) => validator.address === data.validatorAddress ) ) { continue; } const pendingAction = balance.pendingActions.find( - (pa) => pa.type === data.pendingaction, + (pa) => pa.type === data.pendingaction ); if (pendingAction) { @@ -147,7 +147,7 @@ const fn = ({ } return Left(new Error("no pending action found")); - }), + }) ) .chain((val) => getYieldOpportunity({ @@ -155,7 +155,7 @@ const fn = ({ yieldId: data.yieldId, queryClient, yieldApiFetchClient, - }).map((yieldOp) => ({ ...val, yieldOp })), + }).map((yieldOp) => ({ ...val, yieldOp })) ) .chain< Error, @@ -179,7 +179,7 @@ const fn = ({ PAMultiValidatorsRequired(val.pendingAction) || PASingleValidatorRequired(val.pendingAction) ? EitherAsync.liftEither( - Right({ type: "positionDetails", ...val }), + Right({ type: "positionDetails", ...val }) ) : EitherAsync.liftEither( preparePendingActionRequestDto({ @@ -191,14 +191,14 @@ const fn = ({ yieldBalance: val.balance, pendingActionDto: val.pendingAction, selectedValidators: [], - }), + }) ).map((res) => ({ yieldOp: val.yieldOp, pendingActionDto: res, type: "review", balanceId: val.balanceId, balance: val.balance, - })), - ), + })) + ) ); }); diff --git a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts index 0228fb85..9dcb1b47 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts @@ -68,7 +68,7 @@ export const useStakeEnterRequestDto = () => { const subnetIdRequired = !!getYieldActionArg( selectedStake, "enter", - "subnetId", + "subnetId" )?.required; return List.head(validators).map((v) => ({ @@ -115,6 +115,6 @@ export const useStakeEnterRequestDto = () => { useMaxAmount, tronResource, selectedProviderYieldId, - ], + ] ); }; diff --git a/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts b/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts index 2737c7b5..bc183714 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-token-balance.ts @@ -15,19 +15,19 @@ export const useTokenBalance = ({ const tokenBalance = useMemo( () => selectedToken.chainNullable((val) => - tokenBalancesMap.get(tokenString(val)), + tokenBalancesMap.get(tokenString(val)) ), - [selectedToken, tokenBalancesMap], + [selectedToken, tokenBalancesMap] ); const availableAmount = useMemo( () => tokenBalance.map((v) => new BigNumber(v.amount)), - [tokenBalance], + [tokenBalance] ); const availableYields = useMemo( () => tokenBalance.map((v) => v.availableYields), - [tokenBalance], + [tokenBalance] ); return { diff --git a/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts b/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts index 3aa9a4bd..c72f161f 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-track-state-events.ts @@ -24,7 +24,7 @@ export const useTrackStateEvents = ({ tokenName: v.token.name, tokenAddress: v.token.address, tokenSymbol: v.token.symbol, - }), + }) ); }, [initYield, trackEvent]); @@ -39,7 +39,7 @@ export const useTrackStateEvents = ({ address: v.address, symbol: v.symbol, decimals: v.decimals, - }), + }) ); }, [initToken, trackEvent]); }; diff --git a/packages/widget/src/pages/details/earn-page/state/utils.ts b/packages/widget/src/pages/details/earn-page/state/utils.ts index 73150a9d..c874a776 100644 --- a/packages/widget/src/pages/details/earn-page/state/utils.ts +++ b/packages/widget/src/pages/details/earn-page/state/utils.ts @@ -22,10 +22,10 @@ export const onYieldSelectState = ({ stakeAmount: getMinStakeAmount(yieldDto, positionsData), selectedValidators: new Map(), tronResource: Maybe.fromFalsy( - getYieldActionArg(yieldDto, "enter", "tronResource")?.required, + getYieldActionArg(yieldDto, "enter", "tronResource")?.required ).map(() => "ENERGY"), selectedProviderYieldId: Maybe.fromNullable( - getYieldActionArg(yieldDto, "enter", "providerId"), + getYieldActionArg(yieldDto, "enter", "providerId") ) .filter((val) => !!val.required && !!val.options?.length) .chain((val) => List.head(val.options ?? [])), 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 0ddd6296..7b8c780a 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 @@ -62,7 +62,7 @@ export const PositionsListItem = memo( ), - , + )} - | undefined, + | undefined )} > {t( - `position_details.labels.${label.type as PositionDetailsLabelType}.label`, + `position_details.labels.${label.type as PositionDetailsLabelType}.label` )} @@ -131,7 +131,7 @@ export const PositionsListItem = memo( ? inactiveValidator === "jailed" ? "details.validators_jailed" : "details.validators_inactive" - : "positions.claim_rewards", + : "positions.claim_rewards" )} @@ -152,7 +152,7 @@ export const PositionsListItem = memo( count: Math.max(val.length - 1, 1), })} - )), + )) ) .extractNullable()} @@ -220,10 +220,10 @@ export const PositionsListItem = memo( )} ), - , + )} ); - }, + } ); 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 e38ee33c..46fcccf0 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 @@ -11,18 +11,18 @@ import { getRewardRateFormatted } from "../../../../utils/formatters"; import type { usePositions } from "./use-positions"; export const usePositionListItem = ( - item: ReturnType["positionsData"]["data"][number], + item: ReturnType["positionsData"]["data"][number] ) => { const yieldOpportunity = useYieldOpportunity(item.integrationId); const integrationData = useMemo( () => Maybe.fromNullable(yieldOpportunity.data), - [yieldOpportunity.data], + [yieldOpportunity.data] ); const providersDetails = useProvidersDetails({ integrationData, validatorsAddresses: Maybe.of( - item.type === "validators" ? item.validatorsAddresses : [], + item.type === "validators" ? item.validatorsAddresses : [] ), selectedProviderYieldId: Maybe.empty(), }); @@ -35,7 +35,7 @@ export const usePositionListItem = ( rewardRateAverage: val.providersDetails .reduce( (acc, val) => acc.plus(new BigNumber(val.rewardRate || 0)), - new BigNumber(0), + new BigNumber(0) ) .dividedBy(val.providersDetails.length), })) @@ -43,9 +43,9 @@ export const usePositionListItem = ( getRewardRateFormatted({ rewardRate: val.rewardRateAverage.toNumber(), rewardType: getYieldRewardType(val.integrationData), - }), + }) ), - [integrationData, providersDetails], + [integrationData, providersDetails] ); const inactiveValidator = useMemo( @@ -55,31 +55,31 @@ export const usePositionListItem = ( .chainNullable((val) => val.status) .map((v) => v as Exclude) .extractNullable(), - [providersDetails], + [providersDetails] ); const tokenToDisplay = item.token; const baseToken = useMemo( () => integrationData.map((y) => getBaseToken(y)), - [integrationData], + [integrationData] ); const amounts = useMemo( () => baseToken.map((b) => getPositionTotalAmount(item.balancesWithAmount, b)), - [item.balancesWithAmount, baseToken], + [item.balancesWithAmount, baseToken] ); const totalAmount = useMemo(() => amounts.map((v) => v.amount), [amounts]); const totalAmountUsd = useMemo( () => amounts.map((v) => v.amountUsd), - [amounts], + [amounts] ); const totalAmountFormatted = useMemo( () => totalAmount.map(defaultFormattedNumber), - [totalAmount], + [totalAmount] ); return { 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 befb08f9..4a7ead3f 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 @@ -22,7 +22,7 @@ export const usePositions = () => { positionsData: _positionsData.data, variant, }), - [_positionsData.data, variant], + [_positionsData.data, variant] ); const positionsData = { ..._positionsData, data: positionsDataMapped }; @@ -36,7 +36,7 @@ export const usePositions = () => { const listData = useMemo( () => ["header" as const, ...positionsData.data], - [positionsData.data], + [positionsData.data] ); return { @@ -64,8 +64,8 @@ const positionsTableDataSelector = createSelector( value.balances.filter((v) => Just(new BigNumber(v.amount)) .filter((v) => !v.isZero() && !v.isNaN()) - .isJust(), - ), + .isJust() + ) ) .filter((v) => !!v.length) .ifJust((v) => @@ -80,11 +80,11 @@ const positionsTableDataSelector = createSelector( List.sort( (a, b) => compare(priorityOrder[a.type], priorityOrder[b.type]), - value.balances, - ), + value.balances + ) ).map((v) => v.token), actionRequired: v.some( - (b) => b.type === "locked" || b.type === "claimable", + (b) => b.type === "locked" || b.type === "claimable" ), pointsRewardTokenBalances: v .filter((v) => !!v.token.isPoints) @@ -94,10 +94,10 @@ const positionsTableDataSelector = createSelector( })), hasPendingClaimRewards: v.some((balance) => balance.pendingActions.some( - (action) => action.type === "CLAIM_REWARDS", - ), + (action) => action.type === "CLAIM_REWARDS" + ) ), - }), + }) ); }); @@ -116,8 +116,8 @@ const positionsTableDataSelector = createSelector( } & ( | { type: "validators"; validatorsAddresses: string[] } | { type: "default" } - ))[], - ), + ))[] + ) ) .map((val) => variant === "zerion" @@ -126,9 +126,9 @@ const positionsTableDataSelector = createSelector( if (b.hasPendingClaimRewards) return 1; return 0; }) - : val, + : val ) - .unsafeCoerce(), + .unsafeCoerce() ); const priorityOrder: Record = { 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 3605f234..b311bf66 100644 --- a/packages/widget/src/pages/position-details/components/amount-block.tsx +++ b/packages/widget/src/pages/position-details/components/amount-block.tsx @@ -66,7 +66,7 @@ export const AmountBlock = ({ const minMaxUnstakeAmount = Maybe.fromPredicate( (v) => v.variant === "unstake", - rest as Extract, + rest as Extract ) .map( (val) => @@ -74,17 +74,17 @@ export const AmountBlock = ({ val.unstakeMinAmount .map( (v) => - `${t("shared.min")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}`, + `${t("shared.min")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}` ) .extractNullable(), val.unstakeMaxAmount .map( (v) => - `${t("shared.max")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}`, + `${t("shared.max")} ${formatNumber(new BigNumber(v))} ${val.unstakeToken.symbol}` ) .extractNullable(), val.unstakeIsGreaterOrLessIntegrationLimitError, - ] as const, + ] as const ) .filter((val) => val.some(Boolean)) .map(([min, max, error]) => ( @@ -257,6 +257,6 @@ const UnstakeInfo = ({ )) .extractNullable(), - [withdrawnTime, withdrawnNotAvailable, positionLocked], + [withdrawnTime, withdrawnNotAvailable, positionLocked] ); }; diff --git a/packages/widget/src/pages/position-details/components/provider-details.tsx b/packages/widget/src/pages/position-details/components/provider-details.tsx index c572723b..d0d42489 100644 --- a/packages/widget/src/pages/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages/position-details/components/provider-details.tsx @@ -97,7 +97,7 @@ export const ProviderDetails = ({ {t( providerDetails.status === "jailed" ? "details.validators_jailed" - : "details.validators_inactive", + : "details.validators_inactive" )} @@ -136,7 +136,7 @@ const ValidatorMeta = memo((props: Parameters[0]) => { {Object.entries(metaInfo) .filter( (val): val is [keyof typeof metaInfo, NonNullable<(typeof val)[1]>] => - !!val[1], + !!val[1] ) .map(([key, val]) => { return ( 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 c4807983..7579b65f 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 @@ -58,7 +58,7 @@ export const StaticActionBlock = ({ context: isEthenaUsdeStaking(yieldId) ? "ethena_usde" : undefined, - }, + } ), }} components={{ @@ -91,7 +91,7 @@ export const StaticActionBlock = ({ {t( `position_details.pending_action_button.${ pendingActionDto.type.toLowerCase() as Lowercase - }`, + }` )} 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 ab62e20a..f5cfbcce 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 @@ -56,7 +56,7 @@ export const usePendingActions = () => { balance.pendingActions.map((pa) => { const amount = Maybe.fromPredicate( (v) => v, - isPendingActionAmountRequired(pa), + isPendingActionAmountRequired(pa) ).chain(() => Maybe.fromNullable( pendingActionsState.get( @@ -64,9 +64,9 @@ export const usePendingActions = () => { balanceType: balance.type, token: balance.token, actionType: pa.type, - }), - ), - ).altLazy(() => Maybe.of(new BigNumber(0))), + }) + ) + ).altLazy(() => Maybe.of(new BigNumber(0))) ); const formattedAmount = Maybe.fromRecord({ @@ -82,7 +82,7 @@ export const usePendingActions = () => { prices: val.prices, pricePerShare: null, baseToken: val.baseToken, - }), + }) ) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""); @@ -92,9 +92,9 @@ export const usePendingActions = () => { pendingActionDto: pa, yieldBalance: balance, }; - }), - ), - ), + }) + ) + ) ), [ pendingActionsState, @@ -102,11 +102,11 @@ export const usePendingActions = () => { positionBalancesByType, reducedStakedOrLiquidBalance, baseToken, - ], + ] ); const onPendingActionAmountChange = ( - data: PendingActionAmountChange["data"], + data: PendingActionAmountChange["data"] ) => { pendingActionDispatch({ type: "pendingAction/amount/change", data }); }; @@ -134,9 +134,9 @@ export const usePendingActions = () => { PAMultiValidatorsRequired(p.pendingActionDto) || PASingleValidatorRequired(p.pendingActionDto) ), - pa, - ), - ), + pa + ) + ) ) .ifJust((val) => { selectValidatorModalShown.current = true; @@ -176,7 +176,7 @@ export const usePendingActions = () => { pendingActionDto, yieldBalance, selectedValidators: [], - }), + }) ); }; @@ -186,7 +186,7 @@ export const usePendingActions = () => { .chain((val) => { if (!validatorAddressesHandling.showValidatorsModal) { return Left( - new Error("missing validatorAddressesHandling.showValidatorsModal"), + new Error("missing validatorAddressesHandling.showValidatorsModal") ); } if (!selectedValidators.length) { @@ -218,7 +218,7 @@ export const usePendingActions = () => { yieldBalance, selectedValidators, }); - }, + } ); }; 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 9ad6eeb5..721ea156 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 @@ -48,7 +48,7 @@ export const usePositionDetails = () => { .chainNullable((val) => getYieldActionArg(val, "exit", "amount")) .filter((val) => !isForceMaxAmount(val)) .chainNullable((val) => val.maximum), - [integrationData], + [integrationData] ); const unstakeMinAmount = useMemo( @@ -58,7 +58,7 @@ export const usePositionDetails = () => { .filter((val) => !isForceMaxAmount(val)) .map(() => minUnstakeAmount.toNumber()) .filter((val) => new BigNumber(val).isGreaterThan(0)), - [integrationData, minUnstakeAmount], + [integrationData, minUnstakeAmount] ); const onClickHandler = useMutation({ @@ -122,7 +122,7 @@ export const usePositionDetails = () => { positionBalances.data .map((balanceData) => balanceData.rewardRate) .extractNullable(), - [positionBalances.data], + [positionBalances.data] ); const canUnstake = integrationData.filter((d) => !!d.status.exit).isJust(); @@ -135,7 +135,7 @@ export const usePositionDetails = () => { reducedStakedOrLiquidBalance .map((val) => val.amountUsd) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""), - [reducedStakedOrLiquidBalance], + [reducedStakedOrLiquidBalance] ); const onMaxClick = () => { @@ -148,7 +148,7 @@ export const usePositionDetails = () => { const unstakeAvailable = integrationData.mapOrDefault( (d) => d.status.exit, - false, + false ); const { @@ -172,21 +172,21 @@ export const usePositionDetails = () => { (yb) => !yb.token.isPoints && !!yb.validator?.pricePerShare && - !equalTokens(yb.token, v.baseToken), + !equalTokens(yb.token, v.baseToken) ) .forEach((yb) => { acc.set( yb.token.symbol, `1 ${yb.token.symbol} = ${defaultFormattedNumber( - new BigNumber(yb.validator?.pricePerShare ?? 0), - )} ${v.baseToken.symbol}`, + new BigNumber(yb.validator?.pricePerShare ?? 0) + )} ${v.baseToken.symbol}` ); }); return acc; - }, new Map()), + }, new Map()) ), - [integrationData, positionBalancesByType, baseToken], + [integrationData, positionBalancesByType, baseToken] ); const unstakeDisabled = yieldOpportunity.isLoading || !unstakeAvailable; 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 42d7b5a0..ca0fe8f8 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 @@ -47,7 +47,7 @@ export const useStakeExitRequestDto = () => { if (isYieldIntegrationAggregator(val.integrationData)) { return List.find( (b) => !!b.validator?.providerId, - val.stakedOrLiquidBalances, + val.stakedOrLiquidBalances ).map((b) => ({ providerId: b.validator?.providerId, validatorAddress: b.validator?.address, @@ -57,12 +57,12 @@ export const useStakeExitRequestDto = () => { getYieldActionArg( val.integrationData, "exit", - "validatorAddresses", + "validatorAddresses" )?.required ) { return List.find( (b) => !!b.validators?.length, - val.stakedOrLiquidBalances, + val.stakedOrLiquidBalances ).map((b) => ({ validatorAddresses: b.validators?.map((validator) => validator.address) ?? [], @@ -74,11 +74,11 @@ export const useStakeExitRequestDto = () => { ) { return List.find( (b) => !!b.validator?.address, - val.stakedOrLiquidBalances, + val.stakedOrLiquidBalances ).map((b) => { const subnetId = Maybe.fromNullable( getYieldActionArg(val.integrationData, "exit", "subnetId") - ?.required, + ?.required ) .chainNullable(() => b.validator) .map((validator) => validator.subnetId) @@ -122,6 +122,6 @@ export const useStakeExitRequestDto = () => { integrationData, unstakeAmount, unstakeUseMaxAmount, - ], + ] ); }; diff --git a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts index bba8f966..0e8a18c0 100644 --- a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts +++ b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts @@ -1,10 +1,10 @@ -import { transactionGetTransactionVerificationMessageForNetwork } from "@stakekit/api-hooks"; import { useMachine } from "@xstate/react"; import type { SnapshotFromStore } from "@xstate/store"; import { useSelector } from "@xstate/store/react"; import { EitherAsync, Maybe } from "purify-ts"; import { type RefObject, useState } from "react"; import { assign, setup } from "xstate"; +import { transactionGetTransactionVerificationMessageForNetwork } from "../../../common/private-api"; import { getValidStakeSessionTx } from "../../../domain"; import type { TransactionVerificationMessageDto } from "../../../domain/types/transaction"; import type { SKWallet } from "../../../domain/types/wallet"; @@ -22,7 +22,7 @@ export const useUnstakeMachine = ({ onDone }: { onDone: () => void }) => { const exitStore = useExitStakeStore(); const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const yieldApiFetchClient = useYieldApiFetchClient(); @@ -34,7 +34,6 @@ export const useUnstakeMachine = ({ onDone }: { onDone: () => void }) => { exitStore, yieldApiFetchClient, signMessage, - transactionGetTransactionVerificationMessageForNetwork, getData: () => Maybe.fromRecord({ network: Maybe.fromNullable(network), @@ -74,7 +73,7 @@ const getMachine = ( } >; }> - >, + > ) => setup({ types: { @@ -174,7 +173,7 @@ const getMachine = ( loading: { entry: ({ self, context }) => EitherAsync.liftEither( - context.data.toEither(new Error("Missing init values")), + context.data.toEither(new Error("Missing init values")) ) .chain((val) => EitherAsync(() => @@ -186,11 +185,11 @@ const getMachine = ( additionalAddresses: val.addresses.additionalAddresses ?? undefined, }, - }, - ), + } + ) ).mapLeft( - () => new Error("Failed to get verification message"), - ), + () => new Error("Failed to get verification message") + ) ) .caseOf({ Right(v) { @@ -242,8 +241,8 @@ const getMachine = ( entry: ({ self, context }) => EitherAsync.liftEither( context.transactionVerificationMessageDto.toEither( - new Error("Missing transaction verification message"), - ), + new Error("Missing transaction verification message") + ) ) .chain((val) => ref.current.signMessage(val.message)) .caseOf({ @@ -306,31 +305,29 @@ const getMachine = ( }, } as typeof val.requestDto.arguments, }, - }) as typeof val.data, + }) as typeof val.data ) - .orDefault(val), + .orDefault(val) ) - .toEither(new Error("Missing params")), + .toEither(new Error("Missing params")) ) .chain((val) => EitherAsync(() => createExitAction({ - addresses: val.addresses, fetchClient: ref.current.yieldApiFetchClient, requestDto: val.requestDto, - yieldDto: val.integrationData, - }), + }) ) .mapLeft(() => new Error("Stake exit error")) .chain((actionDto) => - EitherAsync.liftEither(getValidStakeSessionTx(actionDto)), + EitherAsync.liftEither(getValidStakeSessionTx(actionDto)) ) .ifRight((result) => ref.current.exitStore.send({ type: "setActionDto", data: result, - }), - ), + }) + ) ) .caseOf({ Right() { 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 4d0b2a7c..5d749125 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 @@ -93,7 +93,7 @@ const reducer = (state: State, action: Actions): State => { return { ...state, multiSelect: isPendingActionValidatorAddressesRequired( - action.data.pendingActionDto, + action.data.pendingActionDto ), selectedValidators: newSelectedValidators, pendingActionDto: action.data.pendingActionDto, @@ -120,7 +120,7 @@ export const useValidatorAddressesHandling = () => { const closeModal = useCallback( () => dispatch({ type: "validator/close" }), - [], + [] ); const openModal = useCallback( @@ -128,13 +128,13 @@ export const useValidatorAddressesHandling = () => { yieldBalance: YieldBalanceDto; pendingActionDto: YieldPendingActionDto; }) => dispatch({ type: "validator/open", data: args }), - [], + [] ); const onItemClick = useCallback( (validator: ValidatorDto["address"]) => dispatch({ type: "validator/multiselect", data: validator }), - [], + [] ); const modalState: SelectModalProps["state"] = useMemo( @@ -142,7 +142,7 @@ export const useValidatorAddressesHandling = () => { isOpen: state.showValidatorsModal, setOpen: (value) => !value && closeModal(), }), - [closeModal, state.showValidatorsModal], + [closeModal, state.showValidatorsModal] ); return { diff --git a/packages/widget/src/pages/position-details/hooks/utils.ts b/packages/widget/src/pages/position-details/hooks/utils.ts index 3140de40..8651058a 100644 --- a/packages/widget/src/pages/position-details/hooks/utils.ts +++ b/packages/widget/src/pages/position-details/hooks/utils.ts @@ -59,7 +59,7 @@ export const preparePendingActionRequestDto = ({ const args: NonNullable = { amount: Maybe.fromPredicate( Boolean, - isPendingActionAmountRequired(pendingActionDto), + isPendingActionAmountRequired(pendingActionDto) ) .chainNullable(() => pendingActionsState.get( @@ -67,8 +67,8 @@ export const preparePendingActionRequestDto = ({ balanceType: yieldBalance.type as YieldBalanceDto["type"], token: yieldBalance.token, actionType: pendingActionDto.type as YieldPendingActionType, - }), - ), + }) + ) ) .map((v) => v.toString()) .alt(Maybe.of(yieldBalance.amount)) 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 add14972..18becaff 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -135,7 +135,7 @@ const PositionDetails = () => { {getRewardRateFormatted({ rewardRate: personalizedRewardRate.total, rewardType: getRewardTypeFromRateType( - personalizedRewardRate.rateType, + personalizedRewardRate.rateType ), })} @@ -164,11 +164,11 @@ const PositionDetails = () => { personalizedRewardRate ? undefined : p.rewardType } stakeType={t( - `position_details.stake_type.${getYieldMetadata(integrationData).type}`, + `position_details.stake_type.${getYieldMetadata(integrationData).type}` )} integrationData={integrationData} /> - )), + )) ) .extractNullable()} @@ -182,7 +182,7 @@ const PositionDetails = () => { integrationData={integrationData} yieldBalance={yb} /> - )), + )) )} {liquidTokensToNativeConversion @@ -241,7 +241,7 @@ const PositionDetails = () => { label={t( `position_details.pending_action_button.${ val.pendingActionDto.type.toLowerCase() as Lowercase - }`, + }` )} onMaxClick={null} formattedAmount={val.formattedAmount} @@ -254,8 +254,8 @@ const PositionDetails = () => { onPendingActionClick={onPendingActionClick} yieldId={integrationData.id} /> - ), - ), + ) + ) ) .extractNullable()} {/* Unstake */} @@ -287,14 +287,14 @@ const PositionDetails = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${getYieldMetadata(integrationData).type}`, + `position_details.unstake_label.${getYieldMetadata(integrationData).type}` )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} yieldDto={integrationData} validators={providersDetails.orDefault([])} /> - ), + ) ) .extractNullable()} diff --git a/packages/widget/src/pages/position-details/state/index.tsx b/packages/widget/src/pages/position-details/state/index.tsx index 9363ced9..6c567e5b 100644 --- a/packages/widget/src/pages/position-details/state/index.tsx +++ b/packages/widget/src/pages/position-details/state/index.tsx @@ -54,7 +54,7 @@ export const UnstakeOrPendingActionProvider = ({ const integrationData = useMemo( () => Maybe.fromNullable(yieldOpportunity.data), - [yieldOpportunity.data], + [yieldOpportunity.data] ); const baseToken = useBaseToken(integrationData); @@ -96,8 +96,8 @@ export const UnstakeOrPendingActionProvider = ({ ], })) .extractNullable(), - [positionBalances, baseToken], - ), + [positionBalances, baseToken] + ) ); /** @@ -108,7 +108,7 @@ export const UnstakeOrPendingActionProvider = ({ }); const stakedOrLiquidBalances = useStakedOrLiquidBalance( - positionBalancesByType, + positionBalancesByType ); const reducedStakedOrLiquidBalance = useMemo( @@ -118,7 +118,7 @@ export const UnstakeOrPendingActionProvider = ({ (acc, next) => { acc.amount = acc.amount.plus(new BigNumber(next.amount)); acc.amountUsd = acc.amountUsd.plus( - new BigNumber(next.amountUsd ?? 0), + new BigNumber(next.amountUsd ?? 0) ); acc.token = next.token; @@ -128,10 +128,10 @@ export const UnstakeOrPendingActionProvider = ({ amountUsd: new BigNumber(0), amount: new BigNumber(0), token: b[0].token, - }, - ), + } + ) ), - [stakedOrLiquidBalances], + [stakedOrLiquidBalances] ); const unstakeToken = useMemo( @@ -139,7 +139,7 @@ export const UnstakeOrPendingActionProvider = ({ stakedOrLiquidBalances .chain((balances) => List.head(balances)) .map((v) => v.token), - [stakedOrLiquidBalances], + [stakedOrLiquidBalances] ); const { @@ -159,7 +159,7 @@ export const UnstakeOrPendingActionProvider = ({ !!( !isForceMax && (getYieldActionArg(d, "exit", "amount")?.required || isERC4626(d)) - ), + ) ); const positionBalancesByTypePendingActions = useMemo( @@ -181,14 +181,14 @@ export const UnstakeOrPendingActionProvider = ({ actionType: p.type, }), { pendingAction: p, balance: b }, - ] as const, - ), - ), - ), + ] as const + ) + ) + ) ) - .orDefault([]), + .orDefault([]) ), - [positionBalancesByType], + [positionBalancesByType] ); const getCorrectPendingActionAmount = ({ @@ -207,14 +207,14 @@ export const UnstakeOrPendingActionProvider = ({ const key = getBalanceTokenActionType({ actionType, balanceType, token }); return Maybe.fromNullable( - positionBalancesByTypePendingActions.get(key), + positionBalancesByTypePendingActions.get(key) ).mapOrDefault((val) => { const newMap = new Map(state); newMap.set(key, amount); const amountConfig = getPendingActionAmountConfig(val.pendingAction); const max = new BigNumber( - amountConfig?.maximum ?? Number.POSITIVE_INFINITY, + amountConfig?.maximum ?? Number.POSITIVE_INFINITY ); const min = new BigNumber(amountConfig?.minimum ?? 0); @@ -297,7 +297,7 @@ export const UnstakeOrPendingActionProvider = ({ canChangeUnstakeAmount, isForceMax, reducedStakedOrLiquidBalance, - ], + ] ); const unstakeAmountValid = useMemo( @@ -305,17 +305,17 @@ export const UnstakeOrPendingActionProvider = ({ unstakeAmount.isGreaterThanOrEqualTo(minEnterOrExitAmount) && unstakeAmount.isLessThanOrEqualTo(maxEnterOrExitAmount) && !unstakeAmount.isZero(), - [maxEnterOrExitAmount, minEnterOrExitAmount, unstakeAmount], + [maxEnterOrExitAmount, minEnterOrExitAmount, unstakeAmount] ); const unstakeIsGreaterThanMax = useMemo( () => unstakeAmount.isGreaterThan(maxEnterOrExitAmount), - [unstakeAmount, maxEnterOrExitAmount], + [unstakeAmount, maxEnterOrExitAmount] ); const unstakeIsLessThanMin = useMemo( () => unstakeAmount.isLessThan(minEnterOrExitAmount), - [unstakeAmount, minEnterOrExitAmount], + [unstakeAmount, minEnterOrExitAmount] ); const unstakeIsGreaterOrLessIntegrationLimitError = useMemo( @@ -323,7 +323,7 @@ export const UnstakeOrPendingActionProvider = ({ maxIntegrationAmount .map((v) => unstakeAmount.isGreaterThan(v)) .orDefault(false) || unstakeIsLessThanMin, - [unstakeAmount, unstakeIsLessThanMin, maxIntegrationAmount], + [unstakeAmount, unstakeIsLessThanMin, maxIntegrationAmount] ); const unstakeAmountError = useMemo( @@ -336,7 +336,7 @@ export const UnstakeOrPendingActionProvider = ({ unstakeIsLessThanMin, unstakeIsGreaterThanMax, unstakeIsGreaterOrLessIntegrationLimitError, - ], + ] ); const value: State & ExtraData = useMemo( @@ -377,7 +377,7 @@ export const UnstakeOrPendingActionProvider = ({ pendingActionType, unstakeIsGreaterOrLessIntegrationLimitError, minEnterOrExitAmount, - ], + ] ); return ( @@ -393,7 +393,7 @@ export const useUnstakeOrPendingActionState = () => { const state = useContext(UnstakeOrPendingActionContext); if (state === undefined) { throw new Error( - "useUnstakeOrPendingActionState must be used within a UnstakeContextProvider", + "useUnstakeOrPendingActionState must be used within a UnstakeContextProvider" ); } @@ -404,7 +404,7 @@ export const useUnstakeOrPendingActionDispatch = () => { const dispatch = useContext(UnstakeOrPendingActionDispatchContext); if (dispatch === undefined) { throw new Error( - "useUnstakeOrPendingActionDispatch must be used within a UnstakeContextProvider", + "useUnstakeOrPendingActionDispatch must be used within a UnstakeContextProvider" ); } diff --git a/packages/widget/src/pages/review/hooks/use-action-review.hook.ts b/packages/widget/src/pages/review/hooks/use-action-review.hook.ts index 77a49004..3878914e 100644 --- a/packages/widget/src/pages/review/hooks/use-action-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-action-review.hook.ts @@ -29,12 +29,12 @@ export const useActionReview = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction, + (state) => state.context.selectedAction ).unsafeCoerce(); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield, + (state) => state.context.selectedYield ).unsafeCoerce(); const inputToken = useMemo( @@ -43,9 +43,9 @@ export const useActionReview = () => { getActionInputToken({ actionDto: selectedAction, yieldDto: selectedYield, - }), + }) ), - [selectedAction, selectedYield], + [selectedAction, selectedYield] ) as Maybe; const transactions = useMemo( @@ -53,9 +53,9 @@ export const useActionReview = () => { Maybe.fromNullable(selectedAction) .map((a) => a.transactions) .map((tx) => - tx.sort((a, b) => (a.stepIndex ?? 0) - (b.stepIndex ?? 0)), + tx.sort((a, b) => (a.stepIndex ?? 0) - (b.stepIndex ?? 0)) ), - [selectedAction], + [selectedAction] ); const onViewTransactionClick = (url: string) => @@ -65,7 +65,7 @@ export const useActionReview = () => { const stakeTitle = useYieldType(Maybe.of(selectedYield)).mapOrDefault( (y) => y.review, - "", + "" ); const unstakeTitle = useMemo(() => { @@ -84,9 +84,9 @@ export const useActionReview = () => { t( `position_details.pending_action_button.${ selectedAction.type.toLowerCase() as Lowercase - }` as never, + }` as never ) as string, - [selectedAction.type, t], + [selectedAction.type, t] ); const title = useMemo( @@ -96,7 +96,7 @@ export const useActionReview = () => { : selectedAction.type === ActionTypes.UNSTAKE ? unstakeTitle : pendingActionTitle, - [selectedAction, stakeTitle, unstakeTitle, pendingActionTitle], + [selectedAction, stakeTitle, unstakeTitle, pendingActionTitle] ); const amount = useMemo( @@ -104,7 +104,7 @@ export const useActionReview = () => { Maybe.fromNullable(selectedAction.amount) .map(defaultFormattedNumber) .extractNullable(), - [selectedAction], + [selectedAction] ); const path = useMemo( @@ -114,7 +114,7 @@ export const useActionReview = () => { : selectedAction.type === ActionTypes.STAKE ? "stake" : "pending", - [selectedAction], + [selectedAction] ); const labelKey: LabelKey = useMemo( @@ -123,16 +123,16 @@ export const useActionReview = () => { .chain((txs) => List.find( (tx) => tx.status === TransactionStatus.WAITING_FOR_SIGNATURE, - txs, + txs ).chain((tx) => - List.indexOf(tx, txs) + List.findIndex((val) => val.id === tx.id, txs) .chainNullable((index) => txs[index - 1]) .filter((prevTx) => prevTx.status === TransactionStatus.CONFIRMED) - .map(() => "continue" as LabelKey), - ), + .map(() => "continue" as LabelKey) + ) ) .orDefault("retry"), - [transactions], + [transactions] ); const actionOlderThan7Days = useMemo( @@ -140,7 +140,7 @@ export const useActionReview = () => { Maybe.of(selectedAction.createdAt) .map(dateOlderThen7Days) .orDefault(false), - [selectedAction], + [selectedAction] ); useRegisterFooterButton( @@ -152,8 +152,8 @@ export const useActionReview = () => { isLoading: false, hide: actionOlderThan7Days, }), - [navigate, path, labelKey, actionOlderThan7Days, t], - ), + [navigate, path, labelKey, actionOlderThan7Days, t] + ) ); return { diff --git a/packages/widget/src/pages/review/hooks/use-fees.ts b/packages/widget/src/pages/review/hooks/use-fees.ts index 36963209..d8b80b98 100644 --- a/packages/widget/src/pages/review/hooks/use-fees.ts +++ b/packages/widget/src/pages/review/hooks/use-fees.ts @@ -39,17 +39,17 @@ export const useFees = ({ prices, token, }), - [amount, token, prices], + [amount, token, prices] ); const getBpsInPercentage = useCallback( (val: number) => `${bpsToPercentage(val)}%`, - [], + [] ); const getPercentAmount = useCallback( (val: string) => amount.multipliedBy(val).dividedBy(100), - [amount], + [amount] ); const getPercentInUsd = useCallback( @@ -59,7 +59,7 @@ export const useFees = ({ prices, token, }), - [getPercentAmount, prices, token], + [getPercentAmount, prices, token] ); const depositFee = useMemo( @@ -80,7 +80,7 @@ export const useFees = ({ inPercentage: `${val}%`, explanation: t("review.deposit_fee_explanation"), label: t("review.deposit_fee"), - })), + })) ), [ feeConfigDto, @@ -89,7 +89,7 @@ export const useFees = ({ getPercentInUsd, t, yieldFee, - ], + ] ); const managementFee = useMemo( @@ -110,7 +110,7 @@ export const useFees = ({ inPercentage: `${val}%`, explanation: t("review.management_fee_explanation"), label: t("review.management_fee"), - })), + })) ), [ feeConfigDto, @@ -119,7 +119,7 @@ export const useFees = ({ getPercentInUsd, t, yieldFee, - ], + ] ); const performanceFee = useMemo( @@ -140,7 +140,7 @@ export const useFees = ({ inPercentage: `${val}%`, explanation: t("review.performance_fee_explanation"), label: t("review.performance_fee"), - })), + })) ), [ feeConfigDto, @@ -149,7 +149,7 @@ export const useFees = ({ getPercentInUsd, t, yieldFee, - ], + ] ); return { depositFee, managementFee, performanceFee }; diff --git a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts index 15889223..7c2a8261 100644 --- a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts @@ -27,7 +27,7 @@ export const usePendingActionReview = () => { const pendingRequest = useSelector( pendingActionStore, - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const actionPreviewQuery = useQuery({ @@ -36,10 +36,8 @@ export const usePendingActionReview = () => { retry: false, queryFn: () => createManageAction({ - addresses: pendingRequest.addresses, fetchClient: yieldApiFetchClient, requestDto: pendingRequest.requestDto, - yieldDto: pendingRequest.integrationData, }), }); @@ -49,27 +47,27 @@ export const usePendingActionReview = () => { .map((actionDto) => actionDto.transactions.reduce( (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), - new BigNumber(0), - ), + new BigNumber(0) + ) ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), - [actionPreviewQuery.data], + [actionPreviewQuery.data] ); const amount = useMemo( () => new BigNumber(pendingRequest.requestDto.arguments?.amount ?? 0), - [pendingRequest.requestDto.arguments?.amount], + [pendingRequest.requestDto.arguments?.amount] ); const interactedToken = useMemo( () => Maybe.of(pendingRequest.interactedToken), - [pendingRequest.interactedToken], + [pendingRequest.interactedToken] ); const integrationData = useMemo( () => Maybe.of(pendingRequest.integrationData), - [pendingRequest.integrationData], + [pendingRequest.integrationData] ); const pricesState = useTokensPrices({ @@ -93,10 +91,10 @@ export const usePendingActionReview = () => { t( `position_details.pending_action_button.${ pendingRequest.requestDto.action.toLowerCase() as Lowercase - }` as const, - ), + }` as const + ) ), - [pendingRequest.requestDto.action, t], + [pendingRequest.requestDto.action, t] ); const navigate = useNavigate(); @@ -108,7 +106,7 @@ export const usePendingActionReview = () => { prices: Maybe.fromNullable(pricesState.data), yieldDto: integrationData, }), - [integrationData, pendingTxGas, pricesState.data], + [integrationData, pendingTxGas, pricesState.data] ); const actionPendingMutation = useMutation({ @@ -146,7 +144,7 @@ export const usePendingActionReview = () => { rewardToken, } satisfies ComponentProps; }), - [integrationData, pendingRequest.requestDto.action], + [integrationData, pendingRequest.requestDto.action] ); const onClickRef = useSavedRef(onClick); @@ -159,8 +157,8 @@ export const usePendingActionReview = () => { disabled: false, isLoading: actionPendingMutation.isPending, }), - [onClickRef, t, actionPendingMutation.isPending], - ), + [onClickRef, t, actionPendingMutation.isPending] + ) ); const metaInfo: MetaInfoProps = useMemo(() => ({ showMetaInfo: false }), []); diff --git a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts index d98ef0eb..6a354bf5 100644 --- a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts @@ -27,14 +27,14 @@ export const useStakeReview = () => { const enterRequest = useSelector( enterStore, - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const yieldApiFetchClient = useYieldApiFetchClient(); const stakeAmount = useMemo( () => new BigNumber(enterRequest.requestDto.arguments?.amount ?? 0), - [enterRequest], + [enterRequest] ); const actionPreviewQuery = useQuery({ @@ -43,10 +43,8 @@ export const useStakeReview = () => { retry: false, queryFn: () => createEnterAction({ - addresses: enterRequest.addresses, fetchClient: yieldApiFetchClient, requestDto: enterRequest.requestDto, - yieldDto: enterRequest.selectedStake, }), }); @@ -56,12 +54,12 @@ export const useStakeReview = () => { .map((actionDto) => actionDto.transactions.reduce( (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), - new BigNumber(0), - ), + new BigNumber(0) + ) ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), - [actionPreviewQuery.data], + [actionPreviewQuery.data] ); const gasCheckWarning = useGasWarningCheck({ @@ -76,16 +74,16 @@ export const useStakeReview = () => { const selectedStake = useMemo( () => Maybe.of(enterRequest.selectedStake), - [enterRequest.selectedStake], + [enterRequest.selectedStake] ); const selectedToken = useMemo( () => Maybe.of(enterRequest.selectedToken), - [enterRequest.selectedToken], + [enterRequest.selectedToken] ); const selectedProviderYieldId = useMemo( () => Maybe.fromNullable(enterRequest.requestDto.arguments?.providerId), - [enterRequest.requestDto.arguments?.providerId], + [enterRequest.requestDto.arguments?.providerId] ); const rewardToken = useRewardTokenDetails(selectedStake); @@ -97,13 +95,13 @@ export const useStakeReview = () => { }); const yieldType = useYieldType(selectedStake).mapOrDefault( (y) => y.review, - "", + "" ); const amount = useMemo(() => formatNumber(stakeAmount), [stakeAmount]); const interestRate = useMemo( () => estimatedRewards.mapOrDefault((r) => r.percentage.toString(), ""), - [estimatedRewards], + [estimatedRewards] ); const pricesState = useTokensPrices({ @@ -118,7 +116,7 @@ export const useStakeReview = () => { prices: Maybe.fromNullable(pricesState.data), yieldDto: selectedStake, }), - [pricesState.data, selectedStake, stakeEnterTxGas], + [pricesState.data, selectedStake, stakeEnterTxGas] ); const { depositFee, managementFee, performanceFee } = useFees({ @@ -138,11 +136,11 @@ export const useStakeReview = () => { }; } ).mechanics?.fee ?? null, - [enterRequest.selectedStake], + [enterRequest.selectedStake] ), prices: useMemo( () => Maybe.fromNullable(pricesState.data), - [pricesState.data], + [pricesState.data] ), }); @@ -175,8 +173,8 @@ export const useStakeReview = () => { label: t("shared.confirm"), onClick: () => onClickRef.current(), }), - [onClickRef, t, enterMutation.isPending], - ), + [onClickRef, t, enterMutation.isPending] + ) ); const { variant } = useSettings(); @@ -193,7 +191,7 @@ export const useStakeReview = () => { }, } : { showMetaInfo: false }) satisfies MetaInfoProps, - [selectedStake, selectedToken, enterRequest.selectedValidators, variant], + [selectedStake, selectedToken, enterRequest.selectedValidators, variant] ); const commissionFee = useMemo( @@ -201,10 +199,10 @@ export const useStakeReview = () => { selectedStake .chainNullable((y) => getYieldMetadata(y).commission) .map((commission) => - commission.reduce((acc, curr) => acc + curr.value, 0), + commission.reduce((acc, curr) => acc + curr.value, 0) ) .map((val) => `${APToPercentage(val)}%`), - [selectedStake], + [selectedStake] ); return { diff --git a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts index 709b824b..d9ecfbb2 100644 --- a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts @@ -27,7 +27,7 @@ import type { MetaInfoProps } from "../pages/common-page/common.page"; export const useUnstakeActionReview = () => { const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const yieldApiFetchClient = useYieldApiFetchClient(); @@ -38,10 +38,8 @@ export const useUnstakeActionReview = () => { retry: false, queryFn: () => createExitAction({ - addresses: exitRequest.addresses, fetchClient: yieldApiFetchClient, requestDto: exitRequest.requestDto, - yieldDto: exitRequest.integrationData, }), }); @@ -51,22 +49,22 @@ export const useUnstakeActionReview = () => { .map((actionDto) => actionDto.transactions.reduce( (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), - new BigNumber(0), - ), + new BigNumber(0) + ) ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), - [actionPreviewQuery.data], + [actionPreviewQuery.data] ); const interactedToken = useMemo( () => Maybe.of(exitRequest.unstakeToken), - [exitRequest.unstakeToken], + [exitRequest.unstakeToken] ); const integrationData = useMemo( () => Maybe.of(exitRequest.integrationData), - [exitRequest.integrationData], + [exitRequest.integrationData] ); const pricesState = useTokensPrices({ @@ -76,7 +74,7 @@ export const useUnstakeActionReview = () => { const amount = useMemo( () => new BigNumber(exitRequest.requestDto.arguments?.amount ?? 0), - [exitRequest.requestDto.arguments?.amount], + [exitRequest.requestDto.arguments?.amount] ); const gasWarningCheck = useGasWarningCheck({ @@ -111,7 +109,7 @@ export const useUnstakeActionReview = () => { prices: Maybe.fromNullable(pricesState.data), yieldDto: integrationData, }), - [integrationData, pricesState.data, stakeExitTxGas], + [integrationData, pricesState.data, stakeExitTxGas] ); const rewardTokenDetailsProps = integrationData @@ -167,8 +165,8 @@ export const useUnstakeActionReview = () => { disabled: false, isLoading: unstakeIsLoading, }), - [onClickRef, t, unstakeIsLoading], - ), + [onClickRef, t, unstakeIsLoading] + ) ); return { diff --git a/packages/widget/src/pages/review/pages/action-review.page.tsx b/packages/widget/src/pages/review/pages/action-review.page.tsx index c739154f..dad7b9a5 100644 --- a/packages/widget/src/pages/review/pages/action-review.page.tsx +++ b/packages/widget/src/pages/review/pages/action-review.page.tsx @@ -33,7 +33,7 @@ export const ActionReviewPage = () => { Maybe.fromNullable(selectedYield.token) .map((val) => `${amount} ${val.symbol}`) .extractNullable(), - [amount, selectedYield.token], + [amount, selectedYield.token] ); return ( @@ -82,7 +82,7 @@ export const ActionReviewPage = () => { .extractNullable()} - )), + )) ) .extractNullable()} diff --git a/packages/widget/src/pages/review/pages/pending-review.page.tsx b/packages/widget/src/pages/review/pages/pending-review.page.tsx index 657719ac..60df3098 100644 --- a/packages/widget/src/pages/review/pages/pending-review.page.tsx +++ b/packages/widget/src/pages/review/pages/pending-review.page.tsx @@ -22,7 +22,7 @@ export const PendingReviewPage = () => { const info = useMemo( () => token.map((val) => `${amount} ${val.symbol}`).extractNullable(), - [amount, token], + [amount, token] ); const { depositFee, managementFee, performanceFee, feeConfigLoading } = @@ -33,7 +33,7 @@ export const PendingReviewPage = () => { performanceFee: Maybe.empty(), feeConfigLoading: false, }), - [], + [] ); return ( diff --git a/packages/widget/src/pages/review/pages/stake-review.page.tsx b/packages/widget/src/pages/review/pages/stake-review.page.tsx index b668432a..39f3d82f 100644 --- a/packages/widget/src/pages/review/pages/stake-review.page.tsx +++ b/packages/widget/src/pages/review/pages/stake-review.page.tsx @@ -46,12 +46,12 @@ export const StakeReviewPage = () => { /> )) .extractNullable(), - [amount, interestRate, token], + [amount, interestRate, token] ); const rewardTokenDetailsProps = useMemo( () => Maybe.of({ rewardToken, type: "stake" as const }), - [rewardToken], + [rewardToken] ); return ( diff --git a/packages/widget/src/pages/review/pages/unstake-review.page.tsx b/packages/widget/src/pages/review/pages/unstake-review.page.tsx index 6fcb52d9..07c85099 100644 --- a/packages/widget/src/pages/review/pages/unstake-review.page.tsx +++ b/packages/widget/src/pages/review/pages/unstake-review.page.tsx @@ -26,7 +26,7 @@ export const UnstakeReviewPage = () => { const info = useMemo( () => token.map((val) => `${amount} ${val.symbol}`).extractNullable(), - [amount, token], + [amount, token] ); const { depositFee, managementFee, performanceFee, feeConfigLoading } = @@ -37,7 +37,7 @@ export const UnstakeReviewPage = () => { performanceFee: Maybe.empty(), feeConfigLoading: false, }), - [], + [] ); return ( diff --git a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts index bba5b22f..5a485e76 100644 --- a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts +++ b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts @@ -66,7 +66,7 @@ export const useStepsMachine = ({ const sortedTransactions = useMemo( () => [...transactions].sort((a, b) => (a.stepIndex ?? 0) - (b.stepIndex ?? 0)), - [transactions], + [transactions] ); const machineParams = useSavedRef({ @@ -93,11 +93,11 @@ const getMachine = ( actionMeta: ActionMeta; yieldApiFetchClient: ReturnType; }> - >, + > ) => { const initContext = getInitContext( ref.current.transactions, - ref.current.yieldId, + ref.current.yieldId ); return setup({ @@ -175,8 +175,8 @@ const getMachine = ( signedTx: event.val.data.signedTx, }, } - : val, - ), + : val + ) ) .orDefault(context.txStates), }), @@ -199,8 +199,8 @@ const getMachine = ( signError: event.val, }, } - : val, - ), + : val + ) ) .orDefault(context.txStates), }), @@ -211,7 +211,7 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx].tx) - .toEither(new SignError({ network: "unknown", txId: "unknown" })), + .toEither(new SignError({ network: "unknown", txId: "unknown" })) ) .chain< SendTransactionError | TransactionDecodeError | SignError, @@ -227,8 +227,8 @@ const getMachine = ( new SignError({ network: tx.network, txId: tx.id, - }), - ), + }) + ) ); } @@ -249,7 +249,7 @@ const getMachine = ( new SignError({ network: tx.network, txId: tx.id, - }), + }) ); } @@ -283,7 +283,7 @@ const getMachine = ( txId: tx.id, network: tx.network, yieldId: context.yieldId, - }), + }) ) .map((val) => ({ type: "regular", data: val })); }) @@ -325,8 +325,8 @@ const getMachine = ( signError: null, }, } - : val, - ), + : val + ) ) .orDefault(context.txStates), }), @@ -347,8 +347,8 @@ const getMachine = ( signError: null, }, } - : val, - ), + : val + ) ) .orDefault(context.txStates), }), @@ -358,7 +358,7 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx]) - .toEither(new Error("missing tx")), + .toEither(new Error("missing tx")) ) .chain((currentTx) => { if (currentTx.meta.broadcasted) { @@ -367,7 +367,7 @@ const getMachine = ( fetchClient: ref.current.yieldApiFetchClient, hash: currentTx.meta.signedTx!, transactionId: currentTx.tx.id, - }), + }) ) .mapLeft(() => new SubmitHashError()) .ifRight(() => { @@ -385,7 +385,7 @@ const getMachine = ( fetchClient: ref.current.yieldApiFetchClient, signedTransaction: currentTx.meta.signedTx!, transactionId: currentTx.tx.id, - }), + }) ) .mapLeft(() => new SubmitError()) .ifRight(() => { @@ -441,8 +441,8 @@ const getMachine = ( signError: null, }, } - : val, - ), + : val + ) ) .orDefault(context.txStates), }), @@ -453,14 +453,14 @@ const getMachine = ( EitherAsync.liftEither( context.currentTxMeta .chainNullable((v) => context.txStates[v.idx]) - .toEither(new Error("missing tx")), + .toEither(new Error("missing tx")) ) .chain((currentTx) => EitherAsync(() => getTransaction({ fetchClient: ref.current.yieldApiFetchClient, transactionId: currentTx.tx.id, - }), + }) ) .map((res) => ({ url: res.explorerUrl, @@ -474,14 +474,14 @@ const getMachine = ( new SignError({ txId: currentTx.tx.id, network: currentTx.tx.network, - }), + }) ) : Right({ url: val.url, isConfirmed: val.status === "CONFIRMED", - }), - ), - ), + }) + ) + ) ) .caseOf({ Left: (l) => { @@ -512,14 +512,14 @@ const getMachine = ( done: true, }, } - : val, - ), + : val + ) ) .orDefault(context.txStates); const newCurrentTxMeta = List.findIndex( (val) => !val.meta.done, - newTxStates, + newTxStates ) .map((idx) => ({ idx, @@ -577,7 +577,7 @@ const getMachine = ( const getInitContext = ( transactions: ActionDto["transactions"], - yieldId: ActionDto["yieldId"], + yieldId: ActionDto["yieldId"] ) => { if (!transactions.length) { return { diff --git a/packages/widget/src/pages/steps/hooks/use-steps.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps.hook.ts index f2f2a393..ae4ad2ba 100644 --- a/packages/widget/src/pages/steps/hooks/use-steps.hook.ts +++ b/packages/widget/src/pages/steps/hooks/use-steps.hook.ts @@ -44,11 +44,11 @@ export const useSteps = ({ rewardType: v.rewardType, website: v.website, logo: v.logo, - })), + })) ) .orDefault([]), }), - [session, providersDetails, inputToken], + [session, providersDetails, inputToken] ); const [machineState, send, actorRef] = useStepsMachine({ @@ -71,7 +71,7 @@ export const useSteps = ({ */ useEffect(() => { const sub = actorRef.on("signSuccess", () => - callbacksRef.current.onSignSuccess?.(), + callbacksRef.current.onSignSuccess?.() ); return () => { @@ -108,7 +108,7 @@ export const useSteps = ({ urls: machineState.context.txStates .map((val) => ({ type: val.tx.type, url: val.meta.url })) .filter( - (val): val is { type: TransactionType; url: string } => !!val.url, + (val): val is { type: TransactionType; url: string } => !!val.url ), }, relative: "path", @@ -161,7 +161,7 @@ export const useSteps = ({ machineState.context.currentTxMeta, machineState.context.txStates, machineState.value, - ], + ] ); const { t } = useTranslation(); @@ -180,8 +180,8 @@ export const useSteps = ({ variant: "secondary", } : null, - [txStates.length, t, onClickRef], - ), + [txStates.length, t, onClickRef] + ) ); return { diff --git a/packages/widget/src/pages/steps/pages/activity-steps.page.tsx b/packages/widget/src/pages/steps/pages/activity-steps.page.tsx index 327fdc2c..1b41c0bd 100644 --- a/packages/widget/src/pages/steps/pages/activity-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/activity-steps.page.tsx @@ -17,19 +17,19 @@ export const ActivityStepsPage = () => { const selectedAction = useSelector( activityContext, - (state) => state.context.selectedAction, + (state) => state.context.selectedAction ).unsafeCoerce(); const selectedYield = useSelector( activityContext, - (state) => state.context.selectedYield, + (state) => state.context.selectedYield ).unsafeCoerce(); const providersDetails = useProvidersDetails({ integrationData: useMemo(() => Maybe.of(selectedYield), [selectedYield]), validatorsAddresses: useMemo( () => Maybe.of(getActionValidatorAddresses(selectedAction) ?? []), - [selectedAction], + [selectedAction] ), selectedProviderYieldId: Maybe.empty(), }); diff --git a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx index 86ff60cc..ae34ece4 100644 --- a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx @@ -13,7 +13,7 @@ export const PendingStepsPage = () => { const pendingRequest = useSelector( usePendingActionStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const { plain } = useUnstakeOrPendingActionParams(); @@ -26,10 +26,10 @@ export const PendingStepsPage = () => { const providersDetails = useProvidersDetails({ integrationData: useMemo( () => Maybe.of(pendingRequest.integrationData), - [pendingRequest.integrationData], + [pendingRequest.integrationData] ), validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [], + p.type === "validators" ? p.validatorsAddresses : [] ), selectedProviderYieldId: Maybe.empty(), }); diff --git a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx index 16d6f38d..27a0daa6 100644 --- a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx @@ -14,7 +14,7 @@ export const StakeStepsPage = () => { const enterRequest = useSelector( useEnterStakeStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const onSignSuccess = () => @@ -26,11 +26,11 @@ export const StakeStepsPage = () => { const providersDetails = useProvidersDetails({ integrationData: useMemo( () => Maybe.of(enterRequest.selectedStake), - [enterRequest.selectedStake], + [enterRequest.selectedStake] ), validatorsAddresses: useMemo( () => Maybe.of(enterRequest.selectedValidators), - [enterRequest.selectedValidators], + [enterRequest.selectedValidators] ), selectedProviderYieldId: Maybe.empty(), }); diff --git a/packages/widget/src/pages/steps/pages/tx-state.tsx b/packages/widget/src/pages/steps/pages/tx-state.tsx index dde05bfc..6ac39e16 100644 --- a/packages/widget/src/pages/steps/pages/tx-state.tsx +++ b/packages/widget/src/pages/steps/pages/tx-state.tsx @@ -68,7 +68,7 @@ export const TxState = ({ txState, position, count, session }: Props) => { context: isEthenaUsdeStaking(session.yieldId) ? "ETHENA_USDE" : undefined, - } as never, + } as never ) as unknown as string, })} diff --git a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx index d7b75cd0..56baef36 100644 --- a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx @@ -13,7 +13,7 @@ export const UnstakeStepsPage = () => { const exitRequest = useSelector( useExitStakeStore(), - (state) => state.context.data, + (state) => state.context.data ).unsafeCoerce(); const { plain } = useUnstakeOrPendingActionParams(); @@ -25,10 +25,10 @@ export const UnstakeStepsPage = () => { const providersDetails = useProvidersDetails({ integrationData: useMemo( () => Maybe.of(exitRequest.integrationData), - [exitRequest.integrationData], + [exitRequest.integrationData] ), validatorsAddresses: positionBalances.data.map((p) => - p.type === "validators" ? p.validatorsAddresses : [], + p.type === "validators" ? p.validatorsAddresses : [] ), selectedProviderYieldId: Maybe.empty(), }); diff --git a/packages/widget/src/providers/activity-provider/index.tsx b/packages/widget/src/providers/activity-provider/index.tsx index e0a1348d..4d7a167b 100644 --- a/packages/widget/src/providers/activity-provider/index.tsx +++ b/packages/widget/src/providers/activity-provider/index.tsx @@ -14,7 +14,7 @@ const store = createStore({ _, event: { data: Maybe<{ selectedAction: ActionDto; selectedYield: Yield }>; - }, + } ) => ({ selectedAction: event.data.map(({ selectedAction }) => selectedAction), selectedYield: event.data.map(({ selectedYield }) => selectedYield), @@ -37,7 +37,7 @@ export const useActivityContext = () => { if (!value) { throw new Error( - "useActivityContext must be used within a ActivityProvider", + "useActivityContext must be used within a ActivityProvider" ); } diff --git a/packages/widget/src/providers/api/get-enabled-networks.ts b/packages/widget/src/providers/api/get-enabled-networks.ts index a46da5cc..7d4c1797 100644 --- a/packages/widget/src/providers/api/get-enabled-networks.ts +++ b/packages/widget/src/providers/api/get-enabled-networks.ts @@ -19,8 +19,8 @@ export const getEnabledNetworks = ({ queryFn: async () => new Set( (await getResponseData(yieldApiFetchClient.GET("/v1/networks"))).map( - (network) => network.id as Networks, - ), + (network) => network.id as Networks + ) ), - }), + }) ).mapLeft(() => new Error("Could not get enabled networks")); diff --git a/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts b/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts index 5b5aec47..915d3790 100644 --- a/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts +++ b/packages/widget/src/providers/cosmos/chains/get-chain-registry.ts @@ -166,13 +166,13 @@ const registryIdsToSKCosmosNetworks: Record = supportedCosmosChains.map((key) => [ skCosmosNetworksToRegistryIds[key], key, - ]), + ]) ); const registryIdsSet = new Set(Object.values(skCosmosNetworksToRegistryIds)); const chainMapper = ( - val: T, + val: T ): WithWagmiName => { let wagmiName = val.chain_name[0].toUpperCase() + val.chain_name.slice(1); @@ -202,7 +202,7 @@ const chainMapper = ( }; const assetMapper = ( - val: WithWagmiName>, + val: WithWagmiName> ) => { if (val.chain_id === "comdex-1") { val.assets[1].coingecko_id = "harbor-2"; @@ -223,7 +223,7 @@ export const getRegistryIdsToSKCosmosNetworks = (): typeof registryIdsToSKCosmosNetworks => registryIdsToSKCosmosNetworks; const filteredCosmosChainNames = new Map( - cosmosRegistryChains.map((c) => [c.chain_name, c.chain_id]), + cosmosRegistryChains.map((c) => [c.chain_name, c.chain_id]) ); const filterMissingChainName = (val: AssetList) => !!val.chain_name; diff --git a/packages/widget/src/providers/cosmos/chains/index.ts b/packages/widget/src/providers/cosmos/chains/index.ts index 51b91d51..f26aed03 100644 --- a/packages/widget/src/providers/cosmos/chains/index.ts +++ b/packages/widget/src/providers/cosmos/chains/index.ts @@ -6,7 +6,7 @@ import { getNetworkLogo, getTokenLogo } from "../../../utils"; import type { CosmosChainsAssets } from "./types"; export const getWagmiChain = ( - chain: CosmosChainsAssets, + chain: CosmosChainsAssets ): Chain & { cosmosChainName: string } => ({ id: chain.chain_id as unknown as number, iconUrl: Just(chain.chain_id) diff --git a/packages/widget/src/providers/cosmos/config.ts b/packages/widget/src/providers/cosmos/config.ts index cfedfc48..dfe38824 100644 --- a/packages/widget/src/providers/cosmos/config.ts +++ b/packages/widget/src/providers/cosmos/config.ts @@ -31,7 +31,7 @@ const queryFn = async ({ } >((networks) => { const chainsToUse = supportedCosmosChains.filter((chain) => - networks.has(chain), + networks.has(chain) ); if (!chainsToUse.length) { @@ -40,7 +40,7 @@ const queryFn = async ({ cosmosChainsMap: {}, cosmosWagmiChains: [], connector: Maybe.empty(), - }), + }) ); } @@ -70,14 +70,14 @@ const queryFn = async ({ wagmiChain: getWagmiChain(next), }, }; - }, {} as CosmosChainsMap), - ).filter(([_, v]) => networks.has(v.skChainName)), + }, {} as CosmosChainsMap) + ).filter(([_, v]) => networks.has(v.skChainName)) ); return { cosmosChainsMap, cosmosWagmiChains: Object.values(cosmosChainsMap).map( - (val) => val.wagmiChain, + (val) => val.wagmiChain ), }; }) @@ -87,12 +87,12 @@ const queryFn = async ({ (e) => new Error("Could not import cosmos wallet manager", { cause: e, - }), + }) ) .map((v) => - v.getWalletManager({ cosmosChainsMap, forceWalletConnectOnly }), + v.getWalletManager({ cosmosChainsMap, forceWalletConnectOnly }) ) - .map((val) => ({ ...val, cosmosWagmiChains, cosmosChainsMap })), + .map((val) => ({ ...val, cosmosWagmiChains, cosmosChainsMap })) ) .chain((v) => EitherAsync(() => v.walletManager.onMounted()) @@ -100,7 +100,7 @@ const queryFn = async ({ EitherAsync(() => { // @ts-expect-error return cosmosWalletManager._restoreAccounts().catch(() => {}); - }), + }) ) .mapLeft((e) => { console.log(e); @@ -112,9 +112,9 @@ const queryFn = async ({ cosmosWagmiChains: v.cosmosWagmiChains, connector: Maybe.fromPredicate( () => !!v.cosmosWagmiChains.length, - v.connector, + v.connector ), - })), + })) ); }) .caseOf({ @@ -128,7 +128,7 @@ export const getConfig = (opts: Parameters[0]) => staleTime, queryKey, queryFn: () => queryFn(opts), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get cosmos config"); diff --git a/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts b/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts index 018677ea..34d07bec 100644 --- a/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts +++ b/packages/widget/src/providers/cosmos/cosmos-connector-meta.ts @@ -22,5 +22,5 @@ export type ExtraProps = ConnectorWithFilteredChains & { export type CosmosConnector = Connector & ExtraProps; export const isCosmosConnector = ( - connector: Connector, + connector: Connector ): connector is CosmosConnector => connector.type === configMeta.type; diff --git a/packages/widget/src/providers/cosmos/cosmos-connector.ts b/packages/widget/src/providers/cosmos/cosmos-connector.ts index bfde43f8..a41844b0 100644 --- a/packages/widget/src/providers/cosmos/cosmos-connector.ts +++ b/packages/widget/src/providers/cosmos/cosmos-connector.ts @@ -60,7 +60,7 @@ export const createCosmosConnector = ({ const initCw = wallet.chainWalletMap.get( cosmosChainsMap.cosmos?.chain.chain_name ?? - Object.values(cosmosChainsMap)[0].chain.chain_name, + Object.values(cosmosChainsMap)[0].chain.chain_name ); if (!initCw) throw new Error("Chain wallet not found"); @@ -92,7 +92,7 @@ export const createCosmosConnector = ({ }); const connect: ReturnType["connect"] = async ( - args, + args ) => { config.emitter.emit("message", { type: "connecting" }); @@ -168,7 +168,7 @@ export const createCosmosConnector = ({ }; const newCw = wallet.getChainWallet( - cosmosChain.cosmosChainName, + cosmosChain.cosmosChainName ) as ChainWalletBase; if (!newCw) throw new Error("Chain wallet not found"); @@ -232,11 +232,11 @@ export const createCosmosConnector = ({ tx: string; }) => EitherAsync(() => - cw.client.signDirect?.( + cw.client.signDirect!( cw.chainId, cw.address!, - SignDoc.decode(fromHex(tx)) as unknown as DirectSignDoc, // accountNumber bigint/Long issue - ), + SignDoc.decode(fromHex(tx)) as unknown as DirectSignDoc // accountNumber bigint/Long issue + ) ) .mapLeft((e) => { console.log(e); @@ -248,8 +248,8 @@ export const createCosmosConnector = ({ authInfoBytes: val.signed.authInfoBytes, bodyBytes: val.signed.bodyBytes, signatures: [decodeSignature(val.signature).signature], - }).finish(), - ), + }).finish() + ) ); const getChainId: ReturnType["getChainId"] = diff --git a/packages/widget/src/providers/cosmos/wallet-connect/client.ts b/packages/widget/src/providers/cosmos/wallet-connect/client.ts index df2de22c..8b0911f8 100644 --- a/packages/widget/src/providers/cosmos/wallet-connect/client.ts +++ b/packages/widget/src/providers/cosmos/wallet-connect/client.ts @@ -5,7 +5,7 @@ export class WalletConnectClient extends WCClient { this.signClient?.pairing .getAll() .forEach((p) => - this.signClient?.core.pairing.disconnect({ topic: p.topic }), + this.signClient?.core.pairing.disconnect({ topic: p.topic }) ); return super.disconnect(); diff --git a/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts b/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts index 38da78b8..0ebdcd2f 100644 --- a/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts +++ b/packages/widget/src/providers/cosmos/wallet-connect/main-wallet.ts @@ -7,7 +7,7 @@ import { WalletConnectClient } from "./client"; export class WalletConnectWallet extends WCWallet { constructor( walletInfo: Wallet, - preferredEndpoints?: EndpointOptions["endpoints"], + preferredEndpoints?: EndpointOptions["endpoints"] ) { super(walletInfo, ChainWalletConnect, WalletConnectClient); this.preferredEndpoints = preferredEndpoints; diff --git a/packages/widget/src/providers/cosmos/wallet-connect/registry.ts b/packages/widget/src/providers/cosmos/wallet-connect/registry.ts index 1f7d9aa7..3be18e1a 100644 --- a/packages/widget/src/providers/cosmos/wallet-connect/registry.ts +++ b/packages/widget/src/providers/cosmos/wallet-connect/registry.ts @@ -41,7 +41,7 @@ export const walletConnectInfo: Wallet = { appUrl: string, wcUri: string, _os: OS | undefined, - _name: string, + _name: string ): string => { const plainAppUrl = appUrl.replaceAll("/", "").replaceAll(":", ""); const encodedWcUrl = encodeURIComponent(wcUri); diff --git a/packages/widget/src/providers/cosmos/wallet-manager.ts b/packages/widget/src/providers/cosmos/wallet-manager.ts index 913426c5..1e711316 100644 --- a/packages/widget/src/providers/cosmos/wallet-manager.ts +++ b/packages/widget/src/providers/cosmos/wallet-manager.ts @@ -51,8 +51,8 @@ export const getWalletManager = ({ { cosmosWagmiChains: [] as Chain[], chains: [] as CosmosChainsAssets[], - }, - ), + } + ) ) .map((val) => ({ ...val, @@ -60,7 +60,7 @@ export const getWalletManager = ({ // Put cosmos first registryIdsToSKCosmosNetworks[a.chain_id] === CosmosNetworks.Cosmos ? -1 - : 1, + : 1 ), })) .unsafeCoerce(); @@ -73,7 +73,7 @@ export const getWalletManager = ({ wallet: w, cosmosChainsMap, cosmosWagmiChains, - }), + }) ), }; @@ -93,7 +93,7 @@ export const getWalletManager = ({ projectId: config.walletConnectV2.projectId, customStoragePrefix: "cosmoswalletconnect_", }, - }, + } ), }; }; diff --git a/packages/widget/src/providers/enter-stake-store/index.tsx b/packages/widget/src/providers/enter-stake-store/index.tsx index c0d62c00..2149e647 100644 --- a/packages/widget/src/providers/enter-stake-store/index.tsx +++ b/packages/widget/src/providers/enter-stake-store/index.tsx @@ -37,7 +37,7 @@ const store = createStore({ }); const EnterStakeStoreContext = createContext( - undefined, + undefined ); export const EnterStakeStoreProvider = ({ children }: PropsWithChildren) => { @@ -53,7 +53,7 @@ export const useEnterStakeStore = () => { if (!value) { throw new Error( - "useEnterStakeStore must be used within a EnterStakeStoreProvider", + "useEnterStakeStore must be used within a EnterStakeStoreProvider" ); } diff --git a/packages/widget/src/providers/ethereum/config.ts b/packages/widget/src/providers/ethereum/config.ts index 99061eec..7b91a30d 100644 --- a/packages/widget/src/providers/ethereum/config.ts +++ b/packages/widget/src/providers/ethereum/config.ts @@ -40,12 +40,12 @@ const queryFn = async ({ const filteredEvmChainsMap: Partial = typeSafeObjectFromEntries( typeSafeObjectEntries(evmChainsMap).filter(([_, v]) => - networks.has(v.skChainName), - ), + networks.has(v.skChainName) + ) ); const evmChains = Object.values(filteredEvmChainsMap).map( - (val) => val.wagmiChain, + (val) => val.wagmiChain ); const portoWallet: WalletList[number]["wallets"][number] = (args) => ({ @@ -93,7 +93,7 @@ export const getConfig = (opts: Parameters[0]) => staleTime: Number.POSITIVE_INFINITY, queryKey: [config.appPrefix, "evm-config"], queryFn: () => queryFn(opts), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get evm config"); diff --git a/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts b/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts index f4ce18bb..2b6c4f96 100644 --- a/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts +++ b/packages/widget/src/providers/ethereum/finery-wallet-list/index.ts @@ -152,7 +152,7 @@ const safeWalletWC: CommonWalletOptions = Maybe.of(safeWallet()) qrCode: "https://app.safe.global/", }, chainGroup: evmChainGroup, - }) satisfies CommonWalletOptions, + }) satisfies CommonWalletOptions ) .unsafeCoerce(); @@ -194,7 +194,7 @@ export const createFineryWallets: (evmChains: Chain[]) => { // copperConnectProvider: (typeof providers)[number] | undefined; mpcVaultProvider: (typeof providers)[number] | undefined; cactusProvider: (typeof providers)[number] | undefined; - }, + } ); const cactusLinkWallet: WalletList[number]["wallets"][number] = () => ({ @@ -286,8 +286,8 @@ export const createFineryWallets: (evmChains: Chain[]) => { id: `${w.id}-wc`, rdns: `${w.rdns}-wc`, }), - evmChains, - ), + evmChains + ) ); const primaryWallets: WalletList[number]["wallets"] = [ diff --git a/packages/widget/src/providers/ethereum/utils.ts b/packages/widget/src/providers/ethereum/utils.ts index 11fbce26..6cb242c0 100644 --- a/packages/widget/src/providers/ethereum/utils.ts +++ b/packages/widget/src/providers/ethereum/utils.ts @@ -4,7 +4,7 @@ import { createConnector } from "wagmi"; export const passCorrectChainsToWallet = ( wallet: WalletList[number]["wallets"][number], - chains: Chain[], + chains: Chain[] ): WalletList[number]["wallets"][number] => (props) => { const w = wallet(props); @@ -16,7 +16,7 @@ export const passCorrectChainsToWallet = w.createConnector(walletDetails)({ ...config, chains: chains as [Chain, ...Chain[]], - }), + }) ), }; }; diff --git a/packages/widget/src/providers/exit-stake-store/index.tsx b/packages/widget/src/providers/exit-stake-store/index.tsx index ae354908..3083df69 100644 --- a/packages/widget/src/providers/exit-stake-store/index.tsx +++ b/packages/widget/src/providers/exit-stake-store/index.tsx @@ -37,7 +37,7 @@ const store = createStore({ }); const ExitStakeStoreContext = createContext( - undefined, + undefined ); export const ExitStakeStoreProvider = ({ children }: PropsWithChildren) => { @@ -53,7 +53,7 @@ export const useExitStakeStore = () => { if (!value) { throw new Error( - "useExitStakeStore must be used within a ExitStakeStoreProvider", + "useExitStakeStore must be used within a ExitStakeStoreProvider" ); } diff --git a/packages/widget/src/providers/external-provider/index.ts b/packages/widget/src/providers/external-provider/index.ts index a77184ff..550127f4 100644 --- a/packages/widget/src/providers/external-provider/index.ts +++ b/packages/widget/src/providers/external-provider/index.ts @@ -29,11 +29,11 @@ type ExtraProps = ConnectorWithFilteredChains & type ExternalConnector = Connector & ExtraProps; export const isExternalProviderConnector = ( - connector: Connector, + connector: Connector ): connector is ExternalConnector => connector.id === configMeta.id; export const externalProviderConnector = ( - variant: RefObject, + variant: RefObject ): WalletList[number] => ({ groupName: "External Providers", wallets: [ @@ -54,8 +54,8 @@ export const externalProviderConnector = ( .map((val) => new Set(val)) .mapOrDefault( (val) => connectorConfig.chains.filter((c) => val.has(c.id)), - connectorConfig.chains as [Chain, ...Chain[]], - ), + connectorConfig.chains as [Chain, ...Chain[]] + ) ); if ($filteredChains.getValue().length === 0) { @@ -71,7 +71,7 @@ export const externalProviderConnector = ( async () => $filteredChains.getValue()[0].id; const connect: ReturnType["connect"] = async ( - args, + args ) => { connectorConfig.emitter.emit("message", { type: "connecting" }); @@ -94,11 +94,11 @@ export const externalProviderConnector = ( await EitherAsync.liftEither( List.find( (c) => c.id === chainId, - connectorConfig.chains as unknown as Array, - ).toEither(new Error("Chain not found")), + connectorConfig.chains as unknown as Array + ).toEither(new Error("Chain not found")) ) .chain((chain) => - provider.switchChain({ chainId }).map(() => chain), + provider.switchChain({ chainId }).map(() => chain) ) .ifRight((chain) => onChainChanged(chain.id.toString())) ).unsafeCoerce(); @@ -140,8 +140,8 @@ export const externalProviderConnector = ( .mapOrDefault( (val) => connectorConfig.chains.filter((c) => val.has(c.id)), - connectorConfig.chains as [Chain, ...Chain[]], - ), + connectorConfig.chains as [Chain, ...Chain[]] + ) ); // If the current chain is not in the supported chains, switch to the first supported chain @@ -149,7 +149,7 @@ export const externalProviderConnector = ( $filteredChains.getValue().every((c) => c.id !== currentChainId) ) { getChainId().then((chainId) => - onChainChanged(chainId.toString()), + onChainChanged(chainId.toString()) ); } }; diff --git a/packages/widget/src/providers/ledger/config.ts b/packages/widget/src/providers/ledger/config.ts index 03a57aa8..fbc13172 100644 --- a/packages/widget/src/providers/ledger/config.ts +++ b/packages/widget/src/providers/ledger/config.ts @@ -20,12 +20,12 @@ const queryFn = async ({ wallets: WalletList[number]["wallets"]; } | null> => { return EitherAsync.liftEither( - Maybe.fromFalsy(isLedgerDappBrowserProvider()).toEither(null), + Maybe.fromFalsy(isLedgerDappBrowserProvider()).toEither(null) ) .chain(() => EitherAsync(() => import("./ledger-connector")) .mapLeft(() => new Error("Could not import ledger-connector")) - .map((v) => v.ledgerLiveConnector({ enabledChainsMap, queryParams })), + .map((v) => v.ledgerLiveConnector({ enabledChainsMap, queryParams })) ) .chainLeft((e) => EitherAsync.liftEither(e ? Left(e) : Right(null))) .caseOf({ @@ -35,14 +35,14 @@ const queryFn = async ({ }; export const getConfig = ( - opts: Parameters[0] & { queryClient: QueryClient }, + opts: Parameters[0] & { queryClient: QueryClient } ) => EitherAsync(() => opts.queryClient.fetchQuery({ staleTime, queryKey, queryFn: () => queryFn(opts), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get ledger live config"); diff --git a/packages/widget/src/providers/ledger/ledger-connector.ts b/packages/widget/src/providers/ledger/ledger-connector.ts index daa4a63f..a71a07d3 100644 --- a/packages/widget/src/providers/ledger/ledger-connector.ts +++ b/packages/widget/src/providers/ledger/ledger-connector.ts @@ -45,7 +45,7 @@ const createLedgerLiveConnector = ({ const $currentAccount = new BehaviorSubject(undefined); const $currentAccountId = $currentAccount.pipe( - map((v) => v?.parentAccountId ?? v?.id), + map((v) => v?.parentAccountId ?? v?.id) ); let ledgerAccounts: Account[] = []; const $accountsOnCurrentChain = new BehaviorSubject([]); @@ -79,7 +79,7 @@ const createLedgerLiveConnector = ({ .map((val) => ({ accounts: val, accountsMap: new Map( - val.map((v) => [v.id, v]), + val.map((v) => [v.id, v]) ), })) .map((val) => @@ -91,8 +91,8 @@ const createLedgerLiveConnector = ({ currency: parentAcc.currency, })) .orDefault(acc) - : acc, - ), + : acc + ) ) .mapLeft((e) => { console.log(e); @@ -111,8 +111,8 @@ const createLedgerLiveConnector = ({ filteredSkSupportedChainsToCurrencyIdMap = new Map( [...filteredSupportedLedgerFamiliesWithCurrency.values()].flatMap((v) => - [...v.values()].map((v) => [v.chain.id, v.currencyId]), - ), + [...v.values()].map((v) => [v.chain.id, v.currencyId]) + ) ); filteredSkSupportedChainsValues = @@ -132,7 +132,7 @@ const createLedgerLiveConnector = ({ return acc; }, - { enabled: [] as Chain[], disabled: [] as Chain[] }, + { enabled: [] as Chain[], disabled: [] as Chain[] } ); // Set chains to expose for switcher @@ -147,7 +147,7 @@ const createLedgerLiveConnector = ({ if (!family) return acc; const itemMap = filteredSupportedLedgerFamiliesWithCurrency.get( - family as SupportedLedgerLiveFamilies, + family as SupportedLedgerLiveFamilies ); if (!family || !itemMap) return acc; @@ -160,7 +160,7 @@ const createLedgerLiveConnector = ({ return acc; }, - [] as { account: Account; chainItem: ChainItem }[], + [] as { account: Account; chainItem: ChainItem }[] ) .sort((a, b) => { const aPriority = @@ -175,7 +175,7 @@ const createLedgerLiveConnector = ({ const defaultChain = Maybe.fromNullable( filteredSupportedLedgerFamiliesWithCurrency .get("ethereum") - ?.get("ethereum"), + ?.get("ethereum") ).extractNullable(); if (!defaultChain) throw new Error("Default chain not found"); @@ -204,7 +204,7 @@ const createLedgerLiveConnector = ({ } return { type: "accountId", accountId: accId } as const; - }), + }) ); const accountWithChain = preferredAccount @@ -215,15 +215,15 @@ const createLedgerLiveConnector = ({ } return v.account.id === pa.accountId; - }, accountsWithChain), + }, accountsWithChain) ) .altLazy(() => Maybe.fromNullable(queryParams.network).chain((network) => List.find( (v) => v.chainItem.skChainName === network, - accountsWithChain, - ), - ), + accountsWithChain + ) + ) ) .altLazy(() => List.head(accountsWithChain)) .toEither(new Error("Account not found")) @@ -253,7 +253,7 @@ const createLedgerLiveConnector = ({ Maybe.fromNullable(currentChain) .toEither(new Error("Current chain not found")) .map((val) => - ledgerAccounts.filter((a) => a.currency === val.currencyId), + ledgerAccounts.filter((a) => a.currency === val.currencyId) ); const onAccountsChanged: ReturnType["onAccountsChanged"] = @@ -266,7 +266,7 @@ const createLedgerLiveConnector = ({ }; const onChainChanged: ReturnType["onChainChanged"] = ( - chainId, + chainId ) => { config.emitter.emit("change", { chainId: skNormalizeChainId(chainId) }); }; @@ -304,22 +304,22 @@ const createLedgerLiveConnector = ({ const requestAndSwitchAccount = (chain: Chain) => EitherAsync.liftEither( Maybe.fromNullable( - filteredSkSupportedChainsToCurrencyIdMap?.get(chain.id), - ).toEither(new Error("Chain not found")), + filteredSkSupportedChainsToCurrencyIdMap?.get(chain.id) + ).toEither(new Error("Chain not found")) ) .chain((currencyId) => EitherAsync(() => - walletApiClient.account.request({ currencyIds: [currencyId] }), + walletApiClient.account.request({ currencyIds: [currencyId] }) ).mapLeft((e) => { console.log(e); return new Error("could not request account"); - }), + }) ) .chain((account) => { ledgerAccounts.push(account); $filteredChains.next([...$filteredChains.value, chain]); $disabledChains.next( - $disabledChains.value.filter((c) => c.id !== chain.id), + $disabledChains.value.filter((c) => c.id !== chain.id) ); return EitherAsync(() => switchChain({ chainId: chain.id })); }) diff --git a/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts b/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts index cda49ad4..2b3a1f50 100644 --- a/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts +++ b/packages/widget/src/providers/ledger/ledger-live-connector-meta.ts @@ -30,5 +30,5 @@ export type ExtraProps = ConnectorWithFilteredChains & { type LedgerLiveConnector = Connector & ExtraProps; export const isLedgerLiveConnector = ( - connector: Connector, + connector: Connector ): connector is LedgerLiveConnector => connector.id === configMeta.id; diff --git a/packages/widget/src/providers/ledger/utils.ts b/packages/widget/src/providers/ledger/utils.ts index ba83e4a9..2b49bb26 100644 --- a/packages/widget/src/providers/ledger/utils.ts +++ b/packages/widget/src/providers/ledger/utils.ts @@ -46,7 +46,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ return acc; }, - { accountsFamilies: new Set(), accountsCurrencies: new Set() }, + { accountsFamilies: new Set(), accountsCurrencies: new Set() } ); const v = typeSafeObjectEntries(supportedLedgerFamiliesWithCurrency).reduce( @@ -82,7 +82,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ if ( ledgerChainPriority.has( - item.skChainName as unknown as SupportedSKChains, + item.skChainName as unknown as SupportedSKChains ) ) { // biome-ignore lint: false @@ -95,7 +95,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ // biome-ignore lint: false return { ...acc, [k]: filtered }; }, - {} as MappedSupportedLedgerFamiliesWithCurrency, + {} as MappedSupportedLedgerFamiliesWithCurrency ); type V = typeof v; @@ -129,7 +129,7 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ enabled: boolean; } > - >(), + >() ); }; @@ -151,9 +151,9 @@ export const getLedgerCurrencies = (walletAPIClient: WalletAPIClient) => EitherAsync(() => walletAPIClient.currency.list({ currencyIds: Object.values(supportedLedgerFamiliesWithCurrency).flatMap( - (chain) => Object.values(chain).map((currency) => currency.currencyId), + (chain) => Object.values(chain).map((currency) => currency.currencyId) ), - }), + }) ) .map((val) => { return val.reduce( @@ -169,7 +169,7 @@ export const getLedgerCurrencies = (walletAPIClient: WalletAPIClient) => { cryptoCurrency: new Map(), tokenCurrency: [] } as { cryptoCurrency: Map; tokenCurrency: ERC20TokenCurrency[]; - }, + } ); }) .map((v) => { diff --git a/packages/widget/src/providers/misc/cardano-connector-meta.ts b/packages/widget/src/providers/misc/cardano-connector-meta.ts index 3a608f1b..becafdc6 100644 --- a/packages/widget/src/providers/misc/cardano-connector-meta.ts +++ b/packages/widget/src/providers/misc/cardano-connector-meta.ts @@ -21,5 +21,5 @@ export type StorageItem = { }; export const isCardanoConnector = ( - connector: Connector, + connector: Connector ): connector is CardanoConnector => connector.type === "cardanoWallet"; diff --git a/packages/widget/src/providers/misc/cardano-connector.ts b/packages/widget/src/providers/misc/cardano-connector.ts index 16d3b2e9..e6027cca 100644 --- a/packages/widget/src/providers/misc/cardano-connector.ts +++ b/packages/widget/src/providers/misc/cardano-connector.ts @@ -79,13 +79,13 @@ const createCardanoConnector = ({ getChainId: async () => cardano.id, isAuthorized: async () => { const isDisconnected = await config.storage?.getItem( - "cardano.disconnected", + "cardano.disconnected" ); if (isDisconnected) return false; const lastConnectedWallet = await config.storage?.getItem( - "cardano.lastConnectedWallet", + "cardano.lastConnectedWallet" ); if (!lastConnectedWallet) return false; diff --git a/packages/widget/src/providers/misc/config.ts b/packages/widget/src/providers/misc/config.ts index 6c3099c7..324208cc 100644 --- a/packages/widget/src/providers/misc/config.ts +++ b/packages/widget/src/providers/misc/config.ts @@ -38,25 +38,25 @@ const queryFn = async ({ }>[]; }> => { const miscChainsEntries = typeSafeObjectEntries( - miscChainsMap, + miscChainsMap ).filter(([_, v]) => enabledNetworks.has(v.skChainName)); const filteredMiscChainsMap: Partial = typeSafeObjectFromEntries(miscChainsEntries); const miscChains = Object.values(filteredMiscChainsMap).map( - (val) => val.wagmiChain, + (val) => val.wagmiChain ); return Promise.all([ MaybeAsync.liftMaybe(Maybe.fromFalsy(filteredMiscChainsMap.tron)).chain( () => MaybeAsync(() => import("./tron-connector")).map((v) => - v.getTronConnectors({ forceWalletConnectOnly }), - ), + v.getTronConnectors({ forceWalletConnectOnly }) + ) ), MaybeAsync.liftMaybe( - Maybe.fromFalsy(filteredMiscChainsMap.solana && !config.env.isTestMode), + Maybe.fromFalsy(filteredMiscChainsMap.solana && !config.env.isTestMode) ).chain(() => MaybeAsync(() => import("./solana-connector")).map((v) => v.getSolanaConnectors({ @@ -64,19 +64,19 @@ const queryFn = async ({ wallets: solanaWallets, connection: solanaConnection, variant, - }), - ), + }) + ) ), MaybeAsync.liftMaybe(Maybe.fromFalsy(filteredMiscChainsMap.cardano)).chain( () => MaybeAsync(() => import("./cardano-connector")).map((v) => - v.getCardanoConnectors(), - ), + v.getCardanoConnectors() + ) ), MaybeAsync.liftMaybe(Maybe.fromFalsy(filteredMiscChainsMap.ton)).chain(() => MaybeAsync(() => import("./ton-connector")).map((v) => - v.getTonConnectors({ tonConnectManifestUrl }), - ), + v.getTonConnectors({ tonConnectManifestUrl }) + ) ), ]).then((connectors) => ({ miscChainsMap: filteredMiscChainsMap, @@ -86,14 +86,14 @@ const queryFn = async ({ }; export const getConfig = ( - opts: Parameters[0] & { queryClient: QueryClient }, + opts: Parameters[0] & { queryClient: QueryClient } ) => EitherAsync(() => opts.queryClient.fetchQuery({ staleTime, queryKey, queryFn: () => queryFn(opts), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get misc config"); diff --git a/packages/widget/src/providers/misc/solana-connector-meta.ts b/packages/widget/src/providers/misc/solana-connector-meta.ts index 1890b0c4..a5b1934e 100644 --- a/packages/widget/src/providers/misc/solana-connector-meta.ts +++ b/packages/widget/src/providers/misc/solana-connector-meta.ts @@ -49,6 +49,6 @@ export type StorageItem = { "solana.disconnected": boolean }; type SolanaConnector = Connector & ExtraProps; export const isSolanaConnector = ( - connector: Connector, + connector: Connector ): connector is SolanaConnector => !!("isSolanaConnector" in connector && connector.isSolanaConnector); diff --git a/packages/widget/src/providers/misc/solana-connector.ts b/packages/widget/src/providers/misc/solana-connector.ts index b83e4489..4a9982ec 100644 --- a/packages/widget/src/providers/misc/solana-connector.ts +++ b/packages/widget/src/providers/misc/solana-connector.ts @@ -57,14 +57,14 @@ const createSolanaConnector = ({ solanaTx = Transaction.from(buffer); } catch (legacyErr) { throw new Error( - `Failed to deserialize Solana transaction. VersionedTransaction error: ${versionedError instanceof Error ? versionedError.message : String(versionedError)}. Legacy Transaction error: ${legacyErr instanceof Error ? legacyErr.message : String(legacyErr)}`, + `Failed to deserialize Solana transaction. VersionedTransaction error: ${versionedError instanceof Error ? versionedError.message : String(versionedError)}. Legacy Transaction error: ${legacyErr instanceof Error ? legacyErr.message : String(legacyErr)}` ); } } const signed = await solanaWallet.adapter.sendTransaction( solanaTx, - connection, + connection ); return signed; }, @@ -100,7 +100,7 @@ const createSolanaConnector = ({ getChainId: async () => solana.id, isAuthorized: async () => { const isDisconnected = await config.storage?.getItem( - "solana.disconnected", + "solana.disconnected" ); if (isDisconnected) return false; @@ -149,7 +149,7 @@ export const getSolanaConnectors = ({ .filter((w) => variant === "porto" ? w.adapter instanceof WalletConnectWalletAdapter - : true, + : true ) .map((w) => () => ({ id: w.adapter.name, diff --git a/packages/widget/src/providers/misc/ton-connector-meta.ts b/packages/widget/src/providers/misc/ton-connector-meta.ts index fd4897bc..e1e50b36 100644 --- a/packages/widget/src/providers/misc/ton-connector-meta.ts +++ b/packages/widget/src/providers/misc/ton-connector-meta.ts @@ -17,5 +17,5 @@ export type StorageItem = { }; export const isTonConnector = ( - connector: Connector, + connector: Connector ): connector is TonConnector => connector.type === "tonWallet"; diff --git a/packages/widget/src/providers/misc/ton-connector.ts b/packages/widget/src/providers/misc/ton-connector.ts index 99bd0886..c468f1bd 100644 --- a/packages/widget/src/providers/misc/ton-connector.ts +++ b/packages/widget/src/providers/misc/ton-connector.ts @@ -25,7 +25,7 @@ import { const createTonConnector = ( walletDetailsParams: WalletDetailsParams, - manifestUrl: string | undefined, + manifestUrl: string | undefined ) => createConnector((config) => { const tonconnectUI = new TonConnectUI({ @@ -67,10 +67,10 @@ const createTonConnector = ( Either.encase(() => JSON.parse(tx)).chain((val) => unsignedTonTransactionTonConnectCodec .decode(val) - .mapLeft((e) => new Error(e)), - ), + .mapLeft((e) => new Error(e)) + ) ).then(({ message }) => - loadMessageRelaxed(Cell.fromBase64(message).beginParse()), + loadMessageRelaxed(Cell.fromBase64(message).beginParse()) ); const info = parsedTx.info as CommonMessageInfoRelaxedInternal; @@ -104,7 +104,7 @@ const createTonConnector = ( () => new Promise((resolve, reject) => { deferred = { resolve, reject }; - }), + }) ) .then((wallet) => { deferred = null; @@ -112,7 +112,7 @@ const createTonConnector = ( })); const userFriendlyAddress = toUserFriendlyAddress( - wallet.account.address, + wallet.account.address ); return { diff --git a/packages/widget/src/providers/misc/tron-connector-meta.ts b/packages/widget/src/providers/misc/tron-connector-meta.ts index 55c615bf..3b77f2b5 100644 --- a/packages/widget/src/providers/misc/tron-connector-meta.ts +++ b/packages/widget/src/providers/misc/tron-connector-meta.ts @@ -33,6 +33,6 @@ type TronConnector = Connector & ExtraProps; export type StorageItem = { "tron.disconnected": boolean }; export const isTronConnector = ( - connector: Connector, + connector: Connector ): connector is TronConnector => Object.values(configMeta).some((val) => val.id === connector.id); diff --git a/packages/widget/src/providers/misc/tron-connector.ts b/packages/widget/src/providers/misc/tron-connector.ts index 3482257f..1d0b809f 100644 --- a/packages/widget/src/providers/misc/tron-connector.ts +++ b/packages/widget/src/providers/misc/tron-connector.ts @@ -58,8 +58,8 @@ const createTronConnector = ({ return ( await EitherAsync.liftEither( Maybe.fromNullable([adapter.address as Address]).toEither( - new Error("No account found"), - ), + new Error("No account found") + ) ) ).unsafeCoerce(); }, diff --git a/packages/widget/src/providers/mount-animation/index.tsx b/packages/widget/src/providers/mount-animation/index.tsx index 5d8cf8c1..38c664a3 100644 --- a/packages/widget/src/providers/mount-animation/index.tsx +++ b/packages/widget/src/providers/mount-animation/index.tsx @@ -45,21 +45,21 @@ type ContextValue = { }; const MountAnimationContext = createContext( - undefined, + undefined ); const removeDelay = delayAPIRequests(); export const MountAnimationProvider = ({ children }: PropsWithChildren) => { const onMountAnimationCompleteRef = useSavedRef( - useSettings().onMountAnimationComplete, + useSettings().onMountAnimationComplete ); const { dashboardVariant } = useSettings(); const [state, dispatch] = useReducer( reducer, - dashboardVariant ? { earnPage: true, layout: true } : initialState(), + dashboardVariant ? { earnPage: true, layout: true } : initialState() ); useEffect(() => { @@ -81,7 +81,7 @@ export const MountAnimationProvider = ({ children }: PropsWithChildren) => { const value = useMemo( () => ({ dispatch, state, mountAnimationFinished }) satisfies ContextValue, - [mountAnimationFinished, state], + [mountAnimationFinished, state] ); return ( @@ -96,7 +96,7 @@ export const useMountAnimation = () => { if (!context) { throw new Error( - "useMountAnimation must be used within a MountAnimationProvider", + "useMountAnimation must be used within a MountAnimationProvider" ); } diff --git a/packages/widget/src/providers/pending-action-store/index.tsx b/packages/widget/src/providers/pending-action-store/index.tsx index 50d86346..6a612f39 100644 --- a/packages/widget/src/providers/pending-action-store/index.tsx +++ b/packages/widget/src/providers/pending-action-store/index.tsx @@ -37,7 +37,7 @@ const store = createStore({ }); const PendingActionStoreContext = createContext( - undefined, + undefined ); export const PendingActionStoreProvider = ({ children }: PropsWithChildren) => { @@ -53,7 +53,7 @@ export const usePendingActionStore = () => { if (!value) { throw new Error( - "usePendingActionStore must be used within a PendingActionProvider", + "usePendingActionStore must be used within a PendingActionProvider" ); } diff --git a/packages/widget/src/providers/query-client/index.tsx b/packages/widget/src/providers/query-client/index.tsx index 267901ca..de5bed17 100644 --- a/packages/widget/src/providers/query-client/index.tsx +++ b/packages/widget/src/providers/query-client/index.tsx @@ -33,7 +33,7 @@ export const useSKQueryClient = () => { if (!queryClient) { throw new Error( - "useSKQueryClient must be used within a QueryClientContextProvider", + "useSKQueryClient must be used within a QueryClientContextProvider" ); } diff --git a/packages/widget/src/providers/rainbow-kit.tsx b/packages/widget/src/providers/rainbow-kit.tsx index 7c8a584b..f637052e 100644 --- a/packages/widget/src/providers/rainbow-kit.tsx +++ b/packages/widget/src/providers/rainbow-kit.tsx @@ -43,7 +43,7 @@ export const RainbowKitProviderWithTheme = ({ const chainIdsToUse = useMemo( () => new Set(connectorChains.map((c) => c.id)), - [connectorChains], + [connectorChains] ); const disabledChains = useMemo( @@ -52,7 +52,7 @@ export const RainbowKitProviderWithTheme = ({ ...c, info: t("chain_modal.disabled_chain_info"), })), - [ledgerDisabledChains, t], + [ledgerDisabledChains, t] ); const hideDisconnect = useMemo( @@ -60,7 +60,7 @@ export const RainbowKitProviderWithTheme = ({ Maybe.fromNullable(connector) .map((c) => !shouldShowDisconnect(c)) .orDefault(true), - [connector], + [connector] ); const locale = useMemo(() => { diff --git a/packages/widget/src/providers/rainbow/index.tsx b/packages/widget/src/providers/rainbow/index.tsx index ff65b6d9..56af6c63 100644 --- a/packages/widget/src/providers/rainbow/index.tsx +++ b/packages/widget/src/providers/rainbow/index.tsx @@ -13,7 +13,7 @@ export const RainbowProvider = ({ children }: PropsWithChildren) => { .map((v) => v .filter((a) => a.address !== address) - .map((a) => formatAddress(a.address) as Address), + .map((a) => formatAddress(a.address) as Address) ) .orDefault([]); @@ -25,8 +25,8 @@ export const RainbowProvider = ({ children }: PropsWithChildren) => { Maybe.fromNullable(ledgerAccounts).ifJust((accounts) => List.find( (acc) => formatAddress(acc.address) === address, - accounts, - ).ifJust((acc) => onLedgerAccountChange?.(acc)), + accounts + ).ifJust((acc) => onLedgerAccountChange?.(acc)) ), }} > diff --git a/packages/widget/src/providers/root-element/index.tsx b/packages/widget/src/providers/root-element/index.tsx index 209ed0e4..03a71f50 100644 --- a/packages/widget/src/providers/root-element/index.tsx +++ b/packages/widget/src/providers/root-element/index.tsx @@ -4,7 +4,7 @@ import { rootSelector } from "../../styles/theme/ids"; import { MaybeDocument } from "../../utils/maybe-document"; const RootElementContext = createContext( - undefined, + undefined ); export const RootElementProvider = ({ children }: PropsWithChildren) => { @@ -12,7 +12,7 @@ export const RootElementProvider = ({ children }: PropsWithChildren) => { useLayoutEffect(() => { MaybeDocument.chainNullable( - (doc) => doc.querySelector(rootSelector) as HTMLElement, + (doc) => doc.querySelector(rootSelector) as HTMLElement ).ifJust((el) => setRootElement(el)); }, []); diff --git a/packages/widget/src/providers/safe/config.ts b/packages/widget/src/providers/safe/config.ts index 2205e49d..e7b24478 100644 --- a/packages/widget/src/providers/safe/config.ts +++ b/packages/widget/src/providers/safe/config.ts @@ -26,7 +26,7 @@ export const getConfig = (opts: { queryClient: QueryClient }) => staleTime, queryKey, queryFn: () => queryFn(), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get safe config"); diff --git a/packages/widget/src/providers/safe/safe-connector-meta.ts b/packages/widget/src/providers/safe/safe-connector-meta.ts index 6447b87e..6e53756d 100644 --- a/packages/widget/src/providers/safe/safe-connector-meta.ts +++ b/packages/widget/src/providers/safe/safe-connector-meta.ts @@ -25,5 +25,5 @@ export type ExtraProps = ConnectorWithFilteredChains & { type SafeConnector = Connector & ExtraProps; export const isSafeConnector = ( - connector: Connector, + connector: Connector ): connector is SafeConnector => connector.id === configMeta.id; diff --git a/packages/widget/src/providers/safe/safe-connector.ts b/packages/widget/src/providers/safe/safe-connector.ts index 27ac7b29..043fbbb5 100644 --- a/packages/widget/src/providers/safe/safe-connector.ts +++ b/packages/widget/src/providers/safe/safe-connector.ts @@ -51,7 +51,7 @@ function safe(parameters: { shimDisconnect?: boolean } = {}) { $filteredChains.next( Maybe.fromNullable(config.chains.find((c) => c.id === chainId)) .map((c) => [c]) - .orDefault([]), + .orDefault([]) ); if (!disconnect) { @@ -87,7 +87,7 @@ function safe(parameters: { shimDisconnect?: boolean } = {}) { const provider = await getProvider(); if (!provider) throw new ProviderNotFoundError(); return (await provider.request({ method: "eth_accounts" })).map( - getAddress, + getAddress ); }, async getProvider() { @@ -136,13 +136,13 @@ function safe(parameters: { shimDisconnect?: boolean } = {}) { $filteredChains, getTxStatus(txHash) { return EitherAsync(() => sdk.txs.getBySafeTxHash(txHash)).mapLeft( - () => new Error("Could not get transaction status"), + () => new Error("Could not get transaction status") ); }, txStatus: TransactionStatus, sendTransactions(args) { return EitherAsync(() => sdk.txs.send(args)).mapLeft( - () => new Error("Could not send transactions"), + () => new Error("Could not send transactions") ); }, }; diff --git a/packages/widget/src/providers/settings/index.tsx b/packages/widget/src/providers/settings/index.tsx index 5ac71ad2..da80a509 100644 --- a/packages/widget/src/providers/settings/index.tsx +++ b/packages/widget/src/providers/settings/index.tsx @@ -7,7 +7,7 @@ import utilaTranslations from "../../translation/English/utila-variant.json"; import type { SettingsContextType } from "./types"; export const SettingsContext = createContext( - undefined, + undefined ); export const SettingsContextProvider = ({ @@ -31,9 +31,9 @@ export const SettingsContextProvider = ({ Object.entries(value).map(([tokenString, innerValue]) => [ tokenString.toLowerCase(), innerValue, - ]), + ]) ), - ]), + ]) ) .map((entries) => Object.fromEntries(entries)) .extract() as typeof rest.preferredTokenYieldsPerNetwork; @@ -54,7 +54,7 @@ export const SettingsContextProvider = ({ "translation", utilaTranslations, true, - true, + true ); } @@ -79,7 +79,7 @@ export const useSettings = () => { if (!context) { throw new Error( - "useSettings must be used within a SettingsContextProvider", + "useSettings must be used within a SettingsContextProvider" ); } diff --git a/packages/widget/src/providers/sk-wallet/index.tsx b/packages/widget/src/providers/sk-wallet/index.tsx index 345b7e06..aeb91a11 100644 --- a/packages/widget/src/providers/sk-wallet/index.tsx +++ b/packages/widget/src/providers/sk-wallet/index.tsx @@ -116,10 +116,10 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { miscChainsMap: val.wagmiConfig.miscConfig.miscChainsMap, substrateChainsMap: val.wagmiConfig.substrateConfig.substrateChainsMap, - }), + }) ) .extractNullable(), - [chain, wagmiConfig.data], + [chain, wagmiConfig.data] ); const isConnected = _isConnected && !!address && !!connector && !!network; @@ -162,9 +162,9 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { EitherAsync.liftEither( !isConnected || !network || !connector || !address ? Left(new Error("No wallet connected")) - : Right({ conn: connector, network, address }), + : Right({ conn: connector, network, address }) ), - [connector, isConnected, network, address], + [connector, isConnected, network, address] ); const signTransaction = useCallback( @@ -181,23 +181,21 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { if (isLedgerLiveConnector(conn)) { return EitherAsync.liftEither( Maybe.fromNullable(ledgerCurrentAccountId).toEither( - new SendTransactionError(), - ), + new SendTransactionError() + ) ) .chain((val) => EitherAsync.liftEither( Either.encase(() => JSON.parse(tx)) .chain((parsedTx) => - Either.encase(() => - conn.deserializeTransaction(parsedTx), - ), + Either.encase(() => conn.deserializeTransaction(parsedTx)) ) - .mapLeft(() => new TransactionDecodeError()), + .mapLeft(() => new TransactionDecodeError()) ).map((deserializedTransaction) => ({ accountId: val, deserializedTransaction, - })), + })) ) .chain(({ accountId, deserializedTransaction }) => EitherAsync(() => @@ -206,12 +204,12 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { deserializedTransaction, Maybe.fromNullable(ledgerHwAppId) .map((v) => ({ hwAppId: v })) - .extract(), - ), + .extract() + ) ).mapLeft((e) => { console.log(e); return new SendTransactionError(); - }), + }) ) .map((val) => ({ signedTx: val, broadcasted: true })); } @@ -220,10 +218,10 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { return EitherAsync.liftEither( Either.encase(() => JSON.parse(tx)) .mapLeft(() => "Failed to parse tx") - .chain((val) => substratePayloadCodec.decode(val)), + .chain((val) => substratePayloadCodec.decode(val)) ) .chain((decodedPayload) => - conn.signTransaction({ ...decodedPayload, rawTx: tx }), + conn.signTransaction({ ...decodedPayload, rawTx: tx }) ) .map((signedTx) => ({ signedTx, broadcasted: false })) .chainLeft((e) => { @@ -238,14 +236,14 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { if (isCosmosConnector(conn)) { return EitherAsync.liftEither( Maybe.fromNullable(cosmosCW).toEither( - new Error("cosmosCW missing"), - ), + new Error("cosmosCW missing") + ) ) .chain((cw) => // We need to sign + broadcast as `walletconnect` cosmos client does not support `sendTx` conn .signTransaction({ cw, tx }) - .map((val) => ({ signedTx: val, broadcasted: false })), + .map((val) => ({ signedTx: val, broadcasted: false })) ) .mapLeft(() => new SendTransactionError()); } @@ -260,13 +258,13 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft((e) => { console.log(e); return new TransactionDecodeError(); - }), + }) ) .chain((val) => EitherAsync(() => conn.signTransaction(val)).mapLeft((e) => { console.log(e); return new SendTransactionError(); - }), + }) ) .map((val) => ({ signedTx: JSON.stringify(val), @@ -285,7 +283,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { return Either.encase(() => JSON.parse(tx)) .mapLeft(() => "Failed to parse tx") .chain((val) => - decodeAndPrepareEvmTransaction({ address, input: val }), + decodeAndPrepareEvmTransaction({ address, input: val }) ) .map((v) => ({ type: "evm", tx: v })); } @@ -315,7 +313,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft(() => "Failed to parse tx") .chain((val) => substratePayloadCodec.decode(val)) .map( - (v) => ({ type: "bittensor", tx: v }) as BittensorTx, + (v) => ({ type: "bittensor", tx: v }) as BittensorTx ); } @@ -324,7 +322,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft((e) => { console.log(e); return new TransactionDecodeError(); - }), + }) ) .chain((val) => conn @@ -332,22 +330,22 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft( (e) => new SendTransactionError( - typeof e === "string" ? e : undefined, - ), - ), + typeof e === "string" ? e : undefined + ) + ) ) .map((val) => ({ signedTx: val, broadcasted: true })); } if (isSolanaConnector(conn)) { return EitherAsync.liftEither( - unsignedSolanaTransactionCodec.decode(tx), + unsignedSolanaTransactionCodec.decode(tx) ) .mapLeft(() => new TransactionDecodeError()) .chain((decodedTx) => EitherAsync(() => conn.sendTransaction(decodedTx)) .ifLeft((e) => console.log(e)) - .mapLeft(() => new SendTransactionError()), + .mapLeft(() => new SendTransactionError()) ) .map((res) => ({ signedTx: res, broadcasted: true })); } @@ -373,9 +371,9 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { return EitherAsync.liftEither( Either.encase(() => JSON.parse(tx)) .chain((val) => - decodeAndPrepareEvmTransaction({ address, input: val }), + decodeAndPrepareEvmTransaction({ address, input: val }) ) - .mapLeft(() => new TransactionDecodeError()), + .mapLeft(() => new TransactionDecodeError()) ) .chain((tx) => conn @@ -388,7 +386,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { }, ], }) - .map((res) => res.safeTxHash), + .map((res) => res.safeTxHash) ) .chain((safeTxHash) => withRequestErrorRetry({ @@ -403,11 +401,11 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { res.txStatus === conn.txStatus.FAILED || res.txStatus === conn.txStatus.CANCELLED ? "FAILED" - : "NOT_READY", - ), - ), + : "NOT_READY" + ) + ) ) - : EitherAsync.liftEither(Right(res.txHash)), + : EitherAsync.liftEither(Right(res.txHash)) ) .run() .then((res) => res.unsafeCoerce()), @@ -416,13 +414,13 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .chainNullable((e) => (e as SafeFailedError)._tag === "SafeFailedError" ? (e as SafeFailedError) - : null, + : null ) .filter((e) => e.type !== "FAILED" && !checkIsUnmounted()) .map(() => retryCount < 120) .orDefault(false), retryWaitForMs: () => 7000, - }), + }) ) .mapLeft(() => new SendTransactionError()) .map((val) => ({ signedTx: val as Hash, broadcasted: true })); @@ -437,7 +435,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { .mapLeft((e) => { console.log(e); return new TransactionDecodeError(); - }), + }) ).chain((val) => EitherAsync(() => /** @@ -453,10 +451,10 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { chainId: val.chainId, gas: val.gasLimit, type: val.maxFeePerGas ? "eip1559" : "legacy", - }), + }) ) .mapLeft((e) => new SendTransactionError(e)) - .map((val) => ({ signedTx: val, broadcasted: true })), + .map((val) => ({ signedTx: val, broadcasted: true })) ); }), [ @@ -465,7 +463,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { ledgerCurrentAccountId, sendTransactionAsync, checkIsUnmounted, - ], + ] ); const signMessage = useCallback( @@ -482,7 +480,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { console.log(e); return new Error("sign failed"); }), - [connectorDetails, signMessageAsync], + [connectorDetails, signMessageAsync] ); const onLedgerAccountChange = useCallback( @@ -491,7 +489,7 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => { connector.switchAccount(account); } }, - [connector], + [connector] ); const value = useMemo((): SKWallet => { diff --git a/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts b/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts index 0434538f..10d88740 100644 --- a/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts +++ b/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts @@ -49,7 +49,7 @@ const getAdditionalAddresses = (args: { return getCosmosPubKey(args).map( (pubKey): AddressWithTokenDtoAdditionalAddresses => ({ cosmosPubKey: pubKey, - }), + }) ); } @@ -67,8 +67,8 @@ const getCosmosPubKey = (args: { return EitherAsync(() => args.connector.getAccounts()) .chain((accs) => EitherAsync.liftEither( - List.head(accs).toEither(new Error("no account")), - ), + List.head(accs).toEither(new Error("no account")) + ) ) .chain((acc) => { const skPubKey = prevSkPubKeys[acc]; @@ -82,7 +82,7 @@ const getCosmosPubKey = (args: { }) .chainLeft(() => EitherAsync(() => - args.chainWallet.client.getAccount?.(args.chainWallet.chainId), + args.chainWallet.client.getAccount!(args.chainWallet.chainId) ) .mapLeft((e) => { console.log("missing account error: ", e); @@ -90,5 +90,5 @@ const getCosmosPubKey = (args: { }) .map((account) => { return args.connector.toBase64(account.pubkey); - }), + }) ); diff --git a/packages/widget/src/providers/sk-wallet/use-connector-chains.ts b/packages/widget/src/providers/sk-wallet/use-connector-chains.ts index d406c62d..48ffc765 100644 --- a/packages/widget/src/providers/sk-wallet/use-connector-chains.ts +++ b/packages/widget/src/providers/sk-wallet/use-connector-chains.ts @@ -27,7 +27,7 @@ export const useConnectorChains = ({ return () => sub.unsubscribe(); }, - [connector, subject], + [connector, subject] ); const getSnapshot = useCallback(() => { diff --git a/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts b/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts index 051526e5..5ed6df32 100644 --- a/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts +++ b/packages/widget/src/providers/sk-wallet/use-cosmos-cw.ts @@ -6,7 +6,7 @@ import { isCosmosConnector } from "../cosmos/cosmos-connector-meta"; export const useCosmosCW = (connector?: Connector) => { const [subject] = useState( - () => new BehaviorSubject(null), + () => new BehaviorSubject(null) ); const subscribe = useCallback( @@ -22,7 +22,7 @@ export const useCosmosCW = (connector?: Connector) => { return () => sub.unsubscribe(); }, - [connector, subject], + [connector, subject] ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-init.ts b/packages/widget/src/providers/sk-wallet/use-init.ts index 8312e9de..a2a32887 100644 --- a/packages/widget/src/providers/sk-wallet/use-init.ts +++ b/packages/widget/src/providers/sk-wallet/use-init.ts @@ -43,16 +43,16 @@ export const useInit = () => { return EitherAsync.liftEither( List.find( (c) => c.id === "injected" || c.id === safeConfigMeta.id, - config.connectors as Connector[], - ).toEither(new Error("Could not find injected connector")), + config.connectors as Connector[] + ).toEither(new Error("Could not find injected connector")) ) .chain((injConnector) => EitherAsync(() => connect(config, { connector: injConnector, chainId: queryParamsInitChainId, - }), - ), + }) + ) ) .chainLeft(async () => Right(null)); }) @@ -65,7 +65,7 @@ export const useInit = () => { config.state.chainId !== queryParamsInitChainId ) { return EitherAsync(() => - switchChain(config, { chainId: queryParamsInitChainId }), + switchChain(config, { chainId: queryParamsInitChainId }) ).chainLeft(async () => Right(null)); } diff --git a/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts b/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts index db3fe625..948ee68b 100644 --- a/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts +++ b/packages/widget/src/providers/sk-wallet/use-ledger-accounts.ts @@ -20,7 +20,7 @@ export const useLedgerAccounts = (connector?: Connector) => { return () => sub.unsubscribe(); }, - [connector, subject], + [connector, subject] ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts b/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts index 03b97208..128a4814 100644 --- a/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts +++ b/packages/widget/src/providers/sk-wallet/use-ledger-current-account-id.ts @@ -5,7 +5,7 @@ import { isLedgerLiveConnector } from "../ledger/ledger-live-connector-meta"; export const useLedgerCurrentAccountId = (connector?: Connector) => { const [subject] = useState( - () => new BehaviorSubject(undefined), + () => new BehaviorSubject(undefined) ); const subscribe = useCallback( @@ -21,7 +21,7 @@ export const useLedgerCurrentAccountId = (connector?: Connector) => { return () => sub.unsubscribe(); }, - [connector, subject], + [connector, subject] ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts b/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts index d32811d4..f73b8026 100644 --- a/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts +++ b/packages/widget/src/providers/sk-wallet/use-ledger-disabled-chains.ts @@ -21,7 +21,7 @@ export const useLedgerDisabledChain = (connector?: Nullable) => { return () => sub.unsubscribe(); }, - [connector, subject], + [connector, subject] ); const getSnapshot = useCallback(() => subject.value, [subject]); diff --git a/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts b/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts index d61999d7..84b8561e 100644 --- a/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts +++ b/packages/widget/src/providers/sk-wallet/use-sync-external-provider.ts @@ -28,9 +28,9 @@ export const useSyncExternalProvider = ({ () => List.find( (c) => isExternalProviderConnector(c), - connectors as Connector[], + connectors as Connector[] ).filter(isExternalProviderConnector), - [connectors], + [connectors] ); const connectRef = useSavedRef(connect); @@ -46,7 +46,7 @@ export const useSyncExternalProvider = ({ } externalProviderConnector.ifJust((val) => - connectRef.current({ connector: val }), + connectRef.current({ connector: val }) ); }, [ isConnected, @@ -59,7 +59,7 @@ export const useSyncExternalProvider = ({ useUpdateEffect(() => { connectorRef.current .chain((conn) => - Maybe.fromNullable(chainRef.current).map((c) => ({ c, conn })), + Maybe.fromNullable(chainRef.current).map((c) => ({ c, conn })) ) .ifJust((val) => { val.conn.onSupportedChainsChanged({ diff --git a/packages/widget/src/providers/stake-history/index.tsx b/packages/widget/src/providers/stake-history/index.tsx index b7308421..d8a3dd0a 100644 --- a/packages/widget/src/providers/stake-history/index.tsx +++ b/packages/widget/src/providers/stake-history/index.tsx @@ -27,7 +27,7 @@ export const ActionHistoryContextProvider = ({ const value = useMemo( () => [data, setActionHistoryData] as const, - [data, setActionHistoryData], + [data, setActionHistoryData] ); return ( @@ -42,7 +42,7 @@ const useActionHistory = () => { if (context === undefined) { throw new Error( - "useActionHistory must be used within a ActionHistoryContextProvider", + "useActionHistory must be used within a ActionHistoryContextProvider" ); } diff --git a/packages/widget/src/providers/substrate/config.ts b/packages/widget/src/providers/substrate/config.ts index 9659b0ed..27f1e4f1 100644 --- a/packages/widget/src/providers/substrate/config.ts +++ b/packages/widget/src/providers/substrate/config.ts @@ -40,12 +40,12 @@ const queryFn = async ({ const filteredSubstrateChainsMap: Partial = typeSafeObjectFromEntries( typeSafeObjectEntries(substrateChainsMap).filter( - ([_, v]) => networks.has(v.skChainName), - ), + ([_, v]) => networks.has(v.skChainName) + ) ); const substrateChains = Object.values(filteredSubstrateChainsMap).map( - (val) => val.wagmiChain, + (val) => val.wagmiChain ); const lunoKitChains = Object.values(filteredSubstrateChainsMap).map( @@ -61,7 +61,7 @@ const queryFn = async ({ : getNetworkLogo(val.skChainName), genesisHash: val.genesisHash as `0x${string}`, ss58Format: val.ss58Format, - }), + }) ); return Promise.resolve({ @@ -71,8 +71,8 @@ const queryFn = async ({ getSubstrateConnectors( substrateChains, lunoKitChains, - forceWalletConnectOnly, - ), + forceWalletConnectOnly + ) ), }); }, @@ -85,7 +85,7 @@ export const getConfig = (opts: Parameters[0]) => staleTime, queryKey, queryFn: () => queryFn(opts), - }), + }) ).mapLeft((e) => { console.log(e); return new Error("Could not get substrate config"); diff --git a/packages/widget/src/providers/substrate/substrate-connector-meta.ts b/packages/widget/src/providers/substrate/substrate-connector-meta.ts index 58b65fd0..7f51a049 100644 --- a/packages/widget/src/providers/substrate/substrate-connector-meta.ts +++ b/packages/widget/src/providers/substrate/substrate-connector-meta.ts @@ -21,5 +21,5 @@ export type StorageItem = { type SubstrateConnector = Connector & ExtraProps; export const isSubstrateConnector = ( - connector: Connector, + connector: Connector ): connector is SubstrateConnector => connector.type === configMeta.type; diff --git a/packages/widget/src/providers/substrate/substrate-connector.ts b/packages/widget/src/providers/substrate/substrate-connector.ts index 96acf9c9..882eb889 100644 --- a/packages/widget/src/providers/substrate/substrate-connector.ts +++ b/packages/widget/src/providers/substrate/substrate-connector.ts @@ -57,13 +57,13 @@ const createSubstrateConnector = ({ .chain((signer) => EitherAsync.liftEither( Maybe.fromNullable(signer?.signPayload?.bind(signer)).toEither( - new Error("signer missing"), - ), + new Error("signer missing") + ) ) .chain((signPayload) => EitherAsync(() => - signPayload({ ...payload.tx, withSignedTransaction: true }), - ), + signPayload({ ...payload.tx, withSignedTransaction: true }) + ) ) .chain((res) => { if (res.signedTransaction) { @@ -71,8 +71,8 @@ const createSubstrateConnector = ({ Right( typeof res.signedTransaction === "string" ? res.signedTransaction - : u8aToHex(res.signedTransaction), - ), + : u8aToHex(res.signedTransaction) + ) ); } @@ -81,34 +81,34 @@ const createSubstrateConnector = ({ const registry = new TypeRegistry(); registry.setMetadata( - registry.createType("Metadata", payload.metadataRpc), + registry.createType("Metadata", payload.metadataRpc) ); const extrinsic = registry.createType( "Extrinsic", { method: payload.tx.method }, - { version: payload.tx.version }, + { version: payload.tx.version } ); extrinsic.addSignature( payload.tx.address, res.signature, - payload.tx, + payload.tx ); return u8aToHex(extrinsic.toU8a()); - }), + }) ); - }), + }) ) .mapLeft( - (e) => new Error("Failed to sign transaction", { cause: e }), + (e) => new Error("Failed to sign transaction", { cause: e }) ), connect: async (args) => { config.emitter.emit("message", { type: "connecting" }); baseConnector.once("get_uri", (uri: string) => - baseConnector.emit("display_uri", uri), + baseConnector.emit("display_uri", uri) ); const accounts = await baseConnector.connect(name, lunoKitChains); @@ -150,13 +150,13 @@ const createSubstrateConnector = ({ getChainId: async () => $filteredChains.getValue()[0].id, isAuthorized: async () => { const isDisconnected = await config.storage?.getItem( - "substrate.disconnected", + "substrate.disconnected" ); if (isDisconnected) return false; const lastConnectedId = await config.storage?.getItem( - "substrate.lastConnectedId", + "substrate.lastConnectedId" ); return !!(lastConnectedId && lastConnectedId === baseConnector.id); @@ -184,7 +184,7 @@ const createSubstrateConnector = ({ export const getSubstrateConnectors = ( chains: ReadonlyArray, lunoKitChains: LunoKitChain[], - forceWalletConnectOnly: boolean, + forceWalletConnectOnly: boolean ): WalletList[number] => { const subwallet = subwalletConnector(); const talisman = talismanConnector(); diff --git a/packages/widget/src/providers/theme-wrapper.tsx b/packages/widget/src/providers/theme-wrapper.tsx index e6cd891c..2f0ec63b 100644 --- a/packages/widget/src/providers/theme-wrapper.tsx +++ b/packages/widget/src/providers/theme-wrapper.tsx @@ -45,7 +45,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { return merge( structuredClone(lightTheme), theme.lightMode, - overrides, + overrides ); } @@ -56,7 +56,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { return lightTheme; }) .unsafeCoerce(), - [theme, variant], + [theme, variant] ); const finalDarkTheme = useMemo( @@ -64,7 +64,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { "darkMode" in theme ? merge(structuredClone(darkTheme), theme.darkMode) : null, - [theme], + [theme] ); return ( @@ -80,7 +80,7 @@ export const ThemeWrapper = ({ children }: PropsWithChildren) => { finalDarkTheme ? `@media (prefers-color-scheme: dark) { ${rootSelector} {${assignInlineVars( vars, - finalDarkTheme, + finalDarkTheme )}} }` : null, ].join(""), diff --git a/packages/widget/src/providers/tracking/index.tsx b/packages/widget/src/providers/tracking/index.tsx index 5d4d513c..5649ac47 100644 --- a/packages/widget/src/providers/tracking/index.tsx +++ b/packages/widget/src/providers/tracking/index.tsx @@ -74,7 +74,7 @@ type TrackingContextType = { }; export const TrackingContext = createContext( - undefined, + undefined ); export const TrackingContextProvider = ({ @@ -90,10 +90,10 @@ export const TrackingContextProvider = ({ tracking?.trackEvent?.(trackEventMap[event], ...(props ? [props] : [])); variantTracking?.trackEvent?.( trackEventMap[event], - ...(props ? [props] : []), + ...(props ? [props] : []) ); }, - [tracking, variantTracking], + [tracking, variantTracking] ); const trackPageView = useCallback( @@ -101,15 +101,15 @@ export const TrackingContextProvider = ({ tracking?.trackPageView?.(trackPageMap[page], ...(props ? [props] : [])); variantTracking?.trackPageView?.( trackPageMap[page], - ...(props ? [props] : []), + ...(props ? [props] : []) ); }, - [tracking, variantTracking], + [tracking, variantTracking] ); const value = useMemo( () => ({ trackEvent, trackPageView }), - [trackEvent, trackPageView], + [trackEvent, trackPageView] ); return ( diff --git a/packages/widget/src/providers/wagmi/index.ts b/packages/widget/src/providers/wagmi/index.ts index e5968f79..381ade10 100644 --- a/packages/widget/src/providers/wagmi/index.ts +++ b/packages/widget/src/providers/wagmi/index.ts @@ -133,13 +133,13 @@ const buildWagmiConfig = async (opts: { miscConfig: m, substrateConfig: s, queryParams: qp, - })), - ), - ), - ), - ), - ), - ), + })) + ) + ) + ) + ) + ) + ) ) .chain((val) => getLedgerLiveConfig({ @@ -151,13 +151,13 @@ const buildWagmiConfig = async (opts: { }, queryClient: opts.queryClient, queryParams: val.queryParams, - }).map((l) => ({ ...val, ledgerLiveConnector: l })), + }).map((l) => ({ ...val, ledgerLiveConnector: l })) ) .chain((val) => EitherAsync.liftEither(Maybe.fromFalsy(opts.isSafe).toEither(null)) .chain(() => getSafeConnector({ queryClient: opts.queryClient })) .chainLeft((e) => EitherAsync.liftEither(e ? Left(e) : Right(null))) - .map((s) => ({ ...val, safeConnector: s })), + .map((s) => ({ ...val, safeConnector: s })) ) .map((val) => { const { @@ -272,9 +272,9 @@ const buildWagmiConfig = async (opts: { : wallet; return maybeMapped; - }, + } ), - })), + })) ) .map((walletList) => opts.mapWalletListFn?.(walletList) ?? walletList) .map((walletList) => { @@ -296,7 +296,7 @@ const buildWagmiConfig = async (opts: { : config.chains, }), }; - }, + } ), })); }) @@ -310,7 +310,7 @@ const buildWagmiConfig = async (opts: { val.miscConfig.miscChainsMap[n as keyof MiscChainsMap] ?? val.substrateConfig.substrateChainsMap[ n as keyof SubstrateChainsMap - ], + ] ) .map((c) => c.wagmiChain.id) .extract(); @@ -331,11 +331,11 @@ const buildWagmiConfig = async (opts: { ...prev, ...uniqwith( mipdStore.getProviders(), - (a, b) => a.info.rdns === b.info.rdns, + (a, b) => a.info.rdns === b.info.rdns ).map((p) => ({ rkDetails: { chainGroup: evmChainGroup }, ...wagmiConfig._internal.connectors.setup( - wagmiConfig._internal.connectors.providerDetailToConnector(p), + wagmiConfig._internal.connectors.providerDetailToConnector(p) ), })), ]); @@ -347,9 +347,9 @@ const buildWagmiConfig = async (opts: { (p) => ({ rkDetails: { chainGroup: evmChainGroup }, ...wagmiConfig._internal.connectors.setup( - wagmiConfig._internal.connectors.providerDetailToConnector(p), + wagmiConfig._internal.connectors.providerDetailToConnector(p) ), - }), + }) ), ]); }); @@ -400,7 +400,7 @@ export const useWagmiConfig = () => { i18n, url: yieldsApiUrl ?? config.env.yieldsApiUrl, }), - [apiKey, i18n, yieldsApiUrl], + [apiKey, i18n, yieldsApiUrl] ); const externalProvidersRef = useSavedRef(externalProviders) as diff --git a/packages/widget/src/providers/wagmi/utils.ts b/packages/widget/src/providers/wagmi/utils.ts index 51b075f3..e81efadb 100644 --- a/packages/widget/src/providers/wagmi/utils.ts +++ b/packages/widget/src/providers/wagmi/utils.ts @@ -26,7 +26,7 @@ export const createWallet = isWalletConnect?: never; projectId?: never; } - ), + ) ): WalletList[number]["wallets"][number] => () => { const def = { diff --git a/packages/widget/src/providers/yield-api-client-provider/actions.ts b/packages/widget/src/providers/yield-api-client-provider/actions.ts index ee15c651..fae4cb40 100644 --- a/packages/widget/src/providers/yield-api-client-provider/actions.ts +++ b/packages/widget/src/providers/yield-api-client-provider/actions.ts @@ -3,71 +3,48 @@ import type { YieldCreateActionDto, YieldCreateManageActionDto, } from "../../domain/types/action"; -import type { AddressesDto } from "../../domain/types/addresses"; import type { YieldApiFetchClient } from "../../domain/types/yield-api"; -import type { Yield } from "../../domain/types/yields"; import { getResponseData } from "./request-helpers"; export const createEnterAction = async ({ - addresses, fetchClient, requestDto, - yieldDto, }: { - addresses: AddressesDto; fetchClient: YieldApiFetchClient; requestDto: YieldCreateActionDto; - yieldDto: Yield; }): Promise => { - void addresses; - void yieldDto; - return getResponseData( fetchClient.POST("/v1/actions/enter", { body: requestDto, - }), + }) ); }; export const createExitAction = async ({ - addresses, fetchClient, requestDto, - yieldDto, }: { - addresses: AddressesDto; fetchClient: YieldApiFetchClient; requestDto: YieldCreateActionDto; - yieldDto: Yield; }): Promise => { - void addresses; - void yieldDto; - return getResponseData( fetchClient.POST("/v1/actions/exit", { body: requestDto, - }), + }) ); }; export const createManageAction = async ({ - addresses, fetchClient, requestDto, - yieldDto, }: { - addresses: AddressesDto; fetchClient: YieldApiFetchClient; requestDto: YieldCreateManageActionDto; - yieldDto: Yield; }): Promise => { - void addresses; - void yieldDto; - return getResponseData( fetchClient.POST("/v1/actions/manage", { body: requestDto, - }), + }) ); }; @@ -91,7 +68,7 @@ export const listActions = async ({ limit, }, }, - }), + }) ); export const getTransaction = async ({ @@ -108,7 +85,7 @@ export const getTransaction = async ({ transactionId, }, }, - }), + }) ); export const submitTransaction = async ({ @@ -130,7 +107,7 @@ export const submitTransaction = async ({ body: { signedTransaction, }, - }), + }) ); export const submitTransactionHash = async ({ @@ -152,5 +129,5 @@ export const submitTransactionHash = async ({ body: { hash, }, - }), + }) ); diff --git a/packages/widget/src/providers/yield-api-client-provider/index.tsx b/packages/widget/src/providers/yield-api-client-provider/index.tsx index d52f1d23..9c35b670 100644 --- a/packages/widget/src/providers/yield-api-client-provider/index.tsx +++ b/packages/widget/src/providers/yield-api-client-provider/index.tsx @@ -14,7 +14,7 @@ import type { paths } from "../../types/yield-api-schema"; import { useSettings } from "../settings"; const QueryContext = createContext | undefined>( - undefined, + undefined ); const FetchContext = createContext(undefined); @@ -104,7 +104,7 @@ export const useYieldApiClient = () => { if (!value) { throw new Error( - "useYieldApiClient must be used within a YieldApiClientProvider", + "useYieldApiClient must be used within a YieldApiClientProvider" ); } @@ -116,7 +116,7 @@ export const useYieldApiFetchClient = () => { if (!value) { throw new Error( - "useYieldApiFetchClient must be used within a YieldApiClientProvider", + "useYieldApiFetchClient must be used within a YieldApiClientProvider" ); } diff --git a/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts index 6ba78bb0..ca233251 100644 --- a/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts +++ b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts @@ -22,7 +22,7 @@ export const getResponseData = async < error?: unknown; }, >( - promise: Promise, + promise: Promise ): Promise> => { const response = await promise; diff --git a/packages/widget/src/services/local-storage.ts b/packages/widget/src/services/local-storage.ts index 25f0a27c..097b1376 100644 --- a/packages/widget/src/services/local-storage.ts +++ b/packages/widget/src/services/local-storage.ts @@ -20,7 +20,7 @@ type LocalStorageValue = GetType< >; export const getStorageItem = ( - key: K, + key: K ): Either | null> => { const w = MaybeWindow.extractNullable(); @@ -36,23 +36,23 @@ export const getStorageItem = ( codecs[key] .decode(parsedVal) .map((val) => val as LocalStorageValue) - .mapLeft((e) => new Error(e)), + .mapLeft((e) => new Error(e)) ); }; export const setStorageItem = ( key: K, - value: GetType<(typeof codecs)[K]>, + value: GetType<(typeof codecs)[K]> ) => { const w = MaybeWindow.extractNullable(); return Either.encase(() => - w?.localStorage.setItem(key, JSON.stringify(value)), + w?.localStorage.setItem(key, JSON.stringify(value)) ).ifRight(() => notify(key)); }; type Listener = ( - val: GetType<(typeof codecs)[K]>, + val: GetType<(typeof codecs)[K]> ) => void; const listeners: { [Key in keyof LocalStorageKV]: Map } = { @@ -62,7 +62,7 @@ const listeners: { [Key in keyof LocalStorageKV]: Map } = { export const addLocalStorageListener = ( key: K, - listener: Listener, + listener: Listener ) => { listeners[key].set(listener, listener); @@ -73,6 +73,6 @@ const notify = (key: keyof LocalStorageKV) => { getStorageItem(key).ifRight((val) => listeners[key].forEach((listener) => { listener(val as GetType<(typeof codecs)[typeof key]>); - }), + }) ); }; diff --git a/packages/widget/src/styles/theme/atoms.css.ts b/packages/widget/src/styles/theme/atoms.css.ts index f0486614..7158d99e 100644 --- a/packages/widget/src/styles/theme/atoms.css.ts +++ b/packages/widget/src/styles/theme/atoms.css.ts @@ -21,7 +21,7 @@ const responsiveAtomicProperties = defineProperties({ return acc; }, - {} as { [Key in Breakpoint]: { "@media"?: string } }, + {} as { [Key in Breakpoint]: { "@media"?: string } } ), defaultCondition: "mobile", @@ -51,7 +51,7 @@ const colorAtomicProperties = defineProperties({ export const atoms = createSprinkles( unresponsiveAtomicProperties, responsiveAtomicProperties, - colorAtomicProperties, + colorAtomicProperties ); export type Atoms = Parameters[0]; diff --git a/packages/widget/src/styles/theme/contract.css.ts b/packages/widget/src/styles/theme/contract.css.ts index 08481453..1749dde9 100644 --- a/packages/widget/src/styles/theme/contract.css.ts +++ b/packages/widget/src/styles/theme/contract.css.ts @@ -31,5 +31,5 @@ export const vars = createGlobalThemeContract( `sk-${path .join("-") .replace(/([a-z])([A-Z])/g, "$1-$2") - .toLowerCase()}`, + .toLowerCase()}` ); diff --git a/packages/widget/src/styles/theme/global.css.ts b/packages/widget/src/styles/theme/global.css.ts index a9a61f1b..23b2b73a 100644 --- a/packages/widget/src/styles/theme/global.css.ts +++ b/packages/widget/src/styles/theme/global.css.ts @@ -58,7 +58,7 @@ globalStyle( margin: "0", }, }, - }, + } ); globalStyle(`${rootSelector} input[type=number]`, { @@ -78,7 +78,7 @@ globalStyle( height: "auto", }, }, - }, + } ); globalStyle("button.simple-display-wallet-button", { @@ -105,7 +105,7 @@ globalStyle( { maxHeight: "80vh", overflow: "scroll", - }, + } ); globalStyle( @@ -113,7 +113,7 @@ globalStyle( { width: 0, height: 0, - }, + } ); globalStyle( @@ -121,7 +121,7 @@ globalStyle( { width: 0, height: 0, - }, + } ); globalStyle( @@ -129,12 +129,12 @@ globalStyle( { width: 0, height: 0, - }, + } ); globalStyle( '[data-rk="stakekit"][aria-labelledby="rk_connect_title"][aria-modal="true"][role="dialog"]', { zIndex: 990, - }, + } ); diff --git a/packages/widget/src/styles/tokens/breakpoints.ts b/packages/widget/src/styles/tokens/breakpoints.ts index 1421389b..124690d4 100644 --- a/packages/widget/src/styles/tokens/breakpoints.ts +++ b/packages/widget/src/styles/tokens/breakpoints.ts @@ -11,6 +11,6 @@ export const minMediaQuery = (breakpoint: Breakpoint) => export const minContainerWidth = ( containerName: string, - breakpoint: Breakpoint | number, + breakpoint: Breakpoint | number ) => `${containerName} (min-width: ${typeof breakpoint === "number" ? breakpoint : breakpoints[breakpoint]}px)`; diff --git a/packages/widget/src/translation/index.ts b/packages/widget/src/translation/index.ts index dd2e0073..74d5bc46 100644 --- a/packages/widget/src/translation/index.ts +++ b/packages/widget/src/translation/index.ts @@ -39,7 +39,7 @@ i18nInstance.on("languageChanged", (lng) => { }); i18nInstance.services.formatter?.add("lowercase", (value, _, __) => - value.toLowerCase(), + value.toLowerCase() ); export const useLoadErrorTranslations = () => { @@ -57,12 +57,12 @@ export const useLoadErrorTranslations = () => { ( await EitherAsync(() => apiClient.get>( - `https://i18n.stakek.it/locales/${lng}/errors.json`, - ), + `https://i18n.stakek.it/locales/${lng}/errors.json` + ) ).ifRight((res) => i18n.addResourceBundle(i18n.language, "translation", { errors: res.data, - }), + }) ) ).unsafeCoerce(), }); diff --git a/packages/widget/src/types/purify-extend.d.ts b/packages/widget/src/types/purify-extend.d.ts index ff8f41db..4ccd3b05 100644 --- a/packages/widget/src/types/purify-extend.d.ts +++ b/packages/widget/src/types/purify-extend.d.ts @@ -6,7 +6,7 @@ module "purify-ts" { interface MaybeTypeRef extends OriginalMaybeTypeRef { fromRecord>>( - val: T, + val: T ): Maybe<{ [Key in keyof T]: GetMaybeJust }>; } diff --git a/packages/widget/src/utils/create-state-context.ts b/packages/widget/src/utils/create-state-context.ts index 58d68deb..0b9baa9f 100644 --- a/packages/widget/src/utils/create-state-context.ts +++ b/packages/widget/src/utils/create-state-context.ts @@ -6,7 +6,7 @@ const createStateContext = (defaultInitialValue: T) => { >(undefined); const providerFactory = ( props: { value: [T, React.Dispatch>] }, - children: React.ReactNode, + children: React.ReactNode ) => createElement(context.Provider, props, children); const StateProvider = ({ @@ -17,7 +17,7 @@ const createStateContext = (defaultInitialValue: T) => { initialValue?: T; }) => { const state = useState( - initialValue !== undefined ? initialValue : defaultInitialValue, + initialValue !== undefined ? initialValue : defaultInitialValue ); return providerFactory({ value: state }, children); }; diff --git a/packages/widget/src/utils/date.ts b/packages/widget/src/utils/date.ts index 5430d809..53a337c3 100644 --- a/packages/widget/src/utils/date.ts +++ b/packages/widget/src/utils/date.ts @@ -35,7 +35,7 @@ export const formatDurationUntilDate = (futureDate: Date) => { return formatDuration( { days, hours, minutes, seconds }, - { format: getFormat({ days, hours, minutes }) }, + { format: getFormat({ days, hours, minutes }) } ); }; diff --git a/packages/widget/src/utils/extend-purify.ts b/packages/widget/src/utils/extend-purify.ts index 6753589d..714ba898 100644 --- a/packages/widget/src/utils/extend-purify.ts +++ b/packages/widget/src/utils/extend-purify.ts @@ -2,7 +2,7 @@ import { Just, Maybe, Nothing } from "purify-ts"; import type { GetMaybeJust } from "../types/utils"; Maybe.fromRecord = >>( - val: T, + val: T ): Maybe<{ [Key in keyof T]: GetMaybeJust }> => { const result = {} as { [Key in keyof T]: GetMaybeJust }; diff --git a/packages/widget/src/utils/formatters.ts b/packages/widget/src/utils/formatters.ts index 721f9c5e..ab77576d 100644 --- a/packages/widget/src/utils/formatters.ts +++ b/packages/widget/src/utils/formatters.ts @@ -73,7 +73,7 @@ export const getGasFeeInUSD = ({ ? ` ($${defaultFormattedNumber(val.gasFeeInUSD)})` : "" }`, - "", + "" ); export const getFeesInUSD = ({ @@ -103,7 +103,7 @@ export const getFeesInUSD = ({ ? ` ($${defaultFormattedNumber(val.feeInUSD)})` : "" }`, - "", + "" ); export const capitalizeFirstLetters = (text: string): string => @@ -112,8 +112,8 @@ export const capitalizeFirstLetters = (text: string): string => t .split(" ") .map( - (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), + (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() ) - .join(" "), + .join(" ") ) .orDefault(""); diff --git a/packages/widget/src/utils/index.ts b/packages/widget/src/utils/index.ts index 3521d712..e3a18578 100644 --- a/packages/widget/src/utils/index.ts +++ b/packages/widget/src/utils/index.ts @@ -21,13 +21,13 @@ BigNumber.config({ export const formatNumber = ( number: string | BigNumber | number, - decimals?: number, + decimals?: number ) => Just(BigNumber(number)) .map((v) => typeof decimals === "number" ? v.decimalPlaces(decimals, BigNumber.ROUND_DOWN) - : v, + : v ) .map((v) => v.toFormat()) .unsafeCoerce(); @@ -85,20 +85,20 @@ export const waitForMs = (ms: number) => export const typeSafeObjectFromEntries = < const T extends ReadonlyArray, >( - entries: T, + entries: T ): { [K in T[number] as K[0]]: K[1] } => { return Object.fromEntries(entries) as { [K in T[number] as K[0]]: K[1] }; }; export const typeSafeObjectEntries = >( - obj: T, + obj: T ): { [K in keyof T]: [K, T[K]] }[keyof T][] => { return Object.entries(obj) as { [K in keyof T]: [K, T[K]] }[keyof T][]; }; export function formatAddress( address: string, - opts?: { leadingChars: number; trailingChars: number }, + opts?: { leadingChars: number; trailingChars: number } ): string { const leadingChars = opts?.leadingChars ?? 4; const trailingChars = opts?.trailingChars ?? 4; @@ -106,7 +106,7 @@ export function formatAddress( return address.length < leadingChars + trailingChars ? address : `${address.substring(0, leadingChars)}\u2026${address.substring( - address.length - trailingChars, + address.length - trailingChars )}`; } @@ -129,10 +129,10 @@ export const isMobile = () => { if ( /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test( - navigator.userAgent, + navigator.userAgent ) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( - navigator.userAgent.substring(0, 4), + navigator.userAgent.substring(0, 4) ) ) { return true; @@ -157,7 +157,7 @@ export const bpsToPercentage = (bps: BigNumber | number) => export const groupDateStrings = ( dateStrings: string[], - i18n: i18n, + i18n: i18n ): [string[], number[]] => { const today = new Date(); const yesterday = new Date(today); diff --git a/packages/widget/src/utils/mappers.ts b/packages/widget/src/utils/mappers.ts index 4ce1c3f5..91cf1781 100644 --- a/packages/widget/src/utils/mappers.ts +++ b/packages/widget/src/utils/mappers.ts @@ -8,12 +8,12 @@ const priceDtoToPrice = (priceDto: PriceResponseDto[string]): Price => ({ }); export const priceResponseDtoToPrices = ( - priceResponseDto: PriceResponseDto, + priceResponseDto: PriceResponseDto ): Prices => new Prices( Object.keys(priceResponseDto).reduce((acc, key) => { acc.set(key as TokenString, priceDtoToPrice(priceResponseDto[key])); return acc; - }, new Map()), + }, new Map()) ); diff --git a/packages/widget/src/utils/maybe-window.ts b/packages/widget/src/utils/maybe-window.ts index b8054a4c..c1aaea38 100644 --- a/packages/widget/src/utils/maybe-window.ts +++ b/packages/widget/src/utils/maybe-window.ts @@ -1,5 +1,5 @@ import { Maybe } from "purify-ts"; export const MaybeWindow = Maybe.fromNullable( - typeof window === "undefined" ? null : window, + typeof window === "undefined" ? null : window ); diff --git a/packages/widget/src/utils/memoize.ts b/packages/widget/src/utils/memoize.ts index b5ed10b5..7559a931 100644 --- a/packages/widget/src/utils/memoize.ts +++ b/packages/widget/src/utils/memoize.ts @@ -3,7 +3,7 @@ const cachedResSymbol = Symbol("cachedRes"); type CacheMap = Map>; export const memoize = ( - fn: (...args: Args) => Res, + fn: (...args: Args) => Res ) => { const cache: CacheMap> = new Map(); diff --git a/packages/widget/src/utils/text.ts b/packages/widget/src/utils/text.ts index d9d3a3c5..9ba1f0ad 100644 --- a/packages/widget/src/utils/text.ts +++ b/packages/widget/src/utils/text.ts @@ -1,5 +1,5 @@ import { memoize } from "./memoize"; export const capitalizeFirstLowerRest = memoize( - (txt: string) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase(), + (txt: string) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase() ); diff --git a/packages/widget/src/worker.ts b/packages/widget/src/worker.ts index 0dbb41bb..fb4b7385 100644 --- a/packages/widget/src/worker.ts +++ b/packages/widget/src/worker.ts @@ -222,7 +222,7 @@ export const worker = setupWorker( return HttpResponse.json( url.searchParams.has("ledgerWalletAPICompatible") ? legacyYieldDto - : yieldApiYieldDto, + : yieldApiYieldDto ); }), http.post("*/v1/yields/balances", async () => { @@ -277,5 +277,5 @@ export const worker = setupWorker( token: maticToken, gasLimit: "", }); - }), + }) ); diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index 933b9dc7..6c4b0d1b 100644 --- a/packages/widget/tests/fixtures/index.ts +++ b/packages/widget/tests/fixtures/index.ts @@ -26,7 +26,7 @@ type LegacyYieldDto = ReturnType< const apyFaker = () => faker.number.float({ min: 0, max: 0.05 }); export const yieldRewardRateFixture = ( - overrides?: Partial, + overrides?: Partial ): YieldRewardRateDto => ({ total: apyFaker(), rateType: "APY", @@ -35,7 +35,7 @@ export const yieldRewardRateFixture = ( }); export const yieldApiYieldFixture = ( - overrides?: Partial, + overrides?: Partial ): YieldApiYieldDto => ({ ...getYieldV2ControllerGetYieldByIdResponseMock(), @@ -44,7 +44,7 @@ export const yieldApiYieldFixture = ( }) as YieldApiYieldDto; export const yieldBalanceFixture = ( - overrides?: Partial, + overrides?: Partial ): YieldBalanceDto => { const token = overrides?.token ?? yieldApiYieldFixture().token; @@ -81,12 +81,12 @@ export const yieldFixture = (overrides?: Partial) => status: { enter: true, exit: true }, validators: val.validators.map((v) => ({ ...v, apr: apyFaker() })), ...overrides, - }) satisfies LegacyYieldDto, + }) satisfies LegacyYieldDto ) .unsafeCoerce(); export const yieldValidatorsFixture = ( - validators?: LegacyYieldDto["validators"], + validators?: LegacyYieldDto["validators"] ) => (validators ?? yieldFixture().validators).map((validator) => ({ ...validator, @@ -99,7 +99,7 @@ export const enterResponseFixture = (overrides?: Partial) => ({ }); export const transactionConstructFixture = ( - overrides?: Partial, + overrides?: Partial ) => ({ ...getTransactionControllerConstructResponseMock(), ...overrides, @@ -112,7 +112,7 @@ export const pendingActionFixture = (overrides?: Partial) => ({ export const yieldApiTransactionFixture = ( tx: LegacyTransactionDto, - overrides?: Partial, + overrides?: Partial ) => ({ id: tx.id || faker.string.uuid(), diff --git a/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx b/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx index b96bb409..af98aa92 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx +++ b/packages/widget/tests/use-cases/deep-links-flow/deep-links-flow.test.tsx @@ -32,7 +32,7 @@ describe("Deep links flow", () => { .element( withAvaxLiquidStakingApp .getByText(`${avaxLiquidStaking.metadata.rewardTokens[0].symbol}`) - .first(), + .first() ) .toBeInTheDocument(); @@ -40,7 +40,7 @@ describe("Deep links flow", () => { .element( withAvaxLiquidStakingApp .getByText(`via ${avaxLiquidStaking.metadata.provider.name}`) - .first(), + .first() ) .toBeInTheDocument(); @@ -48,7 +48,7 @@ describe("Deep links flow", () => { .element( withAvaxLiquidStakingApp .getByText(`${APToPercentage(avaxLiquidStaking.rewardRate)}%`) - .first(), + .first() ) .toBeInTheDocument(); @@ -68,7 +68,7 @@ describe("Deep links flow", () => { .element( withAvaxNativeStakingApp .getByText(`${APToPercentage(avaxNativeStaking.rewardRate)}%`) - .first(), + .first() ) .toBeInTheDocument(); @@ -93,7 +93,7 @@ describe("Deep links flow", () => { await expect .element( - app.getByText("By clicking confirm you agree to", { exact: false }), + app.getByText("By clicking confirm you agree to", { exact: false }) ) .toBeInTheDocument(); @@ -150,7 +150,7 @@ describe("Deep links flow", () => { await expect .element( - app.getByText("By clicking confirm you agree to", { exact: false }), + app.getByText("By clicking confirm you agree to", { exact: false }) ) .toBeInTheDocument(); diff --git a/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx b/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx index c341b1c5..76e736bf 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx +++ b/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx @@ -11,7 +11,7 @@ describe("Deep link param validation", () => { it("Should validate yieldId param", async () => { const setAndAssertIsValidYieldIdParam = async ( yieldId: string, - valid: boolean, + valid: boolean ) => { _setUrl({ yieldId }); @@ -27,14 +27,14 @@ describe("Deep link param validation", () => { }); expect(result.result.current.map((v) => v.yieldId).extract()).toEqual( - valid ? yieldId : null, + valid ? yieldId : null ); }; await setAndAssertIsValidYieldIdParam("ethereum-eth-native-staking", true); await setAndAssertIsValidYieldIdParam( "../ethereum-eth-native-staking", - false, + false ); await setAndAssertIsValidYieldIdParam("..", false); await setAndAssertIsValidYieldIdParam("..%2f", false); @@ -42,26 +42,26 @@ describe("Deep link param validation", () => { await setAndAssertIsValidYieldIdParam("AAA-%2f..%2f..%2f-whatever", false); await setAndAssertIsValidYieldIdParam( "./ethereum-eth-native-staking", - false, + false ); await setAndAssertIsValidYieldIdParam( "ethereum-../eth-native-staking", - false, + false ); await setAndAssertIsValidYieldIdParam( "ethereum-eth-native-staking../", - false, + false ); await setAndAssertIsValidYieldIdParam( "ethereum-eth-native-staking/../", - false, + false ); }); it("Should validate pendingAction param", async () => { const setAndAssertIsValidPendingActionParam = async ( pendingaction: ActionType | (string & {}), - valid: boolean, + valid: boolean ) => { _setUrl({ pendingaction }); @@ -77,7 +77,7 @@ describe("Deep link param validation", () => { }); expect(result.current.map((v) => v.pendingaction).extract()).toEqual( - valid ? pendingaction : null, + valid ? pendingaction : null ); }; 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 5ece82d9..9a7d8fea 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -50,7 +50,7 @@ export const setup = async (opts?: { tokens: [token], metadata: { ...def.metadata, type: "staking" }, validators: [], - }), + }) ) .unsafeCoerce(); @@ -104,7 +104,7 @@ export const setup = async (opts?: { ], }, validators: avaxLiquidStakingValidators, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -319,7 +319,7 @@ export const setup = async (opts?: { status: "CONFIRMED", }), }); - }), + }) ); let currentChainId = 43114; @@ -350,7 +350,7 @@ export const setup = async (opts?: { default: throw new Error("unhandled method"); } - }, + } ); const customConnectors = rkMockWallet({ accounts: [account], requestFn }); diff --git a/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx b/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx index 444df694..ec853d16 100644 --- a/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx +++ b/packages/widget/tests/use-cases/external-provider/external-provider.test.tsx @@ -32,7 +32,7 @@ describe("External Provider", () => { await expect .element( - app.getByText(formatAddress(skProps.externalProviders.currentAddress)), + app.getByText(formatAddress(skProps.externalProviders.currentAddress)) ) .toBeInTheDocument(); @@ -45,8 +45,8 @@ describe("External Provider", () => { const chainText = app.getByText( new RegExp( - `${chainNames.eth}|${chainNames.avalanche}|${chainNames.solana}|${chainNames.ton}`, - ), + `${chainNames.eth}|${chainNames.avalanche}|${chainNames.solana}|${chainNames.ton}` + ) ); await expect.element(chainText).toBeInTheDocument(); @@ -78,17 +78,17 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - />, + /> ); await expect .poll(() => - app.getByText(new RegExp(`${chainNames.avalanche}|${chainNames.ton}`)), + app.getByText(new RegExp(`${chainNames.avalanche}|${chainNames.ton}`)) ) .toBeTruthy(); const chainButton = app.getByText( - new RegExp(`${chainNames.avalanche}|${chainNames.ton}`), + new RegExp(`${chainNames.avalanche}|${chainNames.ton}`) ); await chainButton.click(); @@ -117,12 +117,12 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - />, + /> ); await expect .element( - app.getByText(formatAddress(skProps.externalProviders.currentAddress)), + app.getByText(formatAddress(skProps.externalProviders.currentAddress)) ) .toBeInTheDocument(); @@ -136,7 +136,7 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - />, + /> ); await expect @@ -153,12 +153,12 @@ describe("External Provider", () => { ...skProps.externalProviders, supportedChainIds: [avalanche.id, ton.id], }} - />, + /> ); await expect .element( - app.getByText(formatAddress(skProps.externalProviders.currentAddress)), + app.getByText(formatAddress(skProps.externalProviders.currentAddress)) ) .toBeInTheDocument(); app.unmount(); diff --git a/packages/widget/tests/use-cases/external-provider/setup.ts b/packages/widget/tests/use-cases/external-provider/setup.ts index 493c9605..66811837 100644 --- a/packages/widget/tests/use-cases/external-provider/setup.ts +++ b/packages/widget/tests/use-cases/external-provider/setup.ts @@ -56,7 +56,7 @@ export const setup = () => { type: "staking", gasFeeToken: avalancheCToken, }, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -74,7 +74,7 @@ export const setup = () => { type: "staking", gasFeeToken: ether, }, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -92,7 +92,7 @@ export const setup = () => { type: "staking", gasFeeToken: solanaToken, }, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -110,7 +110,7 @@ export const setup = () => { type: "staking", gasFeeToken: tonToken, }, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -199,13 +199,13 @@ export const setup = () => { [tonNativeStaking.id, tonNativeStaking.validators], ]); const validators = yieldValidatorsFixture( - validatorsByYieldId.get(yieldId) ?? [], + validatorsByYieldId.get(yieldId) ?? [] ); return HttpResponse.json({ items: validators, total: validators.length, }); - }), + }) ); }; diff --git a/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx b/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx index f989290a..2bd9a08f 100644 --- a/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx +++ b/packages/widget/tests/use-cases/gas-warning-flow/gas-warning-flow.test.tsx @@ -41,7 +41,7 @@ describe("Gas warning flow", () => { .poll( () => app.getByTestId("select-opportunity").getByText(yieldDto.token.symbol) - .length, + .length ) .greaterThan(0); @@ -58,13 +58,13 @@ describe("Gas warning flow", () => { if (withWarning) { await expect .element( - app.getByText("This action is unlikely to succeed", { exact: false }), + app.getByText("This action is unlikely to succeed", { exact: false }) ) .toBeInTheDocument(); } else { await expect .element( - app.getByText("This action is unlikely to succeed", { exact: false }), + app.getByText("This action is unlikely to succeed", { exact: false }) ) .not.toBeInTheDocument(); } diff --git a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts index c4fffa3b..5eb901f6 100644 --- a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts +++ b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts @@ -191,7 +191,7 @@ export const setup = () => { await delay(); return HttpResponse.json(yieldWithSameGasAndStakeToken.yieldDto); - }, + } ), http.get( `*/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}`, @@ -199,7 +199,7 @@ export const setup = () => { await delay(); return HttpResponse.json(yieldWithDifferentGasAndStakeToken.yieldDto); - }, + } ), http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); @@ -208,10 +208,10 @@ export const setup = () => { const validators = yieldId === yieldWithSameGasAndStakeToken.yieldDto.id ? yieldValidatorsFixture( - yieldWithSameGasAndStakeToken.yieldDto.validators, + yieldWithSameGasAndStakeToken.yieldDto.validators ) : yieldValidatorsFixture( - yieldWithDifferentGasAndStakeToken.yieldDto.validators, + yieldWithDifferentGasAndStakeToken.yieldDto.validators ); return HttpResponse.json({ @@ -243,8 +243,8 @@ export const setup = () => { gasEstimate: gasAmount, status: "CREATED", stepIndex: index, - }, - ), + } + ) ), overrides: { amount: body.arguments?.amount ?? null, @@ -252,7 +252,7 @@ export const setup = () => { }, }), }); - }), + }) ); const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; diff --git a/packages/widget/tests/use-cases/geo-block.test.tsx b/packages/widget/tests/use-cases/geo-block.test.tsx index 4976826a..209af567 100644 --- a/packages/widget/tests/use-cases/geo-block.test.tsx +++ b/packages/widget/tests/use-cases/geo-block.test.tsx @@ -15,9 +15,9 @@ describe("Geo block", () => { regionCode: "AT-9", type: "GEO_LOCATION", }, - { status: 403 }, + { status: 403 } ); - }), + }) ); const app = await renderApp(); diff --git a/packages/widget/tests/use-cases/renders-initial-page.test.tsx b/packages/widget/tests/use-cases/renders-initial-page.test.tsx index c28b5646..95b4c499 100644 --- a/packages/widget/tests/use-cases/renders-initial-page.test.tsx +++ b/packages/widget/tests/use-cases/renders-initial-page.test.tsx @@ -40,7 +40,7 @@ describe("Renders initial page", () => { type: "staking", gasFeeToken: avalancheCToken, }, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -57,7 +57,7 @@ describe("Renders initial page", () => { type: "staking", gasFeeToken: ether, }, - }) satisfies ReturnType, + }) satisfies ReturnType ) .unsafeCoerce(); @@ -91,7 +91,7 @@ describe("Renders initial page", () => { await delay(); return HttpResponse.json(avalancheAvaxNativeStaking); - }), + }) ); const app = await renderApp(); diff --git a/packages/widget/tests/use-cases/select-opportunity.test.tsx b/packages/widget/tests/use-cases/select-opportunity.test.tsx index 97819790..59e10e78 100644 --- a/packages/widget/tests/use-cases/select-opportunity.test.tsx +++ b/packages/widget/tests/use-cases/select-opportunity.test.tsx @@ -123,7 +123,7 @@ describe("Select opportunity", () => { }) .map((val) => HttpResponse.json(val)) .unsafeCoerce(); - }), + }) ); const app = await renderApp(); @@ -148,24 +148,24 @@ describe("Select opportunity", () => { await expect .element( selectContainer.getByTestId( - /^select-opportunity__item_ethereum-eth-lido-staking/, - ), + /^select-opportunity__item_ethereum-eth-lido-staking/ + ) ) .toBeInTheDocument(); await expect .element( selectContainer.getByTestId( - /^select-opportunity__item_ethereum-eth-reth-staking/, - ), + /^select-opportunity__item_ethereum-eth-reth-staking/ + ) ) .toBeInTheDocument(); await expect .element( selectContainer.getByTestId( - /^select-opportunity__item_ethereum-eth-stakewise-staking/, - ), + /^select-opportunity__item_ethereum-eth-stakewise-staking/ + ) ) .not.toBeInTheDocument(); diff --git a/packages/widget/tests/use-cases/sk-wallet.test.tsx b/packages/widget/tests/use-cases/sk-wallet.test.tsx index cf2440e4..a458beff 100644 --- a/packages/widget/tests/use-cases/sk-wallet.test.tsx +++ b/packages/widget/tests/use-cases/sk-wallet.test.tsx @@ -13,7 +13,7 @@ import { worker } from "../mocks/worker"; import { renderHook } from "../utils/test-utils"; const renderHookWithExternalProvider = ( - externalProviders: SKExternalProviders, + externalProviders: SKExternalProviders ) => renderHook(useSKWallet, { wrapper: ({ children }) => ( @@ -44,7 +44,7 @@ describe("SK Wallet", () => { http.get("*/v1/yields/enabled/networks", async () => { await delay(); return HttpResponse.json([MiscNetworks.Solana]); - }), + }) ); const solanaWallet = await renderHookWithExternalProvider({ @@ -109,7 +109,7 @@ describe("SK Wallet", () => { structuredTransaction: null, annotatedTransaction: null, providersDetails: [], - }, + } ); }); @@ -121,7 +121,7 @@ describe("SK Wallet", () => { http.get("*/v1/yields/enabled/networks", async () => { await delay(); return HttpResponse.json([MiscNetworks.Ton]); - }), + }) ); const tonWallet = await renderHookWithExternalProvider({ @@ -185,7 +185,7 @@ describe("SK Wallet", () => { structuredTransaction: null, annotatedTransaction: null, providersDetails: [], - }, + } ); }); }); diff --git a/packages/widget/tests/use-cases/staking-flow/setup.ts b/packages/widget/tests/use-cases/staking-flow/setup.ts index 118f1f6b..b4ac4d6b 100644 --- a/packages/widget/tests/use-cases/staking-flow/setup.ts +++ b/packages/widget/tests/use-cases/staking-flow/setup.ts @@ -329,7 +329,7 @@ export const setup = async () => { amount: body.arguments?.amount ?? null, amountRaw: body.arguments?.amount ?? null, }, - }), + }) ); }), http.put("*/v1/transactions/:transactionId/submit-hash", async (info) => { @@ -342,7 +342,7 @@ export const setup = async () => { id: transactionId, hash: "transaction_hash", status: "BROADCASTED", - }), + }) ); }), http.get("*/v1/transactions/:transactionId", async (info) => { @@ -354,9 +354,9 @@ export const setup = async () => { "https://snowtrace.dev/tx/0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", hash: "0x5c2e4ac81fa12b8e935e1cf5e39eda4594d75e82da0c9b44c6d85f20214452fb", status: "CONFIRMED", - }), + }) ); - }), + }) ); const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; diff --git a/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx b/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx index c76f71cc..0047f798 100644 --- a/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx +++ b/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx @@ -38,7 +38,7 @@ describe("Staking flow", () => { await expect .poll( - () => app.getByTestId("select-opportunity").getByText("AVAX").length, + () => app.getByTestId("select-opportunity").getByText("AVAX").length ) .greaterThan(0); @@ -61,7 +61,7 @@ describe("Staking flow", () => { .element( app .getByTestId("estimated-reward__yearly") - .getByText(`0.00508 ${yieldOp.token.symbol}`), + .getByText(`0.00508 ${yieldOp.token.symbol}`) ) .toBeInTheDocument(); @@ -69,7 +69,7 @@ describe("Staking flow", () => { .element( app .getByTestId("estimated-reward__monthly") - .getByText(`0.00042 ${yieldOp.token.symbol}`), + .getByText(`0.00042 ${yieldOp.token.symbol}`) ) .toBeInTheDocument(); @@ -78,8 +78,8 @@ describe("Staking flow", () => { useRewardTokenDetails( Just(yieldOp) as unknown as Parameters< typeof useRewardTokenDetails - >[0], - ), + >[0] + ) ) ).result.current.unsafeCoerce(); @@ -88,7 +88,7 @@ describe("Staking flow", () => { .toBeInTheDocument(); await expect .element( - app.getByText(`${rewardTokenDetails.rewardTokens[0].symbol}`).first(), + app.getByText(`${rewardTokenDetails.rewardTokens[0].symbol}`).first() ) .toBeInTheDocument(); @@ -97,7 +97,7 @@ describe("Staking flow", () => { await userEvent.click(app.getByText("Stake").last()); const totalGasFee = new BigNumber( - transactionConstruct.gasEstimate?.amount ?? 0, + transactionConstruct.gasEstimate?.amount ?? 0 ); await expect @@ -106,8 +106,8 @@ describe("Staking flow", () => { .getByTestId("estimated_gas_fee") .getByText( `${formatNumber(totalGasFee, 10)} ${yieldOp.token.symbol}`, - { exact: false }, - ), + { exact: false } + ) ) .toBeInTheDocument(); @@ -145,8 +145,8 @@ describe("Staking flow", () => { await expect .element( app.getByText( - `Successfully staked ${stakeAmount} ${yieldOp.token.symbol}`, - ), + `Successfully staked ${stakeAmount} ${yieldOp.token.symbol}` + ) ) .toBeInTheDocument(); await expect diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts index 1902c8a8..bada3e15 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts +++ b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts @@ -284,7 +284,7 @@ export const setup = async () => { return HttpResponse.json( url.searchParams.has("ledgerWalletAPICompatible") ? legacyYield - : rawYield, + : rawYield ); }), http.get("*/v1/yields/:yieldId/validators", async () => { @@ -306,7 +306,7 @@ export const setup = async () => { ], errors: [], }); - }), + }) ); const requestFn = vitest.fn(async ({ method }: { method: string }) => { diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx index 8496237b..d955411d 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx +++ b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx @@ -24,21 +24,21 @@ describe("Trust incentive APY", () => { await expect.element(app.getByText("APY composition")).toBeInTheDocument(); await expect .element( - app.getByTestId("reward-rate-breakdown__native").getByText("4.27%"), + app.getByTestId("reward-rate-breakdown__native").getByText("4.27%") ) .toBeInTheDocument(); await expect .element( app .getByTestId("reward-rate-breakdown__protocol-incentive") - .getByText("0.28%"), + .getByText("0.28%") ) .toBeInTheDocument(); await expect .element( app .getByTestId("reward-rate-breakdown__campaign") - .getByText("Up to 0.20%"), + .getByText("Up to 0.20%") ) .toBeInTheDocument(); @@ -82,21 +82,21 @@ describe("Trust incentive APY", () => { .element( app .getByTestId("personalized-reward-rate-breakdown__native") - .getByText("4.27%"), + .getByText("4.27%") ) .toBeInTheDocument(); await expect .element( app .getByTestId("personalized-reward-rate-breakdown__protocol-incentive") - .getByText("0.28%"), + .getByText("0.28%") ) .toBeInTheDocument(); await expect .element( app .getByTestId("personalized-reward-rate-breakdown__campaign") - .getByText("0.18%"), + .getByText("0.18%") ) .toBeInTheDocument(); diff --git a/packages/widget/tests/use-cases/under-maintenance.test.tsx b/packages/widget/tests/use-cases/under-maintenance.test.tsx index ccfa9460..8384ddae 100644 --- a/packages/widget/tests/use-cases/under-maintenance.test.tsx +++ b/packages/widget/tests/use-cases/under-maintenance.test.tsx @@ -8,7 +8,7 @@ describe("Under maintenance", () => { worker.use( http.get("*/v2/health", async () => { return HttpResponse.json({ db: "FAIL" }); - }), + }) ); const app = await renderApp(); diff --git a/packages/widget/vite/vite.config.bundle.ts b/packages/widget/vite/vite.config.bundle.ts index 4a70e7b2..b000bb85 100644 --- a/packages/widget/vite/vite.config.bundle.ts +++ b/packages/widget/vite/vite.config.bundle.ts @@ -15,5 +15,5 @@ export default defineConfig( outDir: "dist/bundle", sourcemap: false, }, - }), + }) ); diff --git a/packages/widget/vite/vite.config.website.ts b/packages/widget/vite/vite.config.website.ts index d6e9f0ba..6db14f33 100644 --- a/packages/widget/vite/vite.config.website.ts +++ b/packages/widget/vite/vite.config.website.ts @@ -7,5 +7,5 @@ export default defineConfig( outDir: "dist/website", sourcemap: true, }, - }), + }) ); From d480ab345292b1a26ed3114336d2271bf8c41cd1 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Tue, 24 Mar 2026 23:45:07 +0100 Subject: [PATCH 15/23] fix: decode tx gas --- packages/widget/script.ts | 110 ------------------ packages/widget/src/domain/types/action.ts | 31 +---- .../review/hooks/use-pending-review.hook.ts | 10 +- .../review/hooks/use-stake-review.hook.ts | 12 +- .../review/hooks/use-unstake-review.hook.ts | 10 +- 5 files changed, 26 insertions(+), 147 deletions(-) delete mode 100644 packages/widget/script.ts diff --git a/packages/widget/script.ts b/packages/widget/script.ts deleted file mode 100644 index 8726204e..00000000 --- a/packages/widget/script.ts +++ /dev/null @@ -1,110 +0,0 @@ -const main = async () => { - const res = await Promise.all([ - fetch("https://api.stakek.it/v1/yields/enabled/networks", { - headers: { - accept: "application/json, text/plain, */*", - "accept-language": "en,fr;q=0.9,en-US;q=0.8,en-GB;q=0.7", - "cache-control": "no-cache", - pragma: "no-cache", - priority: "u=1, i", - "sec-ch-ua": - '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', - "sec-ch-ua-mobile": "?0", - "sec-ch-ua-platform": '"macOS"', - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "sec-fetch-site": "cross-site", - "x-api-key": "e2d627cf-2ae3-4775-9fbc-76819c7cae38", - }, - referrer: "http://localhost:5173/", - referrerPolicy: "strict-origin-when-cross-origin", - body: null, - method: "GET", - mode: "cors", - credentials: "omit", - }), - fetch("https://api.stakek.it/v1/tokens", { - headers: { - accept: "application/json, text/plain, */*", - "accept-language": "en,fr;q=0.9,en-US;q=0.8,en-GB;q=0.7", - "cache-control": "no-cache", - pragma: "no-cache", - priority: "u=1, i", - "sec-ch-ua": - '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', - "sec-ch-ua-mobile": "?0", - "sec-ch-ua-platform": '"macOS"', - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "sec-fetch-site": "cross-site", - "x-api-key": "e2d627cf-2ae3-4775-9fbc-76819c7cae38", - }, - referrer: "http://localhost:5173/", - referrerPolicy: "strict-origin-when-cross-origin", - body: null, - method: "GET", - mode: "cors", - credentials: "omit", - }), - - fetch("https://api.stakek.it/v1/tokens/balances/scan", { - headers: { - accept: "application/json, text/plain, */*", - "accept-language": "en,fr;q=0.9,en-US;q=0.8,en-GB;q=0.7", - "cache-control": "no-cache", - "content-type": "application/json", - pragma: "no-cache", - priority: "u=1, i", - "sec-ch-ua": - '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', - "sec-ch-ua-mobile": "?0", - "sec-ch-ua-platform": '"macOS"', - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "sec-fetch-site": "cross-site", - "x-api-key": "e2d627cf-2ae3-4775-9fbc-76819c7cae38", - }, - referrer: "http://localhost:5173/", - referrerPolicy: "strict-origin-when-cross-origin", - body: '{"addresses":{"address":"0xdCeE0E27dd7c71f53AfC4E3A5248e286586B174d"},"network":"base"}', - method: "POST", - mode: "cors", - credentials: "omit", - }), - - fetch("https://api.stakek.it/v1/yields/balances/scan", { - headers: { - accept: "application/json, text/plain, */*", - "accept-language": "en,fr;q=0.9,en-US;q=0.8,en-GB;q=0.7", - "cache-control": "no-cache", - "content-type": "application/json", - pragma: "no-cache", - priority: "u=1, i", - "sec-ch-ua": - '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', - "sec-ch-ua-mobile": "?0", - "sec-ch-ua-platform": '"macOS"', - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "sec-fetch-site": "cross-site", - "x-api-key": "e2d627cf-2ae3-4775-9fbc-76819c7cae38", - }, - referrer: "http://localhost:5173/", - referrerPolicy: "strict-origin-when-cross-origin", - body: '{"addresses":{"address":"0xdCeE0E27dd7c71f53AfC4E3A5248e286586B174d"},"network":"base","customValidators":[]}', - method: "POST", - mode: "cors", - credentials: "omit", - }), - ]); - - const failing = res.filter((r) => !r.ok); - - for (const r of failing) { - console.log(r); - const json = await r.json(); - console.log(json); - } -}; - -main(); diff --git a/packages/widget/src/domain/types/action.ts b/packages/widget/src/domain/types/action.ts index bf790748..9c7c7c8d 100644 --- a/packages/widget/src/domain/types/action.ts +++ b/packages/widget/src/domain/types/action.ts @@ -146,13 +146,9 @@ export const getActionCurrentStepIndex = (actionDto: ActionDto) => { return Math.max(actionDto.transactions.length - 1, 0); }; -export const getTransactionGasEstimate = ({ - transactionDto, - gasFeeToken, -}: { - transactionDto: TransactionDto; - gasFeeToken?: TokenDto; -}): TransactionGasEstimate => { +export const getTransactionGasEstimate = ( + transactionDto: TransactionDto +): TransactionGasEstimate => { const gasEstimate = transactionDto.gasEstimate; if (!gasEstimate) { @@ -163,18 +159,11 @@ export const getTransactionGasEstimate = ({ const parsed = JSON.parse(gasEstimate) as EncodedGasEstimate | null; if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { - if (!gasFeeToken) { - return null; - } - - return { - amount: gasEstimate, - token: gasFeeToken, - }; + return null; } const amount = parsed.amount ?? null; - const token = parsed.token ?? gasFeeToken; + const token = parsed.token; if (!amount || !token) { return null; @@ -183,17 +172,9 @@ export const getTransactionGasEstimate = ({ return { amount, token, - ...(parsed.gasLimit ? { gasLimit: parsed.gasLimit } : {}), }; } catch { - if (!gasFeeToken) { - return null; - } - - return { - amount: gasEstimate, - token: gasFeeToken, - }; + return null; } }; diff --git a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts index 7c2a8261..46233372 100644 --- a/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-pending-review.hook.ts @@ -7,6 +7,7 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import type { RewardTokenDetails } from "../../../components/molecules/reward-token-details"; +import { getTransactionGasEstimate } from "../../../domain/types/action"; import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; @@ -45,10 +46,11 @@ export const usePendingActionReview = () => { () => Maybe.fromNullable(actionPreviewQuery.data) .map((actionDto) => - actionDto.transactions.reduce( - (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), - new BigNumber(0) - ) + actionDto.transactions.reduce((acc, transaction) => { + const decoded = getTransactionGasEstimate(transaction); + + return acc.plus(decoded?.amount ?? 0); + }, new BigNumber(0)) ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), diff --git a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts index 6a354bf5..55574ba5 100644 --- a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts @@ -5,6 +5,7 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; +import { getTransactionGasEstimate } from "../../../domain/types/action"; import { getYieldMetadata } from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useEstimatedRewards } from "../../../hooks/use-estimated-rewards"; @@ -52,16 +53,19 @@ export const useStakeReview = () => { () => Maybe.fromNullable(actionPreviewQuery.data) .map((actionDto) => - actionDto.transactions.reduce( - (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), - new BigNumber(0) - ) + actionDto.transactions.reduce((acc, transaction) => { + const decoded = getTransactionGasEstimate(transaction); + + return acc.plus(decoded?.amount ?? 0); + }, new BigNumber(0)) ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), [actionPreviewQuery.data] ); + console.log({ stakeEnterTxGas }); + const gasCheckWarning = useGasWarningCheck({ gasAmount: stakeEnterTxGas, gasFeeToken: enterRequest.gasFeeToken, diff --git a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts index d9ecfbb2..77b8f809 100644 --- a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts @@ -7,6 +7,7 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import type { RewardTokenDetails } from "../../../components/molecules/reward-token-details"; +import { getTransactionGasEstimate } from "../../../domain/types/action"; import { getYieldMetadata, getYieldProviderDetails, @@ -47,10 +48,11 @@ export const useUnstakeActionReview = () => { () => Maybe.fromNullable(actionPreviewQuery.data) .map((actionDto) => - actionDto.transactions.reduce( - (acc, transaction) => acc.plus(transaction.gasEstimate ?? 0), - new BigNumber(0) - ) + actionDto.transactions.reduce((acc, transaction) => { + const decoded = getTransactionGasEstimate(transaction); + + return acc.plus(decoded?.amount ?? 0); + }, new BigNumber(0)) ) .map((value) => (value.isZero() ? null : value)) .chainNullable((value) => value), From e5977ef9ac48fd5bbbdd2be878f4eebced7d3097 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 25 Mar 2026 00:26:23 +0100 Subject: [PATCH 16/23] feat: filter activity actions by network --- packages/widget/package.json | 2 +- .../src/hooks/api/use-activity-actions.ts | 22 +- .../yield-api-client-provider/actions.ts | 4 + .../widget/src/types/yield-api-schema.d.ts | 219 +++++++++++++++++- 4 files changed, 225 insertions(+), 22 deletions(-) diff --git a/packages/widget/package.json b/packages/widget/package.json index 3c085ea5..53f78997 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -56,7 +56,7 @@ "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", - "gen:yield-api": "openapi-typescript https://api.stg.yield.xyz/docs.yaml -o src/types/yield-api-schema.d.ts" + "gen:yield-api": "openapi-typescript https://api.stg.yield.xyz/docs.yaml -o src/types/yield-api-schema.d.ts && biome check --write --unsafe src/types/yield-api-schema.d.ts" }, "peerDependencies": { "react": ">=18", diff --git a/packages/widget/src/hooks/api/use-activity-actions.ts b/packages/widget/src/hooks/api/use-activity-actions.ts index 0ee67967..95eeb640 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -1,18 +1,14 @@ import { useInfiniteQuery } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; import { useMemo } from "react"; -import { - type ActionDto, - ActionStatus, - getActionInputToken, -} from "../../domain/types/action"; +import { type ActionDto, getActionInputToken } from "../../domain/types/action"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; import { listActions } from "../../providers/yield-api-client-provider/actions"; import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportunity"; -const PAGE_SIZE = 20; +const PAGE_SIZE = 50; export const useActivityActions = () => { const { address, isLedgerLive, network } = useSKWallet(); @@ -20,7 +16,7 @@ export const useActivityActions = () => { const yieldApiFetchClient = useYieldApiFetchClient(); const query = useInfiniteQuery({ - enabled: !!address, + enabled: !!address && !!network, queryKey: ["activity-actions", address, network], queryFn: async ({ pageParam = 0 }) => { return ( @@ -30,21 +26,13 @@ export const useActivityActions = () => { fetchClient: yieldApiFetchClient, limit: PAGE_SIZE, offset: pageParam, + network: network!, }) ) .mapLeft(() => new Error("Could not get action list")) - .map((actionList) => ({ - ...actionList, - data: (actionList.items ?? []).filter( - (action) => - action.status !== ActionStatus.CREATED && - (!network || - action.transactions.some((tx) => tx.network === network)) - ), - })) .chain(async (actionList) => EitherAsync.all( - actionList.data.map((action) => + (actionList.items ?? []).map((action) => getYieldOpportunity({ yieldId: action.yieldId, queryClient, diff --git a/packages/widget/src/providers/yield-api-client-provider/actions.ts b/packages/widget/src/providers/yield-api-client-provider/actions.ts index fae4cb40..bdccc1e3 100644 --- a/packages/widget/src/providers/yield-api-client-provider/actions.ts +++ b/packages/widget/src/providers/yield-api-client-provider/actions.ts @@ -3,6 +3,7 @@ import type { YieldCreateActionDto, YieldCreateManageActionDto, } from "../../domain/types/action"; +import type { SupportedSKChains } from "../../domain/types/chains"; import type { YieldApiFetchClient } from "../../domain/types/yield-api"; import { getResponseData } from "./request-helpers"; @@ -53,11 +54,13 @@ export const listActions = async ({ fetchClient, limit, offset, + network, }: { address: string; fetchClient: YieldApiFetchClient; limit: number; offset: number; + network: SupportedSKChains; }) => getResponseData( fetchClient.GET("/v1/actions", { @@ -66,6 +69,7 @@ export const listActions = async ({ address, offset, limit, + network, }, }, }) diff --git a/packages/widget/src/types/yield-api-schema.d.ts b/packages/widget/src/types/yield-api-schema.d.ts index 6ed192e2..b8d0f3de 100644 --- a/packages/widget/src/types/yield-api-schema.d.ts +++ b/packages/widget/src/types/yield-api-schema.d.ts @@ -2585,6 +2585,8 @@ export interface components { * @example false */ skipPrechecks?: boolean; + /** @description Receiver wallet address: ERC4626 vault flows, or on Solana the address for tokens after an optional post-exit swap */ + receiverAddress?: string; /** * @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 @@ -2892,14 +2894,16 @@ export interface components { /** @description Search by yield name */ search?: string; /** - * @description Sort by yield status + * @description Sort by yield status or reward rate * @enum {string} */ sort?: | "statusEnterAsc" | "statusEnterDesc" | "statusExitAsc" - | "statusExitDesc"; + | "statusExitDesc" + | "rewardRateAsc" + | "rewardRateDesc"; }; PaginationQueryDto: { /** @@ -3191,6 +3195,110 @@ export interface components { * @example ethereum-eth-lido-staking */ yieldId?: string; + /** + * @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"; }; SubmitHashDto: { /** @@ -3565,12 +3673,14 @@ export interface operations { providers?: string[]; /** @description Search by yield name */ search?: string; - /** @description Sort by yield status */ + /** @description Sort by yield status or reward rate */ sort?: | "statusEnterAsc" | "statusEnterDesc" | "statusExitAsc" - | "statusExitDesc"; + | "statusExitDesc" + | "rewardRateAsc" + | "rewardRateDesc"; }; header?: never; path?: never; @@ -4800,6 +4910,107 @@ export interface operations { * @example ethereum-eth-lido-staking */ yieldId?: string; + /** @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"; }; header?: never; path?: never; From c45e73789a38b25ee718e0c997e0f126aae4638b Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 25 Mar 2026 15:19:31 +0100 Subject: [PATCH 17/23] chore: dtos cleanup --- packages/widget/.env.test | 4 + .../src/components/atoms/token-icon/index.tsx | 2 +- .../atoms/token-icon/provider-icon/index.tsx | 2 +- .../hooks/use-variant-token-urls.ts | 2 +- .../token-icon/token-icon-container/index.tsx | 2 +- .../molecules/reward-rate-breakdown/index.tsx | 2 - .../select-opportunity-list-item/index.tsx | 27 ++- packages/widget/src/domain/index.ts | 6 +- packages/widget/src/domain/types/action.ts | 7 +- packages/widget/src/domain/types/stake.ts | 9 +- .../widget/src/domain/types/validators.ts | 25 +-- packages/widget/src/domain/types/yields.ts | 166 +++------------ .../widget/src/hooks/api/use-tokens-prices.ts | 12 +- .../src/hooks/api/use-yield-validators.ts | 2 +- .../widget/src/hooks/use-estimated-rewards.ts | 2 + .../widget/src/hooks/use-gas-fee-token.ts | 7 - .../widget/src/hooks/use-provider-details.ts | 11 +- packages/widget/src/hooks/use-summary.tsx | 4 +- .../widget/src/hooks/use-yield-meta-info.tsx | 51 ++--- .../activity/action-list-item/index.tsx | 11 +- .../activity/position-balances.tsx | 4 +- .../components/positions-list-item.tsx | 13 +- .../components/position-details-actions.tsx | 4 +- .../components/position-details-info.tsx | 4 +- .../components/top-header.tsx | 11 +- .../components/positions-list-item.tsx | 11 +- .../hooks/use-activity-complete.hook.ts | 9 +- .../src/pages/complete/pages/common.page.tsx | 4 +- .../complete/pages/pending-complete.page.tsx | 8 +- .../complete/pages/stake-complete.page.tsx | 8 +- .../complete/pages/unstake-complete.page.tsx | 8 +- .../components/action-list-item/index.tsx | 11 +- .../select-opportunity.tsx | 8 +- .../select-yield-section/staked-via.tsx | 4 +- .../use-animate-yield-percent.ts | 16 +- .../earn-page/state/earn-page-context.tsx | 3 +- .../state/use-stake-enter-request-dto.ts | 3 +- .../components/positions-list-item.tsx | 11 +- .../components/position-balances.tsx | 4 +- .../hooks/use-stake-exit-request-dto.ts | 3 +- .../hooks/use-unstake-machine.ts | 6 +- .../src/pages/position-details/hooks/utils.ts | 4 +- .../position-details.page.tsx | 21 +- .../review/hooks/use-action-review.hook.ts | 4 +- .../review/hooks/use-stake-review.hook.ts | 15 +- .../review/hooks/use-unstake-review.hook.ts | 4 +- .../pages/review/pages/action-review.page.tsx | 8 +- .../review/pages/common-page/common.page.tsx | 3 +- .../components/review-top-section.tsx | 3 +- .../review/pages/pending-review.page.tsx | 8 +- .../review/pages/unstake-review.page.tsx | 8 +- packages/widget/src/utils/formatters.ts | 6 +- packages/widget/src/utils/index.ts | 3 +- packages/widget/src/worker.ts | 21 +- packages/widget/tests/fixtures/index.ts | 159 ++++++++++++++ packages/widget/tests/mocks/api-routes.ts | 10 + .../tests/use-cases/deep-links-flow/setup.ts | 25 ++- .../use-cases/external-provider/setup.ts | 85 ++++++-- .../tests/use-cases/gas-warning-flow/setup.ts | 43 +++- .../widget/tests/use-cases/geo-block.test.tsx | 3 +- .../use-cases/renders-initial-page.test.tsx | 54 +++-- .../use-cases/select-opportunity.test.tsx | 194 ++++++++++-------- .../widget/tests/use-cases/sk-wallet.test.tsx | 9 +- .../tests/use-cases/staking-flow/setup.ts | 39 +++- .../use-cases/trust-incentive-apy/setup.ts | 20 +- .../trust-incentive-apy.test.tsx | 4 +- .../use-cases/under-maintenance.test.tsx | 8 +- packages/widget/vite/vite.config.base.ts | 1 + 68 files changed, 791 insertions(+), 478 deletions(-) create mode 100644 packages/widget/.env.test delete mode 100644 packages/widget/src/hooks/use-gas-fee-token.ts create mode 100644 packages/widget/tests/mocks/api-routes.ts diff --git a/packages/widget/.env.test b/packages/widget/.env.test new file mode 100644 index 00000000..047eb440 --- /dev/null +++ b/packages/widget/.env.test @@ -0,0 +1,4 @@ +VITE_API_URL=https://api.stakek.it/ +VITE_YIELDS_API_URL=https://api.yield.xyz/ +VITE_API_KEY=vitest-api-key +MODE=test diff --git a/packages/widget/src/components/atoms/token-icon/index.tsx b/packages/widget/src/components/atoms/token-icon/index.tsx index 222e246e..1c14f4bb 100644 --- a/packages/widget/src/components/atoms/token-icon/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/index.tsx @@ -14,7 +14,7 @@ export const TokenIcon = ({ hideNetwork, }: { token: TokenDto | YieldTokenDto; - metadata?: YieldMetadata; + metadata?: Pick; tokenLogoHw?: Atoms["hw"]; tokenNetworkLogoHw?: Atoms["hw"]; hideNetwork?: boolean; diff --git a/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx b/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx index 64834f59..4ad88d01 100644 --- a/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx @@ -14,7 +14,7 @@ export const ProviderIcon = ({ hideNetwork, }: { token: TokenDto; - metadata?: YieldMetadata; + metadata?: Pick; tokenLogoHw?: Atoms["hw"]; tokenNetworkLogoHw?: Atoms["hw"]; hideNetwork?: boolean; 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 8f948a0b..a577a47a 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 @@ -10,7 +10,7 @@ import { useSettings } from "../../../../../providers/settings"; export const useVariantTokenUrls = ( token: TokenDto | YieldTokenDto, - metadata?: YieldMetadata + metadata?: Pick ): { mainUrl: string | undefined; fallbackUrl: string | undefined; 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 c5465e79..5a92d0a0 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 @@ -8,7 +8,7 @@ import { useVariantTokenUrls } from "./hooks/use-variant-token-urls"; type TokenIconContainerProps = { token: TokenDto | YieldTokenDto; - metadata?: YieldMetadata; + metadata?: Pick; hideNetwork?: boolean; children: (props: TokenIconContainerReturnType) => ReactElement; }; diff --git a/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx b/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx index 8ef6a96c..f629003b 100644 --- a/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx +++ b/packages/widget/src/components/molecules/reward-rate-breakdown/index.tsx @@ -36,8 +36,6 @@ export const RewardRateBreakdown = ({ showUpToCampaign, }); - console.log({ items }); - if (!items.length) { return null; } diff --git a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx index 8ac70612..59fd76c7 100644 --- a/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx +++ b/packages/widget/src/components/molecules/select-opportunity-list-item/index.tsx @@ -7,10 +7,11 @@ import { getYieldRewardRateDetails, } from "../../../domain/types/reward-rate"; import { - getYieldMetadata, - getYieldRewardRate, + getYieldCommission, + getYieldProviderDetails, getYieldRewardTokens, getYieldRewardType, + getYieldTVL, type Yield, } from "../../../domain/types/yields"; import { APToPercentage, formatNumber, fromWei } from "../../../utils"; @@ -44,24 +45,30 @@ export const SelectOpportunityListItem = ({ ).find((rewardRate) => rewardRate.key === "campaign"); const totalRateFormatted = getRewardRateFormatted({ - rewardRate: getYieldRewardRate(item), + rewardRate: item.rewardRate.total, rewardType: getYieldRewardType(item), }); const primaryRateFormatted = getRewardRateFormatted({ rewardRate: campaignRate - ? getYieldRewardRate(item) - campaignRate.rate - : getYieldRewardRate(item), + ? item.rewardRate.total - campaignRate.rate + : item.rewardRate.total, rewardType: getYieldRewardType(item), }); - const metadata = getYieldMetadata(item); + const provider = getYieldProviderDetails(item) ?? undefined; const rewardTokens = getYieldRewardTokens(item); + const tvl = getYieldTVL(item); + const commission = getYieldCommission(item); return ( [0]["metadata"]} + metadata={{ + logoURI: item.metadata.logoURI, + name: item.metadata.name, + provider, + }} token={item.token as Parameters[0]["token"]} /> @@ -80,7 +87,7 @@ export const SelectOpportunityListItem = ({ > - {metadata.name} + {item.metadata.name} @@ -103,7 +110,7 @@ export const SelectOpportunityListItem = ({ {Maybe.fromNullable(rewardTokens.length ? rewardTokens : null) .map((rt) => rt.map((t) => t.symbol).join(", ")) .altLazy(() => - Maybe.fromNullable(metadata.tvl) + Maybe.fromNullable(tvl) .map((tvl) => tvl.reduce( (acc, curr) => acc.plus(curr.value), @@ -127,7 +134,7 @@ export const SelectOpportunityListItem = ({ .extractNullable()} - {Maybe.fromNullable(metadata.commission) + {Maybe.fromNullable(commission) .map((commission) => APToPercentage( commission.reduce((acc, curr) => acc + curr.value, 0) diff --git a/packages/widget/src/domain/index.ts b/packages/widget/src/domain/index.ts index e8052f30..da473297 100644 --- a/packages/widget/src/domain/index.ts +++ b/packages/widget/src/domain/index.ts @@ -12,7 +12,7 @@ import { isPendingActionValidatorAddressRequired, } from "./types/pending-action"; import type { TokenDto, TokenString } from "./types/tokens"; -import { getYieldGasFeeToken, type Yield } from "./types/yields"; +import type { Yield } from "./types/yields"; export { getTokenPriceInUSD } from "./types/price"; @@ -34,7 +34,7 @@ export const stakeTokenSameAsGasToken = ({ }: { stakeToken: TokenDto; yieldDto: Yield; -}) => equalTokens(stakeToken, getGasFeeToken(yieldDto)); +}) => equalTokens(stakeToken, yieldDto.mechanics.gasFeeToken); export const getMaxAmount = ({ availableAmount, @@ -55,8 +55,6 @@ export const getMaxAmount = ({ }; export const getBaseToken = (yieldDto: Yield) => yieldDto.token; -export const getGasFeeToken = (yieldDto: Yield) => - getYieldGasFeeToken(yieldDto); /** * diff --git a/packages/widget/src/domain/types/action.ts b/packages/widget/src/domain/types/action.ts index 9c7c7c8d..dec8fe40 100644 --- a/packages/widget/src/domain/types/action.ts +++ b/packages/widget/src/domain/types/action.ts @@ -1,8 +1,7 @@ import type BigNumber from "bignumber.js"; import type { components } from "../../types/yield-api-schema"; import type { TokenDto } from "./tokens"; -import type { Yield } from "./yields"; -import { getYieldGasFeeToken } from "./yields"; +import { getYieldMetadataTokens, type Yield } from "./yields"; export type ActionDto = components["schemas"]["ActionDto"]; export type TransactionDto = components["schemas"]["TransactionDto"]; export type TransactionType = TransactionDto["type"]; @@ -109,7 +108,7 @@ export const getActionInputToken = ({ [ yieldDto.token, ...(yieldDto.tokens ?? []), - ...(yieldDto.__fallback__.metadata.tokens ?? []), + ...getYieldMetadataTokens(yieldDto), ].find((token) => { const address = token.address ? toLower(token.address) : null; @@ -181,7 +180,7 @@ export const getTransactionGasEstimate = ( export const getActionGasFeeToken = ( yieldDto: Yield, gasFeeToken?: TokenDto -): TokenDto => gasFeeToken ?? getYieldGasFeeToken(yieldDto); +): TokenDto => gasFeeToken ?? yieldDto.mechanics.gasFeeToken; export type ActionDtoWithGasEstimate = { gasEstimate: { diff --git a/packages/widget/src/domain/types/stake.ts b/packages/widget/src/domain/types/stake.ts index b68b90c6..35cb9b2e 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -8,12 +8,7 @@ import type { InitParams } from "./init-params"; import type { PositionsData } from "./positions"; import type { TokenString } from "./tokens"; import type { ValidatorDto } from "./validators"; -import { - getYieldActionArg, - getYieldGasFeeToken, - isBittensorStaking, - type Yield, -} from "./yields"; +import { getYieldActionArg, isBittensorStaking, type Yield } from "./yields"; const amountGreaterThanZero = (val: TokenBalanceScanResponseDto) => new BigNumber(val.amount).isGreaterThan(0); @@ -159,7 +154,7 @@ export const isNetworkWithEnterMinBasedOnPosition = (network: Networks) => const isYieldWithEnterMinBasedOnPosition = (yieldDto: Yield) => Maybe.fromNullable( yieldsWithEnterMinBasedOnPosition.get( - getYieldGasFeeToken(yieldDto).network as Networks + yieldDto.mechanics.gasFeeToken.network as Networks ) ) .filter((set) => set.has(yieldDto.id)) diff --git a/packages/widget/src/domain/types/validators.ts b/packages/widget/src/domain/types/validators.ts index 213448d2..160edd4a 100644 --- a/packages/widget/src/domain/types/validators.ts +++ b/packages/widget/src/domain/types/validators.ts @@ -5,38 +5,25 @@ export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; export type ValidatorDto = LegacyValidatorDto; export const toValidatorDto = ( - validatorDto: YieldValidatorDto | ValidatorDto + validatorDto: YieldValidatorDto ): ValidatorDto => { - const legacyValidator = validatorDto as ValidatorDto; - const rewardRate = - "rewardRate" in validatorDto ? validatorDto.rewardRate : undefined; - const providerId = - "provider" in validatorDto - ? (validatorDto.provider?.id ?? validatorDto.providerId) - : validatorDto.providerId; - const image = - "logoURI" in validatorDto ? validatorDto.logoURI : legacyValidator.image; - const stakedBalance = - "tvl" in validatorDto ? validatorDto.tvl : legacyValidator.stakedBalance; - return { address: validatorDto.address, - apr: "apr" in validatorDto ? validatorDto.apr : rewardRate?.total, + apr: validatorDto.rewardRate?.total, commission: validatorDto.commission, - image, + image: validatorDto.logoURI, minimumStake: validatorDto.minimumStake, name: validatorDto.name, nominatorCount: validatorDto.nominatorCount, preferred: validatorDto.preferred, pricePerShare: validatorDto.pricePerShare, - providerId, + providerId: validatorDto.provider?.id, remainingPossibleStake: validatorDto.remainingPossibleStake, remainingSlots: validatorDto.remainingSlots, - stakedBalance, + stakedBalance: validatorDto.tvl, status: validatorDto.status as ValidatorDto["status"], subnetId: validatorDto.subnetId, - subnetName: - "subnetName" in validatorDto ? validatorDto.subnetName : undefined, + subnetName: validatorDto.subnetName, tokenSymbol: validatorDto.tokenSymbol, votingPower: validatorDto.votingPower, website: validatorDto.website, diff --git a/packages/widget/src/domain/types/yields.ts b/packages/widget/src/domain/types/yields.ts index eb1f5db2..5ba4a0ad 100644 --- a/packages/widget/src/domain/types/yields.ts +++ b/packages/widget/src/domain/types/yields.ts @@ -7,9 +7,10 @@ import BigNumber from "bignumber.js"; import type { TFunction } from "i18next"; import { Maybe } from "purify-ts"; import type { components } from "../../types/yield-api-schema"; +import { tokenString } from ".."; import type { SupportedSKChains } from "./chains"; import type { RewardTypes } from "./reward-rate"; -import type { YieldTokenDto } from "./tokens"; +import type { TokenString, YieldTokenDto } from "./tokens"; import type { ValidatorDto } from "./validators"; export type Yield = components["schemas"]["YieldDto"] & { @@ -120,68 +121,21 @@ const secondsToDays = (seconds: number | undefined) => { return { days: Math.round(seconds / 86400) }; }; -const isSameToken = ( - left: Pick, - right: Pick -) => - left.network === right.network && - left.symbol === right.symbol && - (left.address?.toLowerCase() ?? "") === (right.address?.toLowerCase() ?? ""); - -const mapMechanicsType = ( - type: Yield["mechanics"]["type"] -): Exclude< - ExtendedYieldType, - "liquid-staking" | "native_staking" | "pooled_staking" -> => { - switch (type) { - case "staking": - case "restaking": - case "lending": - case "vault": - return type; - case "liquidity_pool": - case "concentrated_liquidity_pool": - case "fixed_yield": - case "real_world_asset": - return "vault"; - } -}; - -const getBaseYieldType = ( +export const getBaseYieldType = ( yieldDto: Yield ): LegacyYieldType | "liquid-staking" => { - if ( - yieldDto.mechanics.type === "staking" && - (yieldDto.__fallback__.metadata.type === "liquid-staking" || - (!!yieldDto.outputToken && - !isSameToken(yieldDto.outputToken, yieldDto.token))) - ) { + if (yieldDto.__fallback__.metadata.type === "liquid-staking") { return "liquid-staking"; } - return mapMechanicsType(yieldDto.mechanics.type); -}; - -const getFallbackActionArg = ( - yieldDto: Yield, - type: YieldActionType, - name: YieldArgumentName -) => { - const legacyArgs = yieldDto.__fallback__.args?.[type]?.args as - | Record - | undefined; - const legacyField = legacyArgs?.[name] as YieldArgumentConfig | undefined; - - if ( - !legacyField || - typeof legacyField !== "object" || - Array.isArray(legacyField) - ) { - return undefined; + switch (yieldDto.mechanics.type) { + case "staking": + case "restaking": + case "lending": + return yieldDto.mechanics.type; + default: + return "vault"; } - - return legacyField; }; export const getYieldActionArg = ( @@ -192,22 +146,16 @@ export const getYieldActionArg = ( const field = yieldDto.mechanics.arguments?.[type]?.fields?.find( (item) => item.name === name ); - const legacyField = getFallbackActionArg(yieldDto, type, name); - if (!field && !legacyField) { + if (!field) { return null; } return { - ...(legacyField ?? {}), - ...(field - ? { - required: !!field.required, - minimum: toNumber(field.minimum) ?? legacyField?.minimum ?? null, - maximum: toNumber(field.maximum) ?? legacyField?.maximum ?? null, - ...(field.options ? { options: field.options } : {}), - } - : {}), + required: !!field.required, + minimum: toNumber(field.minimum), + maximum: toNumber(field.maximum), + ...(field.options ? { options: field.options } : {}), }; }; @@ -217,9 +165,6 @@ export const isYieldActionArgRequired = ( name: YieldArgumentName ) => !!getYieldActionArg(yieldDto, type, name)?.required; -export const getYieldRewardRate = (yieldDto: Yield) => - yieldDto.rewardRate?.total ?? yieldDto.__fallback__.rewardRate ?? 0; - export const getYieldRewardType = (yieldDto: Yield): RewardTypes => { const rateType = yieldDto.rewardRate?.rateType?.toLowerCase(); @@ -231,12 +176,12 @@ export const getYieldRewardType = (yieldDto: Yield): RewardTypes => { }; const uniqTokens = (tokens: (YieldTokenDto | null | undefined)[]) => { - const seen = new Set(); + const seen = new Set(); return tokens.flatMap((token) => { if (!token) return []; - const key = `${token.network}:${token.address?.toLowerCase() ?? ""}:${token.symbol}`; + const key = tokenString(token); if (seen.has(key)) { return []; @@ -259,82 +204,35 @@ export const getYieldRewardTokens = (yieldDto: Yield) => { return yieldDto.__fallback__.metadata.rewardTokens ?? []; }; -export const getYieldGasFeeToken = (yieldDto: Yield) => - yieldDto.mechanics.gasFeeToken ?? - yieldDto.__fallback__.metadata.gasFeeToken ?? - yieldDto.token; - export const getYieldProviderDetails = (yieldDto: Yield) => - yieldDto.__fallback__.metadata.provider ?? - (yieldDto.providerId - ? { - id: yieldDto.providerId, - name: yieldDto.metadata.name, - logoURI: yieldDto.metadata.logoURI, - externalLink: undefined, - } - : null); + yieldDto.__fallback__.metadata.provider; export const hasYieldFeeConfigurationEnabled = (yieldDto: Yield) => (yieldDto.__fallback__.feeConfigurations?.length ?? 0) > 0; export const getYieldCooldownPeriod = (yieldDto: Yield) => - secondsToDays(yieldDto.mechanics.cooldownPeriod?.seconds) ?? - yieldDto.__fallback__.metadata.cooldownPeriod; + secondsToDays(yieldDto.mechanics.cooldownPeriod?.seconds); export const getYieldWarmupPeriod = (yieldDto: Yield) => - secondsToDays(yieldDto.mechanics.warmupPeriod?.seconds) ?? - yieldDto.__fallback__.metadata.warmupPeriod; + secondsToDays(yieldDto.mechanics.warmupPeriod?.seconds); export const getYieldWithdrawPeriod = (yieldDto: Yield) => yieldDto.__fallback__.metadata.withdrawPeriod; -export const getYieldRewardSchedule = (yieldDto: Yield) => - yieldDto.mechanics.rewardSchedule ?? - yieldDto.__fallback__.metadata.rewardSchedule; +export const getYieldCommission = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.commission; -export const getYieldRewardClaiming = (yieldDto: Yield) => - yieldDto.mechanics.rewardClaiming ?? - yieldDto.__fallback__.metadata.rewardClaiming; +export const getYieldTVL = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.tvl; -export const getYieldMetadata = (yieldDto: Yield): OldYieldDto["metadata"] => { - const fallbackMetadata = yieldDto.__fallback__.metadata; - const tokens = uniqTokens([ - ...(yieldDto.tokens ?? []), - ...(yieldDto.inputTokens ?? []), - ...(fallbackMetadata.tokens ?? []), - ]); +export const getYieldLockupPeriod = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.lockupPeriod; - return { - ...(fallbackMetadata ?? {}), - name: yieldDto.metadata.name ?? fallbackMetadata.name ?? "", - description: - yieldDto.metadata.description ?? fallbackMetadata.description ?? "", - documentation: - yieldDto.metadata.documentation ?? fallbackMetadata.documentation ?? "", - logoURI: yieldDto.metadata.logoURI ?? fallbackMetadata.logoURI ?? "", - type: getBaseYieldType(yieldDto), - token: yieldDto.token ?? fallbackMetadata.token, - tokens: tokens.length ? tokens : fallbackMetadata.tokens, - rewardTokens: getYieldRewardTokens(yieldDto), - rewardSchedule: getYieldRewardSchedule(yieldDto), - rewardClaiming: getYieldRewardClaiming(yieldDto), - cooldownPeriod: getYieldCooldownPeriod(yieldDto), - warmupPeriod: getYieldWarmupPeriod(yieldDto), - withdrawPeriod: getYieldWithdrawPeriod(yieldDto), - gasFeeToken: getYieldGasFeeToken(yieldDto), - provider: getYieldProviderDetails(yieldDto) ?? undefined, - supportsLedgerWalletApi: - yieldDto.mechanics.supportsLedgerWalletApi ?? - fallbackMetadata.supportsLedgerWalletApi, - supportsMultipleValidators: - yieldDto.mechanics.requiresValidatorSelection ?? - fallbackMetadata.supportsMultipleValidators, - supportedStandards: - yieldDto.metadata.supportedStandards ?? - fallbackMetadata.supportedStandards, - } as OldYieldDto["metadata"]; -}; +export const getYieldMetadataTokens = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.tokens ?? []; + +export const hasYieldExitSignatureVerification = (yieldDto: Yield) => + !!yieldDto.__fallback__.args.exit?.args?.signatureVerification?.required; export const getExtendedYieldType = (yieldDto: Yield) => isNativeStaking(yieldDto) diff --git a/packages/widget/src/hooks/api/use-tokens-prices.ts b/packages/widget/src/hooks/api/use-tokens-prices.ts index 4e007972..417f89e5 100644 --- a/packages/widget/src/hooks/api/use-tokens-prices.ts +++ b/packages/widget/src/hooks/api/use-tokens-prices.ts @@ -4,7 +4,6 @@ import { config } from "../../config"; import type { TokenDto, YieldTokenDto } from "../../domain/types/tokens"; import type { Yield } from "../../domain/types/yields"; import { useBaseToken } from "../use-base-token"; -import { useGasFeeToken } from "../use-gas-fee-token"; import { usePrices } from "./use-prices"; /** @@ -18,17 +17,20 @@ export const useTokensPrices = ({ yieldDto: Maybe; }) => { const baseToken = useBaseToken(yieldDto); - const gasFeeToken = useGasFeeToken(yieldDto); const priceRequestDto = useMemo( () => - Maybe.fromRecord({ baseToken, gasFeeToken, token }) + Maybe.fromRecord({ baseToken, yieldDto, token }) .map((val) => ({ currency: config.currency, - tokenList: [val.token, val.baseToken, val.gasFeeToken], + tokenList: [ + val.token, + val.baseToken, + val.yieldDto.mechanics.gasFeeToken, + ], })) .extractNullable(), - [baseToken, gasFeeToken, token] + [baseToken, yieldDto, token] ); return usePrices(priceRequestDto); diff --git a/packages/widget/src/hooks/api/use-yield-validators.ts b/packages/widget/src/hooks/api/use-yield-validators.ts index 655a5bc9..40e7e0e1 100644 --- a/packages/widget/src/hooks/api/use-yield-validators.ts +++ b/packages/widget/src/hooks/api/use-yield-validators.ts @@ -25,7 +25,7 @@ const getYieldValidatorsQueryKey = ({ yieldId }: Pick) => [ yieldId, ]; -export const getYieldValidatorsQueryFn = async ({ +const getYieldValidatorsQueryFn = async ({ yieldId, network, validatorsConfig, diff --git a/packages/widget/src/hooks/use-estimated-rewards.ts b/packages/widget/src/hooks/use-estimated-rewards.ts index 1bcfb894..e60e72ee 100644 --- a/packages/widget/src/hooks/use-estimated-rewards.ts +++ b/packages/widget/src/hooks/use-estimated-rewards.ts @@ -54,6 +54,8 @@ export const useEstimatedRewards = ({ .dividedBy(val.providersDetails.length), })) .map((val) => ({ + rewardRateAverage: val.rewardRateAverage, + rewardType: getYieldRewardType(val.selectedStake), percentage: getRewardRateFormatted({ rewardRate: val.rewardRateAverage.toNumber(), rewardType: getYieldRewardType(val.selectedStake), diff --git a/packages/widget/src/hooks/use-gas-fee-token.ts b/packages/widget/src/hooks/use-gas-fee-token.ts deleted file mode 100644 index 76bc914c..00000000 --- a/packages/widget/src/hooks/use-gas-fee-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Maybe } from "purify-ts"; -import { useMemo } from "react"; -import { getGasFeeToken } from "../domain"; -import type { Yield } from "../domain/types/yields"; - -export const useGasFeeToken = (yieldDto: Maybe) => - useMemo(() => yieldDto.map((val) => getGasFeeToken(val)), [yieldDto]); diff --git a/packages/widget/src/hooks/use-provider-details.ts b/packages/widget/src/hooks/use-provider-details.ts index 7fbd7aa4..e97deb35 100644 --- a/packages/widget/src/hooks/use-provider-details.ts +++ b/packages/widget/src/hooks/use-provider-details.ts @@ -3,10 +3,8 @@ import { useMemo } from "react"; import type { RewardTypes } from "../domain/types/reward-rate"; import type { ValidatorDto } from "../domain/types/validators"; import { - getYieldMetadata, getYieldProviderDetails, getYieldProviderYieldIds, - getYieldRewardRate, getYieldRewardType, isYieldWithProviderOptions, type Yield, @@ -45,9 +43,8 @@ export const getProviderDetails = ({ validatorsData?: ValidatorDto[]; }): Res => { const def = integrationData.chain((val) => { - const rewardRate = getYieldRewardRate(val); + const rewardRate = val.rewardRate.total; const rewardType = getYieldRewardType(val); - const metadata = getYieldMetadata(val); const provider = getYieldProviderDetails(val); const rewardRateFormatted = getRewardRateFormatted({ @@ -67,8 +64,8 @@ export const getProviderDetails = ({ })) .altLazy(() => Maybe.of({ - logo: metadata.logoURI, - name: metadata.name, + logo: val.metadata.logoURI, + name: val.metadata.name, rewardRateFormatted, rewardRate, rewardType, @@ -91,7 +88,7 @@ export const getProviderDetails = ({ .chain(({ selectedProviderYieldId }) => yields.chain(List.find((v) => v.id === selectedProviderYieldId)) ) - .map((v) => getYieldRewardRate(v) + getYieldRewardRate(v)) + .map((v) => v.rewardRate.total + v.rewardRate.total) .map<{ rewardRate: number | undefined; rewardType: RewardTypes }>( (res) => ({ rewardRate: res, diff --git a/packages/widget/src/hooks/use-summary.tsx b/packages/widget/src/hooks/use-summary.tsx index 1bfe5065..db017870 100644 --- a/packages/widget/src/hooks/use-summary.tsx +++ b/packages/widget/src/hooks/use-summary.tsx @@ -8,7 +8,7 @@ import type { StakeKitErrorDto } from "../domain/types/errors"; import { getPositionTotalAmount } from "../domain/types/positions"; import type { Prices } from "../domain/types/price"; import type { EnabledRewardsSummaryYieldId } from "../domain/types/rewards"; -import { getYieldRewardRate, type Yield } from "../domain/types/yields"; +import type { Yield } from "../domain/types/yields"; import { usePositions } from "../pages/details/positions-page/hooks/use-positions"; import { useMultiYields } from "./api/use-multi-yields"; import { usePrices } from "./api/use-prices"; @@ -246,7 +246,7 @@ export const SummaryProvider = ({ const usdAmount = positionTotalAmount.amountUsd; - const rewardRate = getYieldRewardRate(yieldDto); + const rewardRate = yieldDto.rewardRate.total; if (rewardRate > 0 && usdAmount.gt(0)) { return { diff --git a/packages/widget/src/hooks/use-yield-meta-info.tsx b/packages/widget/src/hooks/use-yield-meta-info.tsx index 3b703acf..4ecbb5dd 100644 --- a/packages/widget/src/hooks/use-yield-meta-info.tsx +++ b/packages/widget/src/hooks/use-yield-meta-info.tsx @@ -6,8 +6,14 @@ import { SKAnchor } from "../components/atoms/anchor"; import type { TokenDto, YieldTokenDto } from "../domain/types/tokens"; import type { ValidatorDto } from "../domain/types/validators"; import { - getYieldMetadata, + getBaseYieldType, + getYieldCooldownPeriod, + getYieldLockupPeriod, + getYieldProviderDetails, + getYieldRewardTokens, getYieldRewardType, + getYieldWarmupPeriod, + getYieldWithdrawPeriod, hasYieldFeeConfigurationEnabled, isEthenaUsdeStaking, type Yield, @@ -45,27 +51,26 @@ export const useYieldMetaInfo = ({ typeof ifNotFound >(({ selectedStake: y, tokenDto }) => { const sv = validatorsFormatted.extract(); - const metadata = getYieldMetadata(y); - const haveFeeConfigurationEnabled = hasYieldFeeConfigurationEnabled(y); - const stakeToken = tokenDto.symbol; - const rewardTokens = - metadata.rewardTokens - ?.filter((t) => !t.isPoints) - .map((t) => t.symbol) - .join(", ") ?? ""; - const providerName = - sv ?? (metadata.provider ? metadata.provider.name : metadata.name); - const rewardSchedule = metadata.rewardSchedule; - const cooldownPeriodDays = metadata.cooldownPeriod?.days ?? 0; - const warmupPeriodDays = metadata.warmupPeriod?.days ?? 0; - const rewardClaiming = metadata.rewardClaiming; + const rewardTokens = getYieldRewardTokens(y) + .filter((t) => !t.isPoints) + .map((t) => t.symbol) + .join(", "); + const provider = getYieldProviderDetails(y); + const providerName = sv ?? (provider ? provider.name : y.metadata.name); + const rewardSchedule = y.mechanics.rewardSchedule; + const cooldownPeriodDays = getYieldCooldownPeriod(y)?.days ?? 0; + const warmupPeriodDays = getYieldWarmupPeriod(y)?.days ?? 0; + const rewardClaiming = y.mechanics.rewardClaiming; + const lockupPeriodDays = getYieldLockupPeriod(y)?.days; + const yieldType = getBaseYieldType(y); + const withdrawPeriodDays = getYieldWithdrawPeriod(y)?.days ?? 0; const isCompound = providerName.includes("Compound"); if ( - metadata.rewardSchedule === "campaign" && + rewardSchedule === "campaign" && stakeToken.toUpperCase() === "SUSD" ) { return { @@ -105,7 +110,7 @@ export const useYieldMetaInfo = ({ const def = { campaign: - metadata.rewardSchedule === "campaign" ? ( + rewardSchedule === "campaign" ? ( ) : null, - lockupPeriod: metadata.lockupPeriod?.days + lockupPeriod: lockupPeriodDays ? t("details.lockup_period", { - count: metadata.lockupPeriod?.days, + count: lockupPeriodDays, }) : null, extra: @@ -127,12 +132,12 @@ export const useYieldMetaInfo = ({ ? t("details.reward_type_varialbe", { symbol: capitalizeFirstLowerRest(y.token.symbol), }) - : metadata.token.network === MiscNetworks.Tezos + : y.token.network === MiscNetworks.Tezos ? t("details.extra_tezos") : undefined, }; - switch (metadata.type) { + switch (yieldType) { case "staking": { return { description: null, @@ -249,12 +254,12 @@ export const useYieldMetaInfo = ({ }), withdrawnTime: y.status.exit ? cooldownPeriodDays > 0 - ? (metadata.withdrawPeriod?.days ?? 0) > 0 + ? withdrawPeriodDays > 0 ? t("details.liquid_stake.unstake_time_days_with_claim", { unstakeTime: t("details.liquid_stake.unstake_time", { count: cooldownPeriodDays, }), - count: metadata.withdrawPeriod?.days, + count: withdrawPeriodDays, }) : t("details.liquid_stake.unstake_time", { count: cooldownPeriodDays, diff --git a/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx index 39ece53c..776fe234 100644 --- a/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx +++ b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx @@ -6,7 +6,7 @@ import { ContentLoaderSquare } from "../../../components/atoms/content-loader"; import { ListItem } from "../../../components/atoms/list/list-item"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Text } from "../../../components/atoms/typography/text"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useActionListItem } from "../../../pages/details/activity-page/hooks/use-action-list-item"; import type { ActionYieldDto } from "../../../pages/details/activity-page/types"; import { @@ -64,7 +64,14 @@ export const ActionListItem = ({ justifyContent="flex-start" alignItems="center" > - + { const { t } = useTranslation(); - const yieldType = getYieldMetadata(integrationData).type; + const yieldType = getBaseYieldType(integrationData); const balanceTypeContext = yieldType === "vault" || yieldType === "lending" 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 3a637618..50d2aee6 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 @@ -10,7 +10,7 @@ import { TokenIcon } from "../../../../components/atoms/token-icon"; import { ToolTip } from "../../../../components/atoms/tooltip"; import { Text } from "../../../../components/atoms/typography/text"; import type { PositionDetailsLabelType } from "../../../../domain/types/positions"; -import { getYieldMetadata } from "../../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../../domain/types/yields"; import { columnContainer, listItem, @@ -62,7 +62,14 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), @@ -71,7 +78,7 @@ export const PositionsListItem = memo( - {getYieldMetadata(d).name} + {d.metadata.name} {item.yieldLabelDto .map((label) => { diff --git a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx index 2ac719d2..f95c5fb7 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/position-details-actions.tsx @@ -4,7 +4,7 @@ import { Box } from "../../../components/atoms/box"; import { Button } from "../../../components/atoms/button"; import { Spinner } from "../../../components/atoms/spinner"; import { SelectValidator } from "../../../components/molecules/select-validator"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getBaseYieldType } from "../../../domain/types/yields"; import { AmountBlock } from "../../../pages/position-details/components/amount-block"; import { StaticActionBlock } from "../../../pages/position-details/components/static-action-block"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; @@ -152,7 +152,7 @@ export const PositionDetailsActions = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${getYieldMetadata(v.integrationData).type}` + `position_details.unstake_label.${getBaseYieldType(v.integrationData)}` )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} 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 8c7da23f..c4378250 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 @@ -9,7 +9,7 @@ import { } from "../../../components/atoms/collapsible"; import { Spinner } from "../../../components/atoms/spinner"; import { Text } from "../../../components/atoms/typography/text"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getBaseYieldType } from "../../../domain/types/yields"; import { PositionBalances } from "../../../pages/position-details/components/position-balances"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; import { ProviderDetails } from "./provider-details"; @@ -58,7 +58,7 @@ export const PositionDetailsInfo = () => { {...p} key={p.address ?? idx} stakeType={t( - `position_details.stake_type.${getYieldMetadata(val.integrationData).type}` + `position_details.stake_type.${getBaseYieldType(val.integrationData)}` )} integrationData={val.integrationData} /> diff --git a/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx b/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx index 3c9b3e94..9931569a 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/top-header.tsx @@ -2,7 +2,7 @@ import { Just, Maybe } from "purify-ts"; import { Box } from "../../../components/atoms/box"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Text } from "../../../components/atoms/typography/text"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; import { topHeaderYieldName } from "./styles.css"; @@ -19,7 +19,12 @@ export const TopHeader = () => { <> { variant={{ weight: "normal" }} textAlign="center" > - {getYieldMetadata(val.integrationData).name} + {val.integrationData.metadata.name} {t.symbol} diff --git a/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx b/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx index cfe52a67..8fc049e9 100644 --- a/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx +++ b/packages/widget/src/pages-dashboard/rewards/components/positions-list-item.tsx @@ -10,7 +10,7 @@ import { TokenIcon } from "../../../components/atoms/token-icon"; import { ToolTip } from "../../../components/atoms/tooltip"; import { Text } from "../../../components/atoms/typography/text"; import type { PositionDetailsLabelType } from "../../../domain/types/positions"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { columnContainer, listItem, @@ -61,7 +61,14 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), diff --git a/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts b/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts index 2ac7d253..e43c0fcf 100644 --- a/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts +++ b/packages/widget/src/pages/complete/hooks/use-activity-complete.hook.ts @@ -6,7 +6,7 @@ import { getActionValidatorAddresses, } from "../../../domain/types/action"; import type { TokenDto } from "../../../domain/types/tokens"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; import { useYieldType } from "../../../hooks/use-yield-type"; @@ -50,7 +50,12 @@ export const useActivityComplete = () => { ) as Maybe; const metadata = useMemo( - () => selectedYield.map(getYieldMetadata), + () => + selectedYield.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })), [selectedYield] ); diff --git a/packages/widget/src/pages/complete/pages/common.page.tsx b/packages/widget/src/pages/complete/pages/common.page.tsx index da259f6f..8500846c 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -1,5 +1,6 @@ import { motion } from "motion/react"; import { Just, Maybe } from "purify-ts"; +import type { ComponentProps } from "react"; import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; import { CheckCircleIcon } from "../../../components/atoms/icons/check-circle"; @@ -13,7 +14,6 @@ import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import { type ExtendedYieldType, isEthenaUsdeStaking, - type YieldMetadata, } from "../../../domain/types/yields"; import { AnimationPage } from "../../../navigation/containers/animation-page"; import { capitalizeFirstLowerRest } from "../../../utils/text"; @@ -26,7 +26,7 @@ import { type Props = { token: Maybe; - metadata: Maybe; + metadata: Maybe["metadata"]>; network: string; amount: string; pendingActionType?: YieldPendingActionType; diff --git a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx index a403837c..6bc70b6c 100644 --- a/packages/widget/src/pages/complete/pages/pending-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/pending-complete.page.tsx @@ -2,7 +2,7 @@ import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useUnstakeOrPendingActionParams } from "../../../hooks/navigation/use-unstake-or-pending-action-params"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { usePositionBalances } from "../../../hooks/use-position-balances"; @@ -45,7 +45,11 @@ export const PendingCompletePage = () => { selectedProviderYieldId: Maybe.empty(), }); - const metadata = integrationData.map(getYieldMetadata); + const metadata = integrationData.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })); const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( () => diff --git a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx index 973c5978..a20d4f7d 100644 --- a/packages/widget/src/pages/complete/pages/stake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/stake-complete.page.tsx @@ -2,7 +2,7 @@ import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; import { useYieldType } from "../../../hooks/use-yield-type"; @@ -28,7 +28,11 @@ export const StakeCompletePage = () => { [enterRequest.selectedToken] ); - const metadata = selectedStake.map(getYieldMetadata); + const metadata = selectedStake.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })); const network = selectedToken.mapOrDefault((y) => y.symbol, ""); diff --git a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx index 61e9202d..153615df 100644 --- a/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx +++ b/packages/widget/src/pages/complete/pages/unstake-complete.page.tsx @@ -1,7 +1,7 @@ import { useSelector } from "@xstate/store/react"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useUnstakeOrPendingActionParams } from "../../../hooks/navigation/use-unstake-or-pending-action-params"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { usePositionBalances } from "../../../hooks/use-position-balances"; @@ -43,7 +43,11 @@ export const UnstakeCompletePage = () => { [exitRequest.unstakeToken] ); - const metadata = integrationData.map(getYieldMetadata); + const metadata = integrationData.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })); const network = token.mapOrDefault((t) => t.symbol, ""); const amount = useMemo( () => formatNumber(exitRequest.requestDto.arguments?.amount ?? 0), diff --git a/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx b/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx index 393a6000..07127712 100644 --- a/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx +++ b/packages/widget/src/pages/details/activity-page/components/action-list-item/index.tsx @@ -5,7 +5,7 @@ import { ContentLoaderSquare } from "../../../../../components/atoms/content-loa import { ListItem } from "../../../../../components/atoms/list/list-item"; import { TokenIcon } from "../../../../../components/atoms/token-icon"; import { Text } from "../../../../../components/atoms/typography/text"; -import { getYieldMetadata } from "../../../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../../../domain/types/yields"; import { listItemContainer } from "../../../positions-page/style.css"; import { useActionListItem } from "../../hooks/use-action-list-item"; import { @@ -50,7 +50,14 @@ export const ActionListItem = ({ justifyContent="flex-start" alignItems="center" > - + { > {data.ss.token.symbol} diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx index 1def972c..543ee6bd 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx @@ -4,7 +4,7 @@ import { Image } from "../../../../../components/atoms/image"; import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { - getYieldMetadata, + getBaseYieldType, getYieldProviderDetails, isYieldActionArgRequired, } from "../../../../../domain/types/yields"; @@ -19,7 +19,7 @@ export const StakedVia = () => { .filter( (val) => !!( - getYieldMetadata(val).type === "staking" && + getBaseYieldType(val) === "staking" && !isYieldActionArgRequired(val, "enter", "validatorAddress") && !isYieldActionArgRequired(val, "enter", "validatorAddresses") && getYieldProviderDetails(val) diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts b/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts index b9e392ca..01dfaeea 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/use-animate-yield-percent.ts @@ -1,6 +1,7 @@ import { animate, useMotionValue, useTransform } from "motion/react"; import { useEffect } from "react"; import { config } from "../../../../../config"; +import { APToPercentage } from "../../../../../utils"; import type { EarnPageContextType } from "../../state/types"; export const useAnimateYieldPercent = ( @@ -8,11 +9,14 @@ export const useAnimateYieldPercent = ( ) => { const perReward = estimatedRewards .map((val) => { - const parsedNum = Number.parseFloat(val.percentage); - - if (Number.isNaN(parsedNum)) return val.percentage; - - return parsedNum; + if ( + val.rewardType === "variable" || + !val.rewardRateAverage.isPositive() + ) { + return "- %"; + } + + return val.rewardRateAverage.toNumber(); }) .extractNullable(); @@ -33,7 +37,7 @@ export const useAnimateYieldPercent = ( const transformedMotionValue = useTransform( rewardPercMotionValue, - (val) => `${val.toFixed(2)}%` + (val) => `${APToPercentage(val)}%` ); return typeof perReward === "string" || config.env.isTestMode diff --git a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx index 5b2677c8..6f622409 100644 --- a/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx +++ b/packages/widget/src/pages/details/earn-page/state/earn-page-context.tsx @@ -28,7 +28,6 @@ import type { ValidatorDto } from "../../../../domain/types/validators"; import { type ExtendedYieldType, getExtendedYieldType, - getYieldRewardRate, getYieldRewardTokens, getYieldTypeLabels, getYieldTypesSortRank, @@ -244,7 +243,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { () => Maybe.of(multiYields) .map((val) => - [...val].sort((a, b) => getYieldRewardRate(b) - getYieldRewardRate(a)) + [...val].sort((a, b) => b.rewardRate.total - a.rewardRate.total) ) .map((val) => val.filter(isNonZeroRewardRateYield)) .chain((yieldDtos) => diff --git a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts index 9dcb1b47..17126add 100644 --- a/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts +++ b/packages/widget/src/pages/details/earn-page/state/use-stake-enter-request-dto.ts @@ -5,7 +5,6 @@ import type { AddressesDto } from "../../../../domain/types/addresses"; import type { ValidatorDto } from "../../../../domain/types/validators"; import { getYieldActionArg, - getYieldGasFeeToken, isYieldIntegrationAggregator, type Yield, } from "../../../../domain/types/yields"; @@ -81,7 +80,7 @@ export const useStakeEnterRequestDto = () => { return { selectedValidators, selectedStake: selectedStake, - gasFeeToken: getYieldGasFeeToken(selectedStake), + gasFeeToken: selectedStake.mechanics.gasFeeToken, addresses: { address, additionalAddresses: additionalAddresses ?? undefined, 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 7b8c780a..fbf262ec 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 @@ -10,7 +10,7 @@ import { TokenIcon } from "../../../../components/atoms/token-icon"; import { ToolTip } from "../../../../components/atoms/tooltip"; import { Text } from "../../../../components/atoms/typography/text"; import type { PositionDetailsLabelType } from "../../../../domain/types/positions"; -import { getYieldMetadata } from "../../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../../domain/types/yields"; import { usePositionListItem } from "../hooks/use-position-list-item"; import type { usePositions } from "../hooks/use-positions"; import { @@ -58,7 +58,14 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), 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 fc58cd43..bc41ca5f 100644 --- a/packages/widget/src/pages/position-details/components/position-balances.tsx +++ b/packages/widget/src/pages/position-details/components/position-balances.tsx @@ -6,7 +6,7 @@ import { Box } from "../../../components/atoms/box"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Text } from "../../../components/atoms/typography/text"; import type { YieldBalanceDto } from "../../../domain/types/positions"; -import { getYieldMetadata, type Yield } from "../../../domain/types/yields"; +import { getBaseYieldType, type Yield } from "../../../domain/types/yields"; import { defaultFormattedNumber } from "../../../utils"; import { formatDurationUntilDate } from "../../../utils/date"; @@ -42,7 +42,7 @@ export const PositionBalances = ({ return t("position_details.unstaking_duration", { duration }); }, [yieldBalance.date, yieldBalance.type, t]); - const yieldType = getYieldMetadata(integrationData).type; + const yieldType = getBaseYieldType(integrationData); const balanceTypeContext = yieldType === "vault" || yieldType === "lending" 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 ca0fe8f8..3ecc6d8a 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 @@ -4,7 +4,6 @@ import type { YieldCreateActionDto } from "../../../domain/types/action"; import type { AddressesDto } from "../../../domain/types/addresses"; import { getYieldActionArg, - getYieldGasFeeToken, isYieldIntegrationAggregator, type Yield, } from "../../../domain/types/yields"; @@ -96,7 +95,7 @@ export const useStakeExitRequestDto = () => { .orDefault({}); return { - gasFeeToken: getYieldGasFeeToken(val.integrationData), + gasFeeToken: val.integrationData.mechanics.gasFeeToken, addresses: { address: val.address, additionalAddresses: additionalAddresses ?? undefined, diff --git a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts index 0e8a18c0..c1e3738f 100644 --- a/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts +++ b/packages/widget/src/pages/position-details/hooks/use-unstake-machine.ts @@ -8,6 +8,7 @@ import { transactionGetTransactionVerificationMessageForNetwork } from "../../.. import { getValidStakeSessionTx } from "../../../domain"; import type { TransactionVerificationMessageDto } from "../../../domain/types/transaction"; import type { SKWallet } from "../../../domain/types/wallet"; +import { hasYieldExitSignatureVerification } from "../../../domain/types/yields"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { useSavedRef } from "../../../hooks/use-saved-ref"; import { useExitStakeStore } from "../../../providers/exit-stake-store"; @@ -138,10 +139,7 @@ const getMachine = ( amount: val.requestDto.arguments?.amount, }); - if ( - val.integrationData.__fallback__.args.exit?.args - ?.signatureVerification?.required - ) { + if (hasYieldExitSignatureVerification(val.integrationData)) { self.send({ type: "__GET_VERIFICATION_MESSAGE__", val }); } else { self.send({ type: "__SUBMIT__", val }); diff --git a/packages/widget/src/pages/position-details/hooks/utils.ts b/packages/widget/src/pages/position-details/hooks/utils.ts index 8651058a..10aa7caa 100644 --- a/packages/widget/src/pages/position-details/hooks/utils.ts +++ b/packages/widget/src/pages/position-details/hooks/utils.ts @@ -10,7 +10,7 @@ import { } from "../../../domain/types/pending-action"; import type { ValidatorDto } from "../../../domain/types/validators"; import type { SKWallet } from "../../../domain/types/wallet"; -import { getYieldGasFeeToken, type Yield } from "../../../domain/types/yields"; +import type { Yield } from "../../../domain/types/yields"; import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; import type { YieldBalanceDto, @@ -96,7 +96,7 @@ export const preparePendingActionRequestDto = ({ }, address: val, additionalAddresses: additionalAddresses ?? undefined, - gasFeeToken: getYieldGasFeeToken(integration), + gasFeeToken: integration.mechanics.gasFeeToken, integrationData: integration, }; }); 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 18becaff..8dfd58c1 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -9,7 +9,10 @@ import { Text } from "../../components/atoms/typography/text"; import { RewardRateBreakdown } from "../../components/molecules/reward-rate-breakdown"; import { SelectValidator } from "../../components/molecules/select-validator"; import { getRewardTypeFromRateType } from "../../domain/types/reward-rate"; -import { getYieldMetadata } from "../../domain/types/yields"; +import { + getBaseYieldType, + getYieldProviderDetails, +} from "../../domain/types/yields"; import { useTrackPage } from "../../hooks/tracking/use-track-page"; import { AnimationPage } from "../../navigation/containers/animation-page"; import type { YieldPendingActionType } from "../../providers/yield-api-client-provider/types"; @@ -91,11 +94,13 @@ const PositionDetails = () => { alignItems="center" > [0]["metadata"] - } + metadata={{ + logoURI: integrationData.metadata.logoURI, + name: integrationData.metadata.name, + provider: + getYieldProviderDetails(integrationData) ?? + undefined, + }} token={t} tokenLogoHw="14" /> @@ -164,7 +169,7 @@ const PositionDetails = () => { personalizedRewardRate ? undefined : p.rewardType } stakeType={t( - `position_details.stake_type.${getYieldMetadata(integrationData).type}` + `position_details.stake_type.${getBaseYieldType(integrationData)}` )} integrationData={integrationData} /> @@ -287,7 +292,7 @@ const PositionDetails = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${getYieldMetadata(integrationData).type}` + `position_details.unstake_label.${getBaseYieldType(integrationData)}` )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} diff --git a/packages/widget/src/pages/review/hooks/use-action-review.hook.ts b/packages/widget/src/pages/review/hooks/use-action-review.hook.ts index 3878914e..8a5c0ec4 100644 --- a/packages/widget/src/pages/review/hooks/use-action-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-action-review.hook.ts @@ -10,7 +10,7 @@ import { TransactionStatus, } from "../../../domain/types/action"; import type { TokenDto } from "../../../domain/types/tokens"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getBaseYieldType } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { useYieldType } from "../../../hooks/use-yield-type"; import { useActivityContext } from "../../../providers/activity-provider"; @@ -69,7 +69,7 @@ export const useActionReview = () => { ); const unstakeTitle = useMemo(() => { - switch (getYieldMetadata(selectedYield).type) { + switch (getBaseYieldType(selectedYield)) { case "staking": case "liquid-staking": return t("position_details.unstake") as string; diff --git a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts index 55574ba5..c975b7fc 100644 --- a/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-stake-review.hook.ts @@ -6,7 +6,10 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import { getTransactionGasEstimate } from "../../../domain/types/action"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { + getYieldCommission, + getYieldProviderDetails, +} from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; import { useEstimatedRewards } from "../../../hooks/use-estimated-rewards"; import { useGasWarningCheck } from "../../../hooks/use-gas-warning-check"; @@ -64,8 +67,6 @@ export const useStakeReview = () => { [actionPreviewQuery.data] ); - console.log({ stakeEnterTxGas }); - const gasCheckWarning = useGasWarningCheck({ gasAmount: stakeEnterTxGas, gasFeeToken: enterRequest.gasFeeToken, @@ -148,7 +149,11 @@ export const useStakeReview = () => { ), }); - const metadata = selectedStake.map(getYieldMetadata); + const metadata = selectedStake.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })); const navigate = useNavigate(); @@ -201,7 +206,7 @@ export const useStakeReview = () => { const commissionFee = useMemo( () => selectedStake - .chainNullable((y) => getYieldMetadata(y).commission) + .chainNullable(getYieldCommission) .map((commission) => commission.reduce((acc, curr) => acc + curr.value, 0) ) diff --git a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts index 77b8f809..0dc38d8c 100644 --- a/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts +++ b/packages/widget/src/pages/review/hooks/use-unstake-review.hook.ts @@ -9,7 +9,7 @@ import { useNavigate } from "react-router"; import type { RewardTokenDetails } from "../../../components/molecules/reward-token-details"; import { getTransactionGasEstimate } from "../../../domain/types/action"; import { - getYieldMetadata, + getBaseYieldType, getYieldProviderDetails, } from "../../../domain/types/yields"; import { useTokensPrices } from "../../../hooks/api/use-tokens-prices"; @@ -92,7 +92,7 @@ export const useUnstakeActionReview = () => { const formattedAmount = useMemo(() => formatNumber(amount), [amount]); const title: Maybe = integrationData.map((d) => { - switch (getYieldMetadata(d).type) { + switch (getBaseYieldType(d)) { case "staking": case "liquid-staking": return t("position_details.unstake") as string; diff --git a/packages/widget/src/pages/review/pages/action-review.page.tsx b/packages/widget/src/pages/review/pages/action-review.page.tsx index dad7b9a5..0f26cd6a 100644 --- a/packages/widget/src/pages/review/pages/action-review.page.tsx +++ b/packages/widget/src/pages/review/pages/action-review.page.tsx @@ -6,7 +6,7 @@ import { Divider } from "../../../components/atoms/divider"; import { InfoIcon } from "../../../components/atoms/icons/info"; import { ToolTip } from "../../../components/atoms/tooltip"; import { Text } from "../../../components/atoms/typography/text"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; import { AnimationPage } from "../../../navigation/containers/animation-page"; import { capitalizeFirstLetters } from "../../../utils/formatters"; @@ -41,7 +41,11 @@ export const ActionReviewPage = () => { 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 eff8f78e..92038dac 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 @@ -10,7 +10,6 @@ import { Text } from "../../../../components/atoms/typography/text"; import { WarningBox } from "../../../../components/atoms/warning-box"; import type { RewardTokenDetails } from "../../../../components/molecules/reward-token-details"; import type { TokenDto, YieldTokenDto } from "../../../../domain/types/tokens"; -import type { YieldMetadata } from "../../../../domain/types/yields"; import { useTrackEvent } from "../../../../hooks/tracking/use-track-event"; import { AnimationPage } from "../../../../navigation/containers/animation-page"; import { MetaInfo } from "../../../components/meta-info"; @@ -27,7 +26,7 @@ type ReviewPageProps = { fee: string; title: string; token: Maybe; - metadata: Maybe; + metadata: ComponentProps["metadata"]; info: ReactNode; rewardTokenDetailsProps: Maybe>; isGasCheckError: boolean; 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 bec2fbf1..472de350 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 @@ -11,13 +11,12 @@ import type { TokenDto, YieldTokenDto, } from "../../../../../domain/types/tokens"; -import type { YieldMetadata } from "../../../../../domain/types/yields"; import { headingStyles } from "../../style.css"; type Props = { title: string; token: Maybe; - metadata: Maybe; + metadata: Maybe["metadata"]>; info: ReactNode; rewardTokenDetailsProps?: Maybe>; }; diff --git a/packages/widget/src/pages/review/pages/pending-review.page.tsx b/packages/widget/src/pages/review/pages/pending-review.page.tsx index 60df3098..ef380663 100644 --- a/packages/widget/src/pages/review/pages/pending-review.page.tsx +++ b/packages/widget/src/pages/review/pages/pending-review.page.tsx @@ -1,6 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { usePendingActionReview } from "../hooks/use-pending-review.hook"; import { ReviewPage } from "./common-page/common.page"; @@ -46,7 +46,11 @@ export const PendingReviewPage = () => { performanceFee={performanceFee} feeConfigLoading={feeConfigLoading} info={info} - metadata={integrationData.map(getYieldMetadata)} + metadata={integrationData.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + }))} token={token} isGasCheckError={isGasCheckWarning} loading={gasCheckLoading} diff --git a/packages/widget/src/pages/review/pages/unstake-review.page.tsx b/packages/widget/src/pages/review/pages/unstake-review.page.tsx index 07c85099..d9f6a0a6 100644 --- a/packages/widget/src/pages/review/pages/unstake-review.page.tsx +++ b/packages/widget/src/pages/review/pages/unstake-review.page.tsx @@ -1,6 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; -import { getYieldMetadata } from "../../../domain/types/yields"; +import { getYieldProviderDetails } from "../../../domain/types/yields"; import { useTrackPage } from "../../../hooks/tracking/use-track-page"; import { UnstakeSignPopup } from "../../position-details/components/unstake-sign-popup"; import { useUnstakeActionReview } from "../hooks/use-unstake-review.hook"; @@ -51,7 +51,11 @@ export const UnstakeReviewPage = () => { performanceFee={performanceFee} feeConfigLoading={feeConfigLoading} info={info} - metadata={integrationData.map(getYieldMetadata)} + metadata={integrationData.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + }))} token={token} isGasCheckError={isGasCheckWarning} loading={gasCheckLoading} diff --git a/packages/widget/src/utils/formatters.ts b/packages/widget/src/utils/formatters.ts index ab77576d..64c3e4ce 100644 --- a/packages/widget/src/utils/formatters.ts +++ b/packages/widget/src/utils/formatters.ts @@ -4,7 +4,7 @@ import { getTokenPriceInUSD } from "../domain"; import { Prices } from "../domain/types/price"; import type { RewardTypes } from "../domain/types/reward-rate"; import type { TokenDto, YieldTokenDto } from "../domain/types/tokens"; -import { getYieldGasFeeToken, type Yield } from "../domain/types/yields"; +import type { Yield } from "../domain/types/yields"; import { APToPercentage, defaultFormattedNumber, formatNumber } from "."; export const formatCountryCode = ({ @@ -61,14 +61,14 @@ export const getGasFeeInUSD = ({ gasFeeInUSD: getTokenPriceInUSD({ amount: val.gas.toString(), prices: prices.orDefault(new Prices(new Map())), - token: getYieldGasFeeToken(val.yieldDto), + token: val.yieldDto.mechanics.gasFeeToken, pricePerShare: null, baseToken: null, }), })) .mapOrDefault( (val) => - `${formatNumber(val.gas, 10)} ${getYieldGasFeeToken(val.yieldDto).symbol} ${ + `${formatNumber(val.gas, 10)} ${val.yieldDto.mechanics.gasFeeToken.symbol} ${ val.gasFeeInUSD.isGreaterThan(0) ? ` ($${defaultFormattedNumber(val.gasFeeInUSD)})` : "" diff --git a/packages/widget/src/utils/index.ts b/packages/widget/src/utils/index.ts index e3a18578..a0063eb2 100644 --- a/packages/widget/src/utils/index.ts +++ b/packages/widget/src/utils/index.ts @@ -40,7 +40,8 @@ export const fromWei = (amount: string | BigNumber, decimals: number) => export const defaultFormattedNumber = (number: string | BigNumber) => formatNumber(number, 6); -export const APToPercentage = (ap: number) => (ap * 100).toFixed(2); +export const APToPercentage = (ap: number) => + formatNumber((ap * 100).toFixed(2)); const colorsTuple = ["#6B69D6", "#F1C40F", "#1ABC9C", "#E74C3C"]; diff --git a/packages/widget/src/worker.ts b/packages/widget/src/worker.ts index fb4b7385..80e878a0 100644 --- a/packages/widget/src/worker.ts +++ b/packages/widget/src/worker.ts @@ -1,7 +1,13 @@ import { HttpResponse, http } from "msw"; import { setupWorker } from "msw/browser"; +import { config } from "./config"; const yieldId = "ethereum-matic-native-staking"; +const getApiRoute = (baseUrl: string, path: string) => + new URL(path.startsWith("/") ? path : `/${path}`, baseUrl).toString(); +const legacyApiRoute = (path: string) => getApiRoute(config.env.apiUrl, path); +const yieldApiRoute = (path: string) => + getApiRoute(config.env.yieldsApiUrl, path); const maticToken = { address: "0x0000000000000000000000000000000000001010", @@ -216,15 +222,12 @@ const yieldApiYieldDto = { }; export const worker = setupWorker( - http.get("*/v1/yields/ethereum-matic-native-staking", async ({ request }) => { - const url = new URL(request.url); - - return HttpResponse.json( - url.searchParams.has("ledgerWalletAPICompatible") - ? legacyYieldDto - : yieldApiYieldDto - ); - }), + http.get(legacyApiRoute(`/v1/yields/${yieldId}`), async () => + HttpResponse.json(legacyYieldDto) + ), + http.get(yieldApiRoute(`/v1/yields/${yieldId}`), async () => + HttpResponse.json(yieldApiYieldDto) + ), http.post("*/v1/yields/balances", async () => { return HttpResponse.json({ items: [ diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index 6c4b0d1b..2ad82c0b 100644 --- a/packages/widget/tests/fixtures/index.ts +++ b/packages/widget/tests/fixtures/index.ts @@ -14,6 +14,7 @@ import type { YieldRewardRateDto, YieldTransactionDto, } from "../../src/providers/yield-api-client-provider/types"; +import type { components } from "../../src/types/yield-api-schema"; type LegacyActionDto = ReturnType; type LegacyTransactionDto = ReturnType< @@ -24,6 +25,38 @@ type LegacyYieldDto = ReturnType< >; const apyFaker = () => faker.number.float({ min: 0, max: 0.05 }); +type YieldArgumentFieldName = components["schemas"]["ArgumentFieldDto"]["name"]; +type YieldArgumentFieldType = components["schemas"]["ArgumentFieldDto"]["type"]; + +const yieldArgumentFieldNames = new Set([ + "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", +]); export const yieldRewardRateFixture = ( overrides?: Partial @@ -43,6 +76,132 @@ export const yieldApiYieldFixture = ( ...overrides, }) as YieldApiYieldDto; +const mapYieldArgumentFields = ( + args?: Record< + string, + { + required?: boolean; + minimum?: number | null; + maximum?: number | null; + options?: string[]; + } + > +) => + Object.entries(args ?? {}).flatMap(([name, config]) => { + if (!yieldArgumentFieldNames.has(name as YieldArgumentFieldName)) { + return []; + } + + const type: YieldArgumentFieldType = (() => { + if (name === "validatorAddress" || name === "validatorAddresses") { + return "address"; + } + + if (config?.options?.length) { + return "enum"; + } + + return "string"; + })(); + + return [ + { + name: name as YieldArgumentFieldName, + type, + label: name, + required: config?.required, + minimum: + config?.minimum === null || config?.minimum === undefined + ? undefined + : String(config.minimum), + maximum: + config?.maximum === null || config?.maximum === undefined + ? undefined + : String(config.maximum), + options: config?.options, + isArray: name === "validatorAddresses" ? true : undefined, + }, + ]; + }); + +export const yieldApiYieldFixtureFromLegacy = ({ + legacyYield, + overrides, +}: { + legacyYield: LegacyYieldDto; + overrides?: Partial; +}): YieldApiYieldDto => + ({ + id: legacyYield.id, + network: legacyYield.token.network, + inputTokens: legacyYield.tokens?.length + ? legacyYield.tokens + : [legacyYield.token], + outputToken: legacyYield.metadata.rewardTokens?.[0] ?? legacyYield.token, + token: legacyYield.token, + tokens: legacyYield.tokens, + rewardRate: yieldRewardRateFixture({ + total: + typeof legacyYield.rewardRate === "number" + ? legacyYield.rewardRate + : apyFaker(), + rateType: legacyYield.rewardType?.toUpperCase() === "APR" ? "APR" : "APY", + }), + status: legacyYield.status, + metadata: { + name: legacyYield.metadata.name, + description: legacyYield.metadata.description ?? "", + documentation: legacyYield.metadata.documentation ?? "", + logoURI: legacyYield.metadata.logoURI ?? "", + underMaintenance: false, + deprecated: false, + supportedStandards: [], + }, + mechanics: { + type: + legacyYield.metadata.type === "liquid-staking" + ? "staking" + : legacyYield.metadata.type, + requiresValidatorSelection: legacyYield.validators.length > 0, + rewardSchedule: legacyYield.metadata.rewardSchedule ?? "day", + rewardClaiming: legacyYield.metadata.rewardClaiming ?? "auto", + gasFeeToken: legacyYield.metadata.gasFeeToken ?? legacyYield.token, + fee: legacyYield.metadata.fee, + supportsLedgerWalletApi: legacyYield.metadata.supportsLedgerWalletApi, + arguments: { + enter: { + fields: mapYieldArgumentFields( + legacyYield.args.enter?.args as Record< + string, + { + required?: boolean; + minimum?: number | null; + maximum?: number | null; + options?: string[]; + } + > + ), + }, + exit: { + fields: mapYieldArgumentFields( + legacyYield.args.exit?.args as Record< + string, + { + required?: boolean; + minimum?: number | null; + maximum?: number | null; + options?: string[]; + } + > + ), + }, + }, + }, + providerId: legacyYield.metadata.provider?.id ?? "unknown", + validators: legacyYield.validators, + ...overrides, + }) as YieldApiYieldDto; + export const yieldBalanceFixture = ( overrides?: Partial ): YieldBalanceDto => { diff --git a/packages/widget/tests/mocks/api-routes.ts b/packages/widget/tests/mocks/api-routes.ts new file mode 100644 index 00000000..e9b7b3c2 --- /dev/null +++ b/packages/widget/tests/mocks/api-routes.ts @@ -0,0 +1,10 @@ +import { config } from "../../src/config"; + +const getApiRoute = (baseUrl: string, path: string) => + new URL(path.startsWith("/") ? path : `/${path}`, baseUrl).toString(); + +export const legacyApiRoute = (path: string) => + getApiRoute(config.env.apiUrl, path); + +export const yieldApiRoute = (path: string) => + getApiRoute(config.env.yieldsApiUrl, path); 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 9a7d8fea..e5df3511 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -7,9 +7,11 @@ import { pendingActionFixture, yieldApiActionFixture, yieldApiTransactionFixture, + yieldApiYieldFixtureFromLegacy, yieldFixture, yieldValidatorsFixture, } from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; import { setUrl as _setUrl } from "./utils"; @@ -108,6 +110,13 @@ export const setup = async (opts?: { ) .unsafeCoerce(); + const avaxNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: avaxNativeStaking, + }); + const avaxLiquidStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: avaxLiquidStaking, + }); + const avaxLiquidStakingBalances = [ { groupId: "b4684f63-fe54-540d-b0ae-06a2c2ecdb9e", @@ -180,9 +189,9 @@ export const setup = async (opts?: { { token: ether, availableYields: ["ethereum-eth-etherfi-staking"] }, ]); }), - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); - return HttpResponse.json([token.network, ether.network]); + return HttpResponse.json([{ id: token.network }, { id: ether.network }]); }), http.post("*/v1/tokens/balances/scan", async () => { @@ -210,14 +219,22 @@ export const setup = async (opts?: { }, }); }), - http.get(`*/v1/yields/${avaxNativeStaking.id}`, async () => { + http.get(legacyApiRoute(`/v1/yields/${avaxNativeStaking.id}`), async () => { await delay(); return HttpResponse.json(avaxNativeStaking); }), - http.get(`*/v1/yields/${avaxLiquidStaking.id}`, async () => { + http.get(yieldApiRoute(`/v1/yields/${avaxNativeStaking.id}`), async () => { + await delay(); + return HttpResponse.json(avaxNativeStakingYieldApi); + }), + http.get(legacyApiRoute(`/v1/yields/${avaxLiquidStaking.id}`), async () => { await delay(); return HttpResponse.json(avaxLiquidStaking); }), + http.get(yieldApiRoute(`/v1/yields/${avaxLiquidStaking.id}`), async () => { + await delay(); + return HttpResponse.json(avaxLiquidStakingYieldApi); + }), http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); diff --git a/packages/widget/tests/use-cases/external-provider/setup.ts b/packages/widget/tests/use-cases/external-provider/setup.ts index 66811837..c4623b95 100644 --- a/packages/widget/tests/use-cases/external-provider/setup.ts +++ b/packages/widget/tests/use-cases/external-provider/setup.ts @@ -1,6 +1,11 @@ import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; -import { yieldFixture, yieldValidatorsFixture } from "../../fixtures"; +import { + yieldApiYieldFixtureFromLegacy, + yieldFixture, + yieldValidatorsFixture, +} from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; import { worker } from "../../mocks/worker"; type LegacyTokenDto = ReturnType["token"]; @@ -114,14 +119,27 @@ export const setup = () => { ) .unsafeCoerce(); + const etherNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: etherNativeStaking, + }); + const avalancheAvaxNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: avalancheAvaxNativeStaking, + }); + const solanaNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: solanaNativeStaking, + }); + const tonNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: tonNativeStaking, + }); + worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); return HttpResponse.json([ - etherNativeStaking.token.network, - avalancheAvaxNativeStaking.token.network, - solanaNativeStaking.token.network, - tonNativeStaking.token.network, + { id: etherNativeStaking.token.network }, + { id: avalancheAvaxNativeStaking.token.network }, + { id: solanaNativeStaking.token.network }, + { id: tonNativeStaking.token.network }, ]); }), @@ -165,25 +183,60 @@ export const setup = () => { ]); }), - http.get(`*/v1/yields/${etherNativeStaking.id}`, async () => { - await delay(); + http.get( + legacyApiRoute(`/v1/yields/${etherNativeStaking.id}`), + async () => { + await delay(); - return HttpResponse.json(etherNativeStaking); - }), - http.get(`*/v1/yields/${avalancheAvaxNativeStaking.id}`, async () => { + return HttpResponse.json(etherNativeStaking); + } + ), + http.get(yieldApiRoute(`/v1/yields/${etherNativeStaking.id}`), async () => { await delay(); - return HttpResponse.json(avalancheAvaxNativeStaking); + return HttpResponse.json(etherNativeStakingYieldApi); }), - http.get(`*/v1/yields/${solanaNativeStaking.id}`, async () => { + http.get( + legacyApiRoute(`/v1/yields/${avalancheAvaxNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(avalancheAvaxNativeStaking); + } + ), + http.get( + yieldApiRoute(`/v1/yields/${avalancheAvaxNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(avalancheAvaxNativeStakingYieldApi); + } + ), + http.get( + legacyApiRoute(`/v1/yields/${solanaNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(solanaNativeStaking); + } + ), + http.get( + yieldApiRoute(`/v1/yields/${solanaNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(solanaNativeStakingYieldApi); + } + ), + http.get(legacyApiRoute(`/v1/yields/${tonNativeStaking.id}`), async () => { await delay(); - return HttpResponse.json(solanaNativeStaking); + return HttpResponse.json(tonNativeStaking); }), - http.get(`*/v1/yields/${tonNativeStaking.id}`, async () => { + http.get(yieldApiRoute(`/v1/yields/${tonNativeStaking.id}`), async () => { await delay(); - return HttpResponse.json(tonNativeStaking); + return HttpResponse.json(tonNativeStakingYieldApi); }), http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); diff --git a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts index 5eb901f6..c76477ae 100644 --- a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts +++ b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts @@ -8,9 +8,11 @@ import { transactionConstructFixture, yieldApiActionFixture, yieldApiTransactionFixture, + yieldApiYieldFixtureFromLegacy, yieldFixture, yieldValidatorsFixture, } from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; @@ -110,6 +112,14 @@ export const setup = () => { })) .unsafeCoerce(); + const yieldWithSameGasAndStakeTokenYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: yieldWithSameGasAndStakeToken.yieldDto, + }); + const yieldWithDifferentGasAndStakeTokenYieldApi = + yieldApiYieldFixtureFromLegacy({ + legacyYield: yieldWithDifferentGasAndStakeToken.yieldDto, + }); + let avalancheCTokenAmount = "0"; let usdcTokenAmount = "0"; @@ -135,9 +145,9 @@ export const setup = () => { }) => yieldsTxGasAmountMap.set(yieldId, amount); worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); - return HttpResponse.json([avalancheCToken.network]); + return HttpResponse.json([{ id: avalancheCToken.network }]); }), http.get("*/v1/tokens", async () => { @@ -186,7 +196,7 @@ export const setup = () => { }), http.get( - `*/v1/yields/${yieldWithSameGasAndStakeToken.yieldDto.id}`, + legacyApiRoute(`/v1/yields/${yieldWithSameGasAndStakeToken.yieldDto.id}`), async () => { await delay(); @@ -194,13 +204,33 @@ export const setup = () => { } ), http.get( - `*/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}`, + yieldApiRoute(`/v1/yields/${yieldWithSameGasAndStakeToken.yieldDto.id}`), + async () => { + await delay(); + + return HttpResponse.json(yieldWithSameGasAndStakeTokenYieldApi); + } + ), + http.get( + legacyApiRoute( + `/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}` + ), async () => { await delay(); return HttpResponse.json(yieldWithDifferentGasAndStakeToken.yieldDto); } ), + http.get( + yieldApiRoute( + `/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}` + ), + async () => { + await delay(); + + return HttpResponse.json(yieldWithDifferentGasAndStakeTokenYieldApi); + } + ), http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); @@ -240,7 +270,10 @@ export const setup = () => { yieldApiTransactionFixture( tx as ReturnType, { - gasEstimate: gasAmount, + gasEstimate: JSON.stringify({ + amount: gasAmount, + token: avalancheCToken, + }), status: "CREATED", stepIndex: index, } diff --git a/packages/widget/tests/use-cases/geo-block.test.tsx b/packages/widget/tests/use-cases/geo-block.test.tsx index 209af567..db7aaced 100644 --- a/packages/widget/tests/use-cases/geo-block.test.tsx +++ b/packages/widget/tests/use-cases/geo-block.test.tsx @@ -1,12 +1,13 @@ import { HttpResponse, http } from "msw"; import { describe, expect, it } from "vitest"; +import { yieldApiRoute } from "../mocks/api-routes"; import { worker } from "../mocks/worker"; import { renderApp } from "../utils/test-utils"; describe("Geo block", () => { it("Show geo block popup", async () => { worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { return HttpResponse.json( { code: 403, diff --git a/packages/widget/tests/use-cases/renders-initial-page.test.tsx b/packages/widget/tests/use-cases/renders-initial-page.test.tsx index 95b4c499..d3f98020 100644 --- a/packages/widget/tests/use-cases/renders-initial-page.test.tsx +++ b/packages/widget/tests/use-cases/renders-initial-page.test.tsx @@ -1,7 +1,8 @@ import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { describe, expect, it } from "vitest"; -import { yieldFixture } from "../fixtures"; +import { yieldApiYieldFixtureFromLegacy, yieldFixture } from "../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../mocks/api-routes"; import { worker } from "../mocks/worker"; import { renderApp } from "../utils/test-utils"; @@ -61,12 +62,19 @@ describe("Renders initial page", () => { ) .unsafeCoerce(); + const avalancheAvaxNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: avalancheAvaxNativeStaking, + }); + const etherNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: etherNativeStaking, + }); + worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); return HttpResponse.json([ - etherNativeStaking.token.network, - avalancheAvaxNativeStaking.token.network, + { id: etherNativeStaking.token.network }, + { id: avalancheAvaxNativeStaking.token.network }, ]); }), @@ -82,16 +90,38 @@ describe("Renders initial page", () => { ]); }), - http.get(`*/v1/yields/${etherNativeStaking.id}`, async () => { - await delay(); + http.get( + legacyApiRoute(`/v1/yields/${etherNativeStaking.id}`), + async () => { + await delay(); - return HttpResponse.json(etherNativeStaking); - }), - http.get(`*/v1/yields/${avalancheAvaxNativeStaking.id}`, async () => { - await delay(); + return HttpResponse.json(etherNativeStaking); + } + ), + http.get( + yieldApiRoute(`/v1/yields/${etherNativeStaking.id}`), + async () => { + await delay(); - return HttpResponse.json(avalancheAvaxNativeStaking); - }) + return HttpResponse.json(etherNativeStakingYieldApi); + } + ), + http.get( + legacyApiRoute(`/v1/yields/${avalancheAvaxNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(avalancheAvaxNativeStaking); + } + ), + http.get( + yieldApiRoute(`/v1/yields/${avalancheAvaxNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(avalancheAvaxNativeStakingYieldApi); + } + ) ); const app = await renderApp(); diff --git a/packages/widget/tests/use-cases/select-opportunity.test.tsx b/packages/widget/tests/use-cases/select-opportunity.test.tsx index 59e10e78..98c32812 100644 --- a/packages/widget/tests/use-cases/select-opportunity.test.tsx +++ b/packages/widget/tests/use-cases/select-opportunity.test.tsx @@ -2,7 +2,8 @@ import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { describe, expect, it } from "vitest"; import { userEvent } from "vitest/browser"; -import { yieldFixture } from "../fixtures"; +import { yieldApiYieldFixtureFromLegacy, yieldFixture } from "../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../mocks/api-routes"; import { worker } from "../mocks/worker"; import { renderApp } from "../utils/test-utils"; @@ -11,6 +12,8 @@ type LegacyTokenDto = ReturnType["token"]; describe("Select opportunity", () => { // This loads cosmos wagmi config, which takes some time, so we need to increase the timeout it("Works as expected", { timeout: 20000 }, async () => { + window.history.pushState({}, "", "/"); + const token: LegacyTokenDto = { network: "ethereum", name: "Ethereum", @@ -19,58 +22,96 @@ describe("Select opportunity", () => { coinGeckoId: "ethereum", logoURI: "https://assets.stakek.it/tokens/eth.svg", }; + const yieldIds = [ + "ethereum-eth-lido-staking", + "ethereum-eth-stakewise-staking", + "ethereum-eth-reth-staking", + ] as const; + + const getLegacyYield = (integrationId: (typeof yieldIds)[number]) => + Just(yieldFixture()) + .map((mock) => { + const rewardToken = (() => { + switch (integrationId) { + case "ethereum-eth-reth-staking": + return { ...token, name: "Rocket Pool ETH", symbol: "rETH" }; + case "ethereum-eth-lido-staking": + return { ...token, name: "Lido Staked ETH", symbol: "stETH" }; + default: + return { ...token, name: "Banana ETH", symbol: "bananaETH" }; + } + })(); + + return { + ...mock, + id: integrationId, + args: { enter: { args: { nfts: undefined } } }, + token, + metadata: { + ...mock.metadata, + type: "liquid-staking", + rewardTokens: [rewardToken], + provider: { name: "Stakewise" }, + }, + status: { + ...mock.status, + enter: integrationId !== "ethereum-eth-stakewise-staking", + }, + } as ReturnType; + }) + .unsafeCoerce(); worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); return HttpResponse.json([ - "ethereum", - "ethereum-goerli", - "avalanche-c", - "celo", - "akash", - "cosmos", - "kava", - "osmosis", - "juno", - "stargaze", - "persistence", - "axelar", - "onomy", - "quicksilver", - "agoric", - "band-protocol", - "bitsong", - "chihuahua", - "comdex", - "crescent", - "cronos", - "cudos", - "fetch-ai", - "gravity-bridge", - "injective", - "irisnet", - "ki-network", - "mars-protocol", - "regen", - "secret", - "sentinel", - "sommelier", - "teritori", - "umee", - "coreum", - "desmos", - "dydx", - "optimism", - "fantom", - "arbitrum", - "polygon", - "binance", - "near", - "harmony", - "solana", - "tezos", + { id: "ethereum" }, + { id: "ethereum-goerli" }, + { id: "avalanche-c" }, + { id: "celo" }, + { id: "akash" }, + { id: "cosmos" }, + { id: "kava" }, + { id: "osmosis" }, + { id: "juno" }, + { id: "stargaze" }, + { id: "persistence" }, + { id: "axelar" }, + { id: "onomy" }, + { id: "quicksilver" }, + { id: "agoric" }, + { id: "band-protocol" }, + { id: "bitsong" }, + { id: "chihuahua" }, + { id: "comdex" }, + { id: "crescent" }, + { id: "cronos" }, + { id: "cudos" }, + { id: "fetch-ai" }, + { id: "gravity-bridge" }, + { id: "injective" }, + { id: "irisnet" }, + { id: "ki-network" }, + { id: "mars-protocol" }, + { id: "regen" }, + { id: "secret" }, + { id: "sentinel" }, + { id: "sommelier" }, + { id: "teritori" }, + { id: "umee" }, + { id: "coreum" }, + { id: "desmos" }, + { id: "dydx" }, + { id: "optimism" }, + { id: "fantom" }, + { id: "arbitrum" }, + { id: "polygon" }, + { id: "binance" }, + { id: "near" }, + { id: "harmony" }, + { id: "solana" }, + { id: "tezos" }, ]); }), http.get("*/v1/tokens", async () => { @@ -87,42 +128,23 @@ describe("Select opportunity", () => { ]); }), - http.get("*/v1/yields/:integrationId", async (info) => { - const integrationId = info.params.integrationId as string; - await delay(); - - return Just(yieldFixture()) - .map((mock) => { - const rewardToken = (() => { - switch (integrationId) { - case "ethereum-eth-reth-staking": - return { ...token, name: "Rocket Pool ETH", symbol: "rETH" }; - case "ethereum-eth-lido-staking": - return { ...token, name: "Lido Staked ETH", symbol: "stETH" }; - default: - return { ...token, name: "Banana ETH", symbol: "bananaETH" }; - } - })(); - - return { - ...mock, - id: integrationId, - args: { enter: { args: { nfts: undefined } } }, - token, - metadata: { - ...mock.metadata, - type: "liquid-staking", - rewardTokens: [rewardToken], - provider: { name: "Stakewise" }, - }, - status: { - ...mock.status, - enter: integrationId !== "ethereum-eth-stakewise-staking", - }, - } as ReturnType; - }) - .map((val) => HttpResponse.json(val)) - .unsafeCoerce(); + ...yieldIds.flatMap((integrationId) => { + const legacyYield = getLegacyYield(integrationId); + + return [ + http.get(legacyApiRoute(`/v1/yields/${integrationId}`), async () => { + await delay(); + return HttpResponse.json(legacyYield); + }), + http.get(yieldApiRoute(`/v1/yields/${integrationId}`), async () => { + await delay(); + return HttpResponse.json( + yieldApiYieldFixtureFromLegacy({ + legacyYield, + }) + ); + }), + ]; }) ); @@ -141,10 +163,6 @@ describe("Select opportunity", () => { selectContainer = app.getByTestId("select-modal__container"); - await expect - .element(selectContainer.getByText("Liquid Staking")) - .toBeInTheDocument(); - await expect .element( selectContainer.getByTestId( diff --git a/packages/widget/tests/use-cases/sk-wallet.test.tsx b/packages/widget/tests/use-cases/sk-wallet.test.tsx index a458beff..4b58df06 100644 --- a/packages/widget/tests/use-cases/sk-wallet.test.tsx +++ b/packages/widget/tests/use-cases/sk-wallet.test.tsx @@ -9,6 +9,7 @@ import { SettingsContextProvider } from "../../src/providers/settings"; import { SKWalletProvider, useSKWallet } from "../../src/providers/sk-wallet"; import { TrackingContextProviderWithProps } from "../../src/providers/tracking"; import { WagmiConfigProvider } from "../../src/providers/wagmi/provider"; +import { yieldApiRoute } from "../mocks/api-routes"; import { worker } from "../mocks/worker"; import { renderHook } from "../utils/test-utils"; @@ -41,9 +42,9 @@ describe("SK Wallet", () => { const sendTransactionSpy = vi.fn(async () => "hash"); worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); - return HttpResponse.json([MiscNetworks.Solana]); + return HttpResponse.json([{ id: MiscNetworks.Solana }]); }) ); @@ -118,9 +119,9 @@ describe("SK Wallet", () => { const sendTransactionSpy = vi.fn(async (_: unknown) => "hash"); worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); - return HttpResponse.json([MiscNetworks.Ton]); + return HttpResponse.json([{ id: MiscNetworks.Ton }]); }) ); diff --git a/packages/widget/tests/use-cases/staking-flow/setup.ts b/packages/widget/tests/use-cases/staking-flow/setup.ts index b4ac4d6b..f785ce9e 100644 --- a/packages/widget/tests/use-cases/staking-flow/setup.ts +++ b/packages/widget/tests/use-cases/staking-flow/setup.ts @@ -13,8 +13,10 @@ import { waitForMs } from "../../../src/utils"; import { yieldApiActionFixture, yieldApiTransactionFixture, + yieldApiYieldFixtureFromLegacy, yieldValidatorsFixture, } from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; @@ -124,7 +126,9 @@ export const setup = async () => { validators: [], isAvailable: true, }; - + const yieldApiYieldOp = yieldApiYieldFixtureFromLegacy({ + legacyYield: yieldOp, + }); const enterAction: ActionDto = { id: "18bdda99-346a-4694-af71-58dfea68d542", integrationId: "avalanche-avax-liquid-staking", @@ -198,9 +202,9 @@ export const setup = async () => { }; worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); - return HttpResponse.json(["avalanche-c"]); + return HttpResponse.json([{ id: "avalanche-c" }]); }), http.get("*/v1/tokens", async () => { @@ -246,10 +250,20 @@ export const setup = async () => { }, }); }), - http.get("*/v1/yields/avalanche-avax-liquid-staking", async () => { - await delay(); - return HttpResponse.json(yieldOp); - }), + http.get( + legacyApiRoute("/v1/yields/avalanche-avax-liquid-staking"), + async () => { + await delay(); + return HttpResponse.json(yieldOp); + } + ), + http.get( + yieldApiRoute("/v1/yields/avalanche-avax-liquid-staking"), + async () => { + await delay(); + return HttpResponse.json(yieldApiYieldOp); + } + ), http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); @@ -320,8 +334,9 @@ export const setup = async () => { id: enterAction.transactions[0].id, status: "CREATED", type: "STAKE", - gasEstimate: - transactionConstruct.gasEstimate?.amount ?? undefined, + gasEstimate: transactionConstruct.gasEstimate + ? JSON.stringify(transactionConstruct.gasEstimate) + : undefined, stepIndex: 0, }), ], @@ -378,10 +393,14 @@ export const setup = async () => { }); const customConnectors = rkMockWallet({ accounts: [account], requestFn }); + const mergedYieldOp = { + ...yieldApiYieldOp, + __fallback__: yieldOp, + }; return { customConnectors, - yieldOp, + yieldOp: mergedYieldOp, enterAction, transactionConstruct, account, diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts index bada3e15..1772d0ec 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts +++ b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts @@ -13,6 +13,7 @@ import { yieldFixture, yieldRewardRateFixture, } from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; @@ -235,9 +236,9 @@ export const setup = async () => { }); worker.use( - http.get("*/v1/yields/enabled/networks", async () => { + http.get(yieldApiRoute("/v1/networks"), async () => { await delay(); - return HttpResponse.json([token.network]); + return HttpResponse.json([{ id: token.network }]); }), http.get("*/v1/tokens", async () => { await delay(); @@ -276,16 +277,13 @@ export const setup = async () => { }, }); }), - http.get(`*/v1/yields/${yieldId}`, async ({ request }) => { + http.get(legacyApiRoute(`/v1/yields/${yieldId}`), async () => { await delay(); - - const url = new URL(request.url); - - return HttpResponse.json( - url.searchParams.has("ledgerWalletAPICompatible") - ? legacyYield - : rawYield - ); + return HttpResponse.json(legacyYield); + }), + http.get(yieldApiRoute(`/v1/yields/${yieldId}`), async () => { + await delay(); + return HttpResponse.json(rawYield); }), http.get("*/v1/yields/:yieldId/validators", async () => { await delay(); diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx index d955411d..4c799a8f 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx +++ b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx @@ -38,7 +38,7 @@ describe("Trust incentive APY", () => { .element( app .getByTestId("reward-rate-breakdown__campaign") - .getByText("Up to 0.20%") + .getByText("Up to 0.2%") ) .toBeInTheDocument(); @@ -53,7 +53,7 @@ describe("Trust incentive APY", () => { .element(selectContainer.getByText("4.55%")) .toBeInTheDocument(); await expect - .element(selectContainer.getByText("Up to 0.20%")) + .element(selectContainer.getByText("Up to 4.55%")) .toBeInTheDocument(); app.unmount(); diff --git a/packages/widget/tests/use-cases/under-maintenance.test.tsx b/packages/widget/tests/use-cases/under-maintenance.test.tsx index 8384ddae..bb08d3ad 100644 --- a/packages/widget/tests/use-cases/under-maintenance.test.tsx +++ b/packages/widget/tests/use-cases/under-maintenance.test.tsx @@ -1,13 +1,17 @@ import { HttpResponse, http } from "msw"; import { describe, expect, it } from "vitest"; +import { yieldApiRoute } from "../mocks/api-routes"; import { worker } from "../mocks/worker"; import { renderApp } from "../utils/test-utils"; describe("Under maintenance", () => { it("Show under maintenance popup", async () => { worker.use( - http.get("*/v2/health", async () => { - return HttpResponse.json({ db: "FAIL" }); + http.get(yieldApiRoute("/health"), async () => { + return HttpResponse.json({ + status: "FAIL", + timestamp: new Date().toISOString(), + }); }) ); diff --git a/packages/widget/vite/vite.config.base.ts b/packages/widget/vite/vite.config.base.ts index fb22538f..77d02c09 100644 --- a/packages/widget/vite/vite.config.base.ts +++ b/packages/widget/vite/vite.config.base.ts @@ -37,6 +37,7 @@ export const getConfig = (overides?: Partial): UserConfigFnObject => test: { browser: { enabled: true, + screenshotFailures: false, provider: playwright(), instances: [{ browser: "chromium" }], viewport: { width: 800, height: 900 }, From 48ccf6e93e3bfd2761f10f6ae52d7ef61d1fc281 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 25 Mar 2026 16:13:38 +0100 Subject: [PATCH 18/23] chore: refactor validators to use api schema --- .../molecules/select-validator/meta-info.tsx | 44 ++++++++----------- .../select-validator-list.tsx | 6 +-- .../widget/src/domain/types/validators.ts | 29 +----------- .../src/hooks/api/use-yield-validators.ts | 11 ++--- .../widget/src/hooks/use-provider-details.ts | 13 +++--- .../utila-select-validator-trigger.tsx | 2 +- .../select-validator-trigger.tsx | 2 +- .../earn-page/state/earn-page-context.tsx | 3 +- packages/widget/tests/fixtures/index.ts | 44 ++++++++++++++++--- 9 files changed, 76 insertions(+), 78 deletions(-) diff --git a/packages/widget/src/components/molecules/select-validator/meta-info.tsx b/packages/widget/src/components/molecules/select-validator/meta-info.tsx index c1910cc8..200054dc 100644 --- a/packages/widget/src/components/molecules/select-validator/meta-info.tsx +++ b/packages/widget/src/components/molecules/select-validator/meta-info.tsx @@ -31,19 +31,15 @@ export const useMetaInfo = ({ marketCap, tokenSymbol, }: { - [Key in keyof Pick< - ValidatorDto, - | "stakedBalance" - | "votingPower" - | "commission" - | "address" - | "website" - | "nominatorCount" - | "subnetName" - | "marketCap" - | "tokenSymbol" - >]: ValidatorDto[Key] | undefined; -} & { + commission?: ValidatorDto["commission"]; + stakedBalance?: ValidatorDto["tvl"]; + votingPower?: ValidatorDto["votingPower"]; + address?: ValidatorDto["address"]; + website?: ValidatorDto["website"]; + nominatorCount?: ValidatorDto["nominatorCount"]; + subnetName?: ValidatorDto["subnetName"]; + marketCap?: ValidatorDto["marketCap"]; + tokenSymbol?: ValidatorDto["tokenSymbol"]; stakedBalanceToken: TokenDto | undefined; rewardRate: number | undefined; rewardType: RewardTypes | undefined; @@ -51,18 +47,16 @@ export const useMetaInfo = ({ const { t } = useTranslation(); return useMemo<{ - [Key in keyof Pick< - ValidatorDto, - | "stakedBalance" - | "votingPower" - | "commission" - | "address" - | "website" - | "nominatorCount" - | "subnetName" - | "marketCap" - | "tokenSymbol" - >]: { title: string; val: ReactNode | string } | null; + stakedBalance: { title: string; val: ReactNode | string } | null; + votingPower: { title: string; val: ReactNode | string } | null; + commission: { title: string; val: ReactNode | string } | null; + address: { title: string; val: ReactNode | string } | null; + website: { title: string; val: ReactNode | string } | null; + nominatorCount: { title: string; val: ReactNode | string } | null; + subnetName: { title: string; val: ReactNode | string } | null; + marketCap: { title: string; val: ReactNode | string } | null; + tokenSymbol: { title: string; val: ReactNode | string } | null; + rewardRate: { title: string; val: ReactNode | string } | null; }>( () => ({ rewardRate: diff --git a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx index 0ae23bad..bbe6358b 100644 --- a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx +++ b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx @@ -146,7 +146,7 @@ export const SelectValidatorList = ({ {getRewardRateFormatted({ - rewardRate: item.apr, + rewardRate: item.rewardRate?.total, rewardType: getYieldRewardType(selectedStake), })} @@ -213,7 +213,7 @@ export const SelectValidatorList = ({ { - return { - address: validatorDto.address, - apr: validatorDto.rewardRate?.total, - commission: validatorDto.commission, - image: validatorDto.logoURI, - minimumStake: validatorDto.minimumStake, - name: validatorDto.name, - nominatorCount: validatorDto.nominatorCount, - preferred: validatorDto.preferred, - pricePerShare: validatorDto.pricePerShare, - providerId: validatorDto.provider?.id, - remainingPossibleStake: validatorDto.remainingPossibleStake, - remainingSlots: validatorDto.remainingSlots, - stakedBalance: validatorDto.tvl, - status: validatorDto.status as ValidatorDto["status"], - subnetId: validatorDto.subnetId, - subnetName: validatorDto.subnetName, - tokenSymbol: validatorDto.tokenSymbol, - votingPower: validatorDto.votingPower, - website: validatorDto.website, - }; -}; +export type ValidatorDto = YieldValidatorDto; diff --git a/packages/widget/src/hooks/api/use-yield-validators.ts b/packages/widget/src/hooks/api/use-yield-validators.ts index 40e7e0e1..a7a9d596 100644 --- a/packages/widget/src/hooks/api/use-yield-validators.ts +++ b/packages/widget/src/hooks/api/use-yield-validators.ts @@ -1,8 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { - toValidatorDto, - type ValidatorDto, -} from "../../domain/types/validators"; +import type { ValidatorDto } from "../../domain/types/validators"; import type { ValidatorsConfig } from "../../domain/types/yields"; import { filterValidators, type Yield } from "../../domain/types/yields"; import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; @@ -61,9 +58,9 @@ const getYieldValidatorsQueryFn = async ({ ) ); - const validators = [firstPage, ...remainingPages] - .flatMap((page) => page.items ?? []) - .map(toValidatorDto); + const validators = [firstPage, ...remainingPages].flatMap( + (page) => page.items ?? [] + ); return network ? filterValidators({ diff --git a/packages/widget/src/hooks/use-provider-details.ts b/packages/widget/src/hooks/use-provider-details.ts index e97deb35..fd4349df 100644 --- a/packages/widget/src/hooks/use-provider-details.ts +++ b/packages/widget/src/hooks/use-provider-details.ts @@ -21,7 +21,7 @@ type Res = Maybe<{ rewardRate: number | undefined; rewardType: RewardTypes; address?: string; - stakedBalance?: ValidatorDto["stakedBalance"]; + stakedBalance?: ValidatorDto["tvl"]; votingPower?: ValidatorDto["votingPower"]; commission?: ValidatorDto["commission"]; website?: ValidatorDto["website"]; @@ -78,7 +78,10 @@ export const getProviderDetails = ({ validatorAddress .chain>((addr) => List.find( - (v) => v.address === addr || v.providerId === addr, + (v) => + v.address === addr || + v.providerId === addr || + v.provider?.id === addr, validatorsData ?? [] ).map((validator) => { const { rewardRate, rewardType } = Maybe.fromRecord({ @@ -96,12 +99,12 @@ export const getProviderDetails = ({ }) ) .orDefault({ - rewardRate: validator.apr, + rewardRate: validator.rewardRate?.total, rewardType: getYieldRewardType(yieldDto), }); return { - logo: validator.image, + logo: validator.logoURI, name: validator.name ?? validator.address, rewardRateFormatted: getRewardRateFormatted({ rewardRate, @@ -110,7 +113,7 @@ export const getProviderDetails = ({ rewardRate, rewardType: getYieldRewardType(yieldDto), address: validator.address, - stakedBalance: validator.stakedBalance, + stakedBalance: validator.tvl, votingPower: validator.votingPower, commission: validator.commission, status: validator.status, diff --git a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-trigger.tsx b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-trigger.tsx index da42049e..863c6539 100644 --- a/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-trigger.tsx +++ b/packages/widget/src/pages-dashboard/overview/earn-page/utila-select-validator-trigger.tsx @@ -40,7 +40,7 @@ export const SelectValidatorTrigger = ({ { .map((validators) => { if (variant === "utila" || variant === "porto") { return [...validators].sort( - (a, b) => (b.apr ?? 0) - (a.apr ?? 0) + (a, b) => + (b.rewardRate?.total ?? 0) - (a.rewardRate?.total ?? 0) ); } diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index 2ad82c0b..936cf867 100644 --- a/packages/widget/tests/fixtures/index.ts +++ b/packages/widget/tests/fixtures/index.ts @@ -13,6 +13,7 @@ import type { YieldBalanceDto, YieldRewardRateDto, YieldTransactionDto, + YieldValidatorDto, } from "../../src/providers/yield-api-client-provider/types"; import type { components } from "../../src/types/yield-api-schema"; @@ -23,6 +24,7 @@ type LegacyTransactionDto = ReturnType< type LegacyYieldDto = ReturnType< typeof getYieldV2ControllerGetYieldByIdResponseMock >; +type LegacyValidatorDto = NonNullable[number]; const apyFaker = () => faker.number.float({ min: 0, max: 0.05 }); type YieldArgumentFieldName = components["schemas"]["ArgumentFieldDto"]["name"]; @@ -76,6 +78,34 @@ export const yieldApiYieldFixture = ( ...overrides, }) as YieldApiYieldDto; +const mapLegacyValidatorToYieldValidator = ( + validator: LegacyValidatorDto +): YieldValidatorDto => ({ + address: validator.address, + commission: validator.commission, + logoURI: validator.image, + minimumStake: validator.minimumStake, + name: validator.name, + nominatorCount: validator.nominatorCount, + preferred: validator.preferred, + pricePerShare: validator.pricePerShare, + providerId: validator.providerId, + remainingPossibleStake: validator.remainingPossibleStake, + remainingSlots: validator.remainingSlots, + rewardRate: { + total: validator.apr ?? apyFaker(), + rateType: "APR", + components: [], + }, + status: validator.status, + subnetId: validator.subnetId, + subnetName: validator.subnetName, + tvl: validator.stakedBalance, + tokenSymbol: validator.tokenSymbol, + votingPower: validator.votingPower, + website: validator.website, +}); + const mapYieldArgumentFields = ( args?: Record< string, @@ -162,7 +192,7 @@ export const yieldApiYieldFixtureFromLegacy = ({ legacyYield.metadata.type === "liquid-staking" ? "staking" : legacyYield.metadata.type, - requiresValidatorSelection: legacyYield.validators.length > 0, + requiresValidatorSelection: (legacyYield.validators?.length ?? 0) > 0, rewardSchedule: legacyYield.metadata.rewardSchedule ?? "day", rewardClaiming: legacyYield.metadata.rewardClaiming ?? "auto", gasFeeToken: legacyYield.metadata.gasFeeToken ?? legacyYield.token, @@ -198,7 +228,8 @@ export const yieldApiYieldFixtureFromLegacy = ({ }, }, providerId: legacyYield.metadata.provider?.id ?? "unknown", - validators: legacyYield.validators, + validators: + legacyYield.validators?.map(mapLegacyValidatorToYieldValidator) ?? [], ...overrides, }) as YieldApiYieldDto; @@ -246,11 +277,10 @@ export const yieldFixture = (overrides?: Partial) => export const yieldValidatorsFixture = ( validators?: LegacyYieldDto["validators"] -) => - (validators ?? yieldFixture().validators).map((validator) => ({ - ...validator, - apr: validator.apr ?? apyFaker(), - })); +): YieldValidatorDto[] => + (validators ?? yieldFixture().validators).map((validator) => + mapLegacyValidatorToYieldValidator(validator) + ); export const enterResponseFixture = (overrides?: Partial) => ({ ...getActionControllerEnterResponseMock(), From fb914157fb033f05e57e489ac10a06eeef605159 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 25 Mar 2026 16:22:28 +0100 Subject: [PATCH 19/23] fix: hide personalized APY without campaign component --- .../hooks/use-position-details.ts | 7 ++ .../use-cases/trust-incentive-apy/setup.ts | 91 +++++++++++++------ .../trust-incentive-apy.test.tsx | 31 +++++++ 3 files changed, 101 insertions(+), 28 deletions(-) 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 721ea156..7f968453 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,6 +4,7 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useNavigate } from "react-router"; import { equalTokens } from "../../../domain"; +import { getRewardRateBreakdown } from "../../../domain/types/reward-rate"; import { isForceMaxAmount } from "../../../domain/types/stake"; import type { TokenDto } from "../../../domain/types/tokens"; import { getYieldActionArg } from "../../../domain/types/yields"; @@ -121,6 +122,12 @@ export const usePositionDetails = () => { () => positionBalances.data .map((balanceData) => balanceData.rewardRate) + .filter( + (rewardRate) => + !!getRewardRateBreakdown(rewardRate).find( + (item) => item.key === "campaign" + ) + ) .extractNullable(), [positionBalances.data] ); diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts index 1772d0ec..8f671260 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts +++ b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts @@ -47,7 +47,13 @@ const setUrl = ({ window.history.pushState({}, "", url); }; -export const setup = async () => { +export const setup = async ({ + personalizedRewardRate: providedPersonalizedRewardRate, + useRewardRateWithoutCampaign, +}: { + personalizedRewardRate?: YieldRewardRateDto; + useRewardRateWithoutCampaign?: boolean; +} = {}) => { const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; const token: LegacyTokenDto = { @@ -105,33 +111,62 @@ export const setup = async () => { ], }); - const personalizedRewardRate: YieldRewardRateDto = yieldRewardRateFixture({ - total: 0.04530754665300604, - rateType: "APY", - components: [ - { - rate: 0.0028386677110199426, - rateType: "APR", - token: morphoToken, - yieldSource: "protocol_incentive", - description: "MORPHO rewards", - }, - { - rate: 0.0018, - rateType: "APR", - token: rewardToken, - yieldSource: "campaign_incentive", - description: "U rewards", - }, - { - rate: 0.042668878941986094, - rateType: "APY", - token: rewardToken, - yieldSource: "vault", - description: "Supply APY", - }, - ], - }); + const defaultPersonalizedRewardRate: YieldRewardRateDto = + yieldRewardRateFixture({ + total: 0.04530754665300604, + rateType: "APY", + components: [ + { + rate: 0.0028386677110199426, + rateType: "APR", + token: morphoToken, + yieldSource: "protocol_incentive", + description: "MORPHO rewards", + }, + { + rate: 0.0018, + rateType: "APR", + token: rewardToken, + yieldSource: "campaign_incentive", + description: "U rewards", + }, + { + rate: 0.042668878941986094, + rateType: "APY", + token: rewardToken, + yieldSource: "vault", + description: "Supply APY", + }, + ], + }); + + const personalizedRewardRateWithoutCampaign: YieldRewardRateDto = + yieldRewardRateFixture({ + total: 0.04530754665300604, + rateType: "APY", + components: [ + { + rate: 0.0028386677110199426, + rateType: "APR", + token: morphoToken, + yieldSource: "protocol_incentive", + description: "MORPHO rewards", + }, + { + rate: 0.042668878941986094, + rateType: "APY", + token: rewardToken, + yieldSource: "vault", + description: "Supply APY", + }, + ], + }); + + const personalizedRewardRate = + providedPersonalizedRewardRate ?? + (useRewardRateWithoutCampaign + ? personalizedRewardRateWithoutCampaign + : defaultPersonalizedRewardRate); const yieldId = "avalanche-c-usda-trust-0xbeefa1abfebe621df50ceaef9f54fdb73648c92c-vault"; diff --git a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx index 4c799a8f..5a3fa564 100644 --- a/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx +++ b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx @@ -102,4 +102,35 @@ describe("Trust incentive APY", () => { app.unmount(); }); + + it("hides personalized APY when the balance has no campaign component", async () => { + const { account, customConnectors, legacyYield, setUrl } = await setup({ + useRewardRateWithoutCampaign: true, + }); + + setUrl({ + accountId: account, + balanceId: "default", + yieldId: legacyYield.id, + }); + + const app = await renderApp({ + wagmi: { + __customConnectors__: customConnectors, + }, + }); + + await expect.poll(() => app.getByText("Personalized APY").length).toBe(0); + await expect + .poll(() => app.getByTestId("personalized-reward-rate").length) + .toBe(0); + await expect + .poll( + () => + app.getByTestId("personalized-reward-rate-breakdown__campaign").length + ) + .toBe(0); + + app.unmount(); + }); }); From c5fa67ef2427590694f8d159ee3b939c2279997d Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Wed, 25 Mar 2026 16:50:15 +0100 Subject: [PATCH 20/23] fix: ts error --- .../src/hooks/api/use-activity-actions.ts | 12 +++++++++++- .../activity-page/hooks/use-activity-page.tsx | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/widget/src/hooks/api/use-activity-actions.ts b/packages/widget/src/hooks/api/use-activity-actions.ts index 95eeb640..0153d79c 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -2,6 +2,7 @@ import { useInfiniteQuery } from "@tanstack/react-query"; import { EitherAsync } from "purify-ts"; import { useMemo } from "react"; import { type ActionDto, getActionInputToken } from "../../domain/types/action"; +import type { Yield } from "../../domain/types/yields"; import { useSKQueryClient } from "../../providers/query-client"; import { useSKWallet } from "../../providers/sk-wallet"; import { useYieldApiFetchClient } from "../../providers/yield-api-client-provider"; @@ -10,7 +11,16 @@ import { getYieldOpportunity } from "./use-yield-opportunity/get-yield-opportuni const PAGE_SIZE = 50; -export const useActivityActions = () => { +type ActivityActionItem = { + actionData: ActionDto; + yieldData: Yield; +}; + +type UseActivityActionsResult = ReturnType & { + allItems: ActivityActionItem[] | undefined; +}; + +export const useActivityActions = (): UseActivityActionsResult => { const { address, isLedgerLive, network } = useSKWallet(); const queryClient = useSKQueryClient(); const yieldApiFetchClient = useYieldApiFetchClient(); diff --git a/packages/widget/src/pages/details/activity-page/hooks/use-activity-page.tsx b/packages/widget/src/pages/details/activity-page/hooks/use-activity-page.tsx index 3b97bca0..39a4b676 100644 --- a/packages/widget/src/pages/details/activity-page/hooks/use-activity-page.tsx +++ b/packages/widget/src/pages/details/activity-page/hooks/use-activity-page.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { type ReactNode, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { Box } from "../../../../components/atoms/box"; import { ContentLoaderSquare } from "../../../../components/atoms/content-loader"; @@ -7,8 +7,22 @@ import { useTrackPage } from "../../../../hooks/tracking/use-track-page"; import { useSKWallet } from "../../../../providers/sk-wallet"; import { FallbackContent } from "../../positions-page/components/fallback-content"; import { useActivityPageContext } from "../state/activity-page.context"; +import type { ItemBulletType } from "../state/types"; +import type { ActionYieldDto } from "../types"; -export const useActivityPage = () => { +type UseActivityPageResult = { + content: ReactNode; + onActionSelect: (val: ActionYieldDto) => void; + labels: string[]; + counts: number[]; + bulletLines: ItemBulletType[]; + allData: ReturnType< + typeof useActivityPageContext + >["activityActions"]["allItems"]; + activityActions: ReturnType["activityActions"]; +}; + +export const useActivityPage = (): UseActivityPageResult => { useTrackPage("activity"); const { isConnected, isConnecting } = useSKWallet(); From 201c25c4da7ba61e41bac72cae140c740b50d5c0 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 26 Mar 2026 11:48:16 +0100 Subject: [PATCH 21/23] refactor(widget): simplify image fallback handling --- .../components/atoms/image-fallback/index.tsx | 43 ------- .../atoms/image-fallback/styles.css.ts | 5 - .../src/components/atoms/image/index.tsx | 105 +++++++----------- .../token-icon/network-icon-image/index.tsx | 5 +- .../token-icon/token-icon-image/index.tsx | 13 +-- .../components/atoms/virtual-list/index.tsx | 12 +- .../molecules/reward-token-details/index.tsx | 9 +- .../select-validator-list.tsx | 13 +-- .../utila-select-validator-trigger.tsx | 18 +-- .../components/provider-details.tsx | 15 +-- .../src/pages/complete/pages/common.page.tsx | 12 +- .../components/select-provider/index.tsx | 18 +-- .../select-validator-trigger.tsx | 18 +-- .../select-yield-reward-details.tsx | 12 +- .../select-yield-section/staked-via.tsx | 5 +- .../components/provider-details.tsx | 15 +-- .../tests/components/atoms/image.test.tsx | 84 ++++++++++++++ .../atoms/token-icon-image.test.tsx | 45 ++++++++ 18 files changed, 208 insertions(+), 239 deletions(-) delete mode 100644 packages/widget/src/components/atoms/image-fallback/index.tsx delete mode 100644 packages/widget/src/components/atoms/image-fallback/styles.css.ts create mode 100644 packages/widget/tests/components/atoms/image.test.tsx create mode 100644 packages/widget/tests/components/atoms/token-icon-image.test.tsx diff --git a/packages/widget/src/components/atoms/image-fallback/index.tsx b/packages/widget/src/components/atoms/image-fallback/index.tsx deleted file mode 100644 index 45dd11a0..00000000 --- a/packages/widget/src/components/atoms/image-fallback/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { assignInlineVars } from "@vanilla-extract/dynamic"; -import type { Atoms } from "../../../styles/theme/atoms.css"; -import { getBackgroundColor } from "../../../utils"; -import { Box } from "../box"; -import type { TextVariants } from "../typography/styles.css"; -import { Text } from "../typography/text"; -import { defaultColor, fallbackContainer } from "./styles.css"; - -export const ImageFallback = ({ - name, - tokenLogoHw, - textVariant, -}: { - name: string; - tokenLogoHw?: Atoms["hw"]; - textVariant?: TextVariants; -}) => { - return ( - - - {name.charAt(0).toUpperCase()} - - - ); -}; diff --git a/packages/widget/src/components/atoms/image-fallback/styles.css.ts b/packages/widget/src/components/atoms/image-fallback/styles.css.ts deleted file mode 100644 index dc6465f1..00000000 --- a/packages/widget/src/components/atoms/image-fallback/styles.css.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createVar, style } from "@vanilla-extract/css"; - -export const defaultColor = createVar(); - -export const fallbackContainer = style({ background: defaultColor }); diff --git a/packages/widget/src/components/atoms/image/index.tsx b/packages/widget/src/components/atoms/image/index.tsx index b6c2690c..e4c8dfdd 100644 --- a/packages/widget/src/components/atoms/image/index.tsx +++ b/packages/widget/src/components/atoms/image/index.tsx @@ -1,85 +1,66 @@ -import type { HTMLProps, ReactNode } from "react"; -import { isValidElement, useEffect, useMemo, useState } from "react"; +import type { HTMLProps } from "react"; +import { useMemo } from "react"; +import { getBackgroundColor } from "../../../utils"; import type { BoxProps } from "../box"; import { Box } from "../box"; -const failLoadImages = new Set(); - type ImageProps = { - src: string | undefined; - fallback: string | ReactNode; - containerProps?: Omit; - imageProps?: Omit; + src?: string; + fallbackName?: string; + wrapperProps?: Omit; + imgProps?: Omit; }; export const Image = ({ - fallback, src, - containerProps, - imageProps, + fallbackName, + wrapperProps, + imgProps, }: ImageProps) => { - const [loadState, setLoadState] = useState(() => ({ - src, - loaded: false, - timeoutFallback: false, - })); - - /** - * Reset on src change - */ - if (loadState.src !== src) { - setLoadState({ src, loaded: false, timeoutFallback: false }); - } - - useEffect(() => { - if (src && failLoadImages.has(src)) return; - - const id = setTimeout(() => { - setLoadState((prev) => ({ ...prev, timeoutFallback: true })); - }, 500); - - return () => clearTimeout(id); - }, [src]); - - const onLoad: HTMLProps["onLoad"] = (e) => { - setLoadState((prev) => ({ ...prev, loaded: true })); - imageProps?.onLoad?.(e); - }; + const generatedFallbackSrc = useMemo( + () => createMonogramImageSrc(fallbackName), + [fallbackName] + ); const onError: HTMLProps["onError"] = (e) => { - if (src) { - failLoadImages.add(src); + if (generatedFallbackSrc) { + e.currentTarget.src = generatedFallbackSrc; } - imageProps?.onError?.(e); + imgProps?.onError?.(e); }; - const showFallback = useMemo(() => { - if (!isValidElement(fallback)) return false; - - return ( - !src || - failLoadImages.has(src) || - (loadState.timeoutFallback && !loadState.loaded) - ); - }, [fallback, loadState.loaded, loadState.timeoutFallback, src]); - return ( - {showFallback && {fallback}} - {!!(src && !failLoadImages.has(src)) && ( - - )} + ); }; + +const createMonogramImageSrc = (name?: string) => { + if (!name) return undefined; + + const initial = escapeForSvg(name.charAt(0).toUpperCase()); + const backgroundColor = getBackgroundColor(name); + + const svg = `${initial}`; + + return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`; +}; + +const escapeForSvg = (value: string) => + value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); diff --git a/packages/widget/src/components/atoms/token-icon/network-icon-image/index.tsx b/packages/widget/src/components/atoms/token-icon/network-icon-image/index.tsx index e0a0cfad..c604dcc0 100644 --- a/packages/widget/src/components/atoms/token-icon/network-icon-image/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/network-icon-image/index.tsx @@ -15,9 +15,8 @@ export const NetworkLogoImage = ({ ); diff --git a/packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx b/packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx index afd87a91..68b5101c 100644 --- a/packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/token-icon-image/index.tsx @@ -1,6 +1,5 @@ import type { Atoms } from "../../../../styles/theme/atoms.css"; import { Image } from "../../image"; -import { ImageFallback } from "../../image-fallback"; type TokenIconProps = { mainUrl?: string; @@ -17,15 +16,9 @@ export const TokenIconImage = ({ }: TokenIconProps) => ( } - /> - } + fallbackName={name} /> ); diff --git a/packages/widget/src/components/atoms/virtual-list/index.tsx b/packages/widget/src/components/atoms/virtual-list/index.tsx index eb88c416..2fe3f100 100644 --- a/packages/widget/src/components/atoms/virtual-list/index.tsx +++ b/packages/widget/src/components/atoms/virtual-list/index.tsx @@ -104,11 +104,7 @@ export const VirtualList = ({ }} > {virtualItems.map((virtualItem) => ( - + {itemContent(virtualItem.index, data[virtualItem.index])} ))} @@ -233,11 +229,7 @@ export const GroupedVirtualList = ({ const type = item.type; return ( - + {type === "child" ? itemContent(item.index, item.parentIndex) : groupContent(item.index)} diff --git a/packages/widget/src/components/molecules/reward-token-details/index.tsx b/packages/widget/src/components/molecules/reward-token-details/index.tsx index d982c275..97feef83 100644 --- a/packages/widget/src/components/molecules/reward-token-details/index.tsx +++ b/packages/widget/src/components/molecules/reward-token-details/index.tsx @@ -6,7 +6,6 @@ import type { YieldPendingActionType } from "../../../providers/yield-api-client import { Box } from "../../atoms/box"; import { MorphoStarsIcon } from "../../atoms/icons/morpho-stars"; import { Image } from "../../atoms/image"; -import { ImageFallback } from "../../atoms/image-fallback"; import { Text } from "../../atoms/typography/text"; import { inlineText } from "./style.css"; @@ -51,12 +50,10 @@ export const RewardTokenDetails = ({ alignSelf="flex-start" > - } + fallbackName={rt.providerName} /> diff --git a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx index bbe6358b..457ce033 100644 --- a/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx +++ b/packages/widget/src/components/molecules/select-validator/select-validator-list.tsx @@ -13,7 +13,6 @@ import { Button } from "../../atoms/button"; import { CheckSteps } from "../../atoms/icons/check-steps"; import { PreferredIcon } from "../../atoms/icons/preferred"; import { Image } from "../../atoms/image"; -import { ImageFallback } from "../../atoms/image-fallback"; import { SelectModalItem, SelectModalItemContainer, @@ -144,16 +143,10 @@ export const SelectValidatorList = ({ )} - } + fallbackName={item.name || item.address} /> - - - } + fallbackName={nameOrAddress} /> diff --git a/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx b/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx index 6f9d8066..2ccd9714 100644 --- a/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages-dashboard/position-details/components/provider-details.tsx @@ -10,7 +10,6 @@ import { import { Divider } from "../../../components/atoms/divider"; import { PreferredIcon } from "../../../components/atoms/icons/preferred"; import { Image } from "../../../components/atoms/image"; -import { ImageFallback } from "../../../components/atoms/image-fallback"; import { Text } from "../../../components/atoms/typography/text"; import { useMetaInfo } from "../../../components/molecules/select-validator/meta-info"; import type { RewardTypes } from "../../../domain/types/reward-rate"; @@ -49,18 +48,10 @@ export const ProviderDetails = ({ - - - } + fallbackName={nameOrAddress} /> diff --git a/packages/widget/src/pages/complete/pages/common.page.tsx b/packages/widget/src/pages/complete/pages/common.page.tsx index 8500846c..cd1e4f9a 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -5,7 +5,6 @@ import { useTranslation } from "react-i18next"; import { Box } from "../../../components/atoms/box"; import { CheckCircleIcon } from "../../../components/atoms/icons/check-circle"; import { Image } from "../../../components/atoms/image"; -import { ImageFallback } from "../../../components/atoms/image-fallback"; import { TokenIcon } from "../../../components/atoms/token-icon"; import { Heading } from "../../../components/atoms/typography/heading"; import { Text } from "../../../components/atoms/typography/text"; @@ -157,15 +156,10 @@ export const CompletePageComponent = ({ > {v.logo && ( - } + fallbackName={v.name || v.logo} /> )} diff --git a/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx b/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx index e0b4a595..3531d97a 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-provider/index.tsx @@ -5,7 +5,6 @@ import { Box } from "../../../../../components/atoms/box"; import { ContentLoaderSquare } from "../../../../../components/atoms/content-loader"; import { CaretDownIcon } from "../../../../../components/atoms/icons/caret-down"; import { Image } from "../../../../../components/atoms/image"; -import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { SelectYield } from "../../../../../components/molecules/select-yield"; import { @@ -84,21 +83,10 @@ export const SelectProvider = () => { > - - - } + fallbackName={val.provider.name} /> diff --git a/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx b/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx index 3e29e731..a0d5bc2b 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-validator-section/select-validator-trigger.tsx @@ -7,7 +7,6 @@ import { PlusIcon } from "../../../../../components/atoms/icons/plus"; import { PreferredIcon } from "../../../../../components/atoms/icons/preferred"; import { XIcon } from "../../../../../components/atoms/icons/x-icon"; import { Image } from "../../../../../components/atoms/image"; -import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { inactiveContainer } from "../../../../../components/molecules/select-validator/styles.css"; import type { ValidatorDto } from "../../../../../domain/types/validators"; @@ -60,21 +59,10 @@ export const SelectValidatorTrigger = ({ > - - - } + fallbackName={nameOrAddress} /> diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx index 21e7b24e..2c7cdf9d 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/select-yield-reward-details.tsx @@ -3,7 +3,6 @@ import { Trans, useTranslation } from "react-i18next"; import { Box } from "../../../../../components/atoms/box"; import { MorphoStarsIcon } from "../../../../../components/atoms/icons/morpho-stars"; import { Image } from "../../../../../components/atoms/image"; -import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { RewardRateBreakdown } from "../../../../../components/molecules/reward-rate-breakdown"; import { @@ -83,15 +82,10 @@ export const SelectYieldRewardDetails = () => { gap="1" > - } + fallbackName={rt.providerName} /> {isMorphoProvider(rt.providerName) && ( diff --git a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx index 543ee6bd..3b83a3c4 100644 --- a/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx +++ b/packages/widget/src/pages/details/earn-page/components/select-yield-section/staked-via.tsx @@ -1,7 +1,6 @@ import { useTranslation } from "react-i18next"; import { Box } from "../../../../../components/atoms/box"; import { Image } from "../../../../../components/atoms/image"; -import { ImageFallback } from "../../../../../components/atoms/image-fallback"; import { Text } from "../../../../../components/atoms/typography/text"; import { getBaseYieldType, @@ -43,9 +42,9 @@ export const StakedVia = () => { } + fallbackName={val.name} /> )) diff --git a/packages/widget/src/pages/position-details/components/provider-details.tsx b/packages/widget/src/pages/position-details/components/provider-details.tsx index d0d42489..cfe1d7fb 100644 --- a/packages/widget/src/pages/position-details/components/provider-details.tsx +++ b/packages/widget/src/pages/position-details/components/provider-details.tsx @@ -10,7 +10,6 @@ import { import { Divider } from "../../../components/atoms/divider"; import { PreferredIcon } from "../../../components/atoms/icons/preferred"; import { Image } from "../../../components/atoms/image"; -import { ImageFallback } from "../../../components/atoms/image-fallback"; import { Text } from "../../../components/atoms/typography/text"; import { useMetaInfo } from "../../../components/molecules/select-validator/meta-info"; import type { RewardTypes } from "../../../domain/types/reward-rate"; @@ -56,18 +55,10 @@ export const ProviderDetails = ({ - - - } + fallbackName={nameOrAddress} /> diff --git a/packages/widget/tests/components/atoms/image.test.tsx b/packages/widget/tests/components/atoms/image.test.tsx new file mode 100644 index 00000000..33bcf521 --- /dev/null +++ b/packages/widget/tests/components/atoms/image.test.tsx @@ -0,0 +1,84 @@ +import { describe, expect, it } from "vitest"; +import { Image } from "../../../src/components/atoms/image"; +import { render } from "../../utils/test-utils"; + +const validSrcTwo = + "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='2'%20height='2'%3E%3C/svg%3E"; +const brokenSrcOne = "data:image/png;base64,Zm9vYmFy"; +const brokenSrcFour = "data:image/png;base64,YmFkMw=="; + +describe("Image", () => { + it("swaps to the generated monogram when src fails and resets on src change", async () => { + const app = await render( + + ); + + const image = + app.container.querySelector("img[alt='icon']"); + + expect(image).not.toBeNull(); + + image?.dispatchEvent(new Event("error")); + + expect(image?.getAttribute("src")).toContain("data:image/svg+xml"); + + await app.rerender( + + ); + + const rerenderedImage = + app.container.querySelector("img[alt='icon']"); + + expect(rerenderedImage).not.toBeNull(); + expect(rerenderedImage?.getAttribute("src")).toBe(validSrcTwo); + + app.unmount(); + }); + + it("uses the generated monogram when src is missing", async () => { + const app = await render( + + ); + + const image = app.container.querySelector( + "img[alt='missing-src']" + ); + + expect(image).not.toBeNull(); + expect(image?.getAttribute("src")).toContain("data:image/svg+xml"); + }); + + it("falls back to the generated monogram after src fails", async () => { + const app = await render( + + ); + + const image = app.container.querySelector( + "img[alt='monogram']" + ); + + expect(image).not.toBeNull(); + image?.dispatchEvent(new Event("error")); + + expect(image?.getAttribute("src")).toContain("data:image/svg+xml"); + }); + + it("keeps the broken src when there is no monogram fallback", async () => { + const app = await render( + + ); + + const image = app.container.querySelector("img[alt='no-fallback']"); + + expect(image).not.toBeNull(); + expect(image?.getAttribute("src")).toBe(brokenSrcFour); + }); +}); diff --git a/packages/widget/tests/components/atoms/token-icon-image.test.tsx b/packages/widget/tests/components/atoms/token-icon-image.test.tsx new file mode 100644 index 00000000..3b8e672b --- /dev/null +++ b/packages/widget/tests/components/atoms/token-icon-image.test.tsx @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; +import { TokenIconImage } from "../../../src/components/atoms/token-icon/token-icon-image"; +import { render } from "../../utils/test-utils"; + +const validSrc = + "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1'%20height='1'%3E%3C/svg%3E"; +const brokenSrcOne = "data:image/png;base64,dG9rZW4x"; + +describe("TokenIconImage", () => { + it("uses a single image element and prefers mainUrl when present", async () => { + const app = await render( + + ); + + expect(app.container.querySelectorAll("img")).toHaveLength(1); + + const image = app.container.querySelector("img"); + + expect(image).not.toBeNull(); + expect(image?.getAttribute("src")).toBe(validSrc); + }); + + it("falls through to the generated monogram after mainUrl fails", async () => { + const app = await render( + + ); + + const image = app.container.querySelector("img"); + + expect(image).not.toBeNull(); + image?.dispatchEvent(new Event("error")); + + expect(image?.getAttribute("src")).toContain("data:image/svg+xml"); + }); +}); From faa900af4fe408f8f7020734baafc093c414c3f8 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 26 Mar 2026 12:09:02 +0100 Subject: [PATCH 22/23] refactor(widget): simplify base token handling across yield flows --- packages/widget/src/domain/index.ts | 2 -- .../widget/src/hooks/api/use-tokens-prices.ts | 13 +++--------- packages/widget/src/hooks/use-base-token.ts | 7 ------- packages/widget/src/hooks/use-summary.tsx | 10 ++++----- .../components/position-details-info.tsx | 4 ++-- .../earn-page/state/earn-page-context.tsx | 9 +++----- .../hooks/use-position-list-item.ts | 3 +-- .../hooks/use-pending-actions.ts | 3 +-- .../hooks/use-position-details.ts | 21 +++++++------------ .../position-details.page.tsx | 4 ++-- .../pages/position-details/state/index.tsx | 3 +-- 11 files changed, 25 insertions(+), 54 deletions(-) delete mode 100644 packages/widget/src/hooks/use-base-token.ts diff --git a/packages/widget/src/domain/index.ts b/packages/widget/src/domain/index.ts index da473297..e8dbe376 100644 --- a/packages/widget/src/domain/index.ts +++ b/packages/widget/src/domain/index.ts @@ -54,8 +54,6 @@ export const getMaxAmount = ({ ); }; -export const getBaseToken = (yieldDto: Yield) => yieldDto.token; - /** * * @summary Get stake transactions available for signing or tx status check. diff --git a/packages/widget/src/hooks/api/use-tokens-prices.ts b/packages/widget/src/hooks/api/use-tokens-prices.ts index 417f89e5..5e9672f1 100644 --- a/packages/widget/src/hooks/api/use-tokens-prices.ts +++ b/packages/widget/src/hooks/api/use-tokens-prices.ts @@ -3,7 +3,6 @@ import { useMemo } from "react"; import { config } from "../../config"; import type { TokenDto, YieldTokenDto } from "../../domain/types/tokens"; import type { Yield } from "../../domain/types/yields"; -import { useBaseToken } from "../use-base-token"; import { usePrices } from "./use-prices"; /** @@ -16,21 +15,15 @@ export const useTokensPrices = ({ token: Maybe; yieldDto: Maybe; }) => { - const baseToken = useBaseToken(yieldDto); - const priceRequestDto = useMemo( () => - Maybe.fromRecord({ baseToken, yieldDto, token }) + Maybe.fromRecord({ yieldDto, token }) .map((val) => ({ currency: config.currency, - tokenList: [ - val.token, - val.baseToken, - val.yieldDto.mechanics.gasFeeToken, - ], + tokenList: [val.token, val.token, val.yieldDto.mechanics.gasFeeToken], })) .extractNullable(), - [baseToken, yieldDto, token] + [yieldDto, token] ); return usePrices(priceRequestDto); diff --git a/packages/widget/src/hooks/use-base-token.ts b/packages/widget/src/hooks/use-base-token.ts deleted file mode 100644 index 91a606a1..00000000 --- a/packages/widget/src/hooks/use-base-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Maybe } from "purify-ts"; -import { useMemo } from "react"; -import { getBaseToken } from "../domain"; -import type { Yield } from "../domain/types/yields"; - -export const useBaseToken = (yieldDto: Maybe) => - useMemo(() => yieldDto.map((val) => getBaseToken(val)), [yieldDto]); diff --git a/packages/widget/src/hooks/use-summary.tsx b/packages/widget/src/hooks/use-summary.tsx index db017870..d7a609b0 100644 --- a/packages/widget/src/hooks/use-summary.tsx +++ b/packages/widget/src/hooks/use-summary.tsx @@ -3,7 +3,7 @@ import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import { createContext, useCallback, useContext, useMemo } from "react"; import { config } from "../config"; -import { getBaseToken, getTokenPriceInUSD } from "../domain"; +import { getTokenPriceInUSD } from "../domain"; import type { StakeKitErrorDto } from "../domain/types/errors"; import { getPositionTotalAmount } from "../domain/types/positions"; import type { Prices } from "../domain/types/price"; @@ -107,7 +107,7 @@ export const SummaryProvider = ({ const positionTotalAmount = getPositionTotalAmount( p.balancesWithAmount, - getBaseToken(yieldDto) + yieldDto.token ); const yields = [...multiYieldsMapQuery.data.values()]; @@ -172,11 +172,9 @@ export const SummaryProvider = ({ if (!yieldDto) return []; - const baseToken = getBaseToken(yieldDto); - const common = { pricePerShare: "1", - baseToken, + baseToken: yieldDto.token, token: rewardSummary.token, prices, }; @@ -241,7 +239,7 @@ export const SummaryProvider = ({ const positionTotalAmount = getPositionTotalAmount( p.balancesWithAmount, - getBaseToken(yieldDto) + yieldDto.token ); const usdAmount = positionTotalAmount.amountUsd; 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 c4378250..1d1b4785 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 @@ -21,7 +21,7 @@ export const PositionDetailsInfo = () => { integrationData, positionBalancesByType, providersDetails, - liquidTokensToNativeConversion, + shareToAmountConversions, } = usePositionDetails(); const { t } = useTranslation(); @@ -101,7 +101,7 @@ export const PositionDetailsInfo = () => { )} - {liquidTokensToNativeConversion + {shareToAmountConversions .filter((val) => val.size > 0) .map((val) => ( { const { t } = useTranslation(); const initParams = useInitParams(); - const baseToken = useBaseToken(selectedStake); - const { externalProviders, variant } = useSettings(); const { isConnected, isConnecting, isLedgerLiveAccountPlaceholder, chain } = @@ -149,11 +146,11 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { Maybe.fromRecord({ prices: Maybe.fromNullable(pricesState.data), selectedToken, - baseToken, + selectedStake, }) .map((val) => getTokenPriceInUSD({ - baseToken: val.baseToken, + baseToken: val.selectedStake.token, amount: stakeAmount, token: val.selectedToken, prices: val.prices, @@ -161,7 +158,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { }) ) .mapOrDefault((v) => `$${defaultFormattedNumber(v)}`, ""), - [baseToken, pricesState.data, selectedToken, stakeAmount] + [pricesState.data, selectedToken, stakeAmount, selectedStake] ); const selectedTokenAvailableAmount = useMemo( 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 46fcccf0..2d1fc2da 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 @@ -1,7 +1,6 @@ import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import { useMemo } from "react"; -import { getBaseToken } from "../../../../domain"; import { getPositionTotalAmount } from "../../../../domain/types/positions"; import { getYieldRewardType } from "../../../../domain/types/yields"; import { useYieldOpportunity } from "../../../../hooks/api/use-yield-opportunity"; @@ -60,7 +59,7 @@ export const usePositionListItem = ( const tokenToDisplay = item.token; const baseToken = useMemo( - () => integrationData.map((y) => getBaseToken(y)), + () => integrationData.map((y) => y.token), [integrationData] ); 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 f5cfbcce..40169242 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 @@ -12,7 +12,6 @@ import type { ValidatorDto } from "../../../domain/types/validators"; import type { Yield } from "../../../domain/types/yields"; 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"; @@ -40,7 +39,7 @@ export const usePendingActions = () => { positionBalancePrices, } = useUnstakeOrPendingActionState(); - const baseToken = useBaseToken(integrationData); + const baseToken = integrationData.map((val) => val.token); const pendingActionDispatch = useUnstakeOrPendingActionDispatch(); 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 7f968453..efd4194b 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 @@ -3,14 +3,12 @@ import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { useNavigate } from "react-router"; -import { equalTokens } from "../../../domain"; import { getRewardRateBreakdown } from "../../../domain/types/reward-rate"; import { isForceMaxAmount } from "../../../domain/types/stake"; import type { TokenDto } from "../../../domain/types/tokens"; import { getYieldActionArg } from "../../../domain/types/yields"; import { useYieldValidators } from "../../../hooks/api/use-yield-validators"; import { useTrackEvent } from "../../../hooks/tracking/use-track-event"; -import { useBaseToken } from "../../../hooks/use-base-token"; import { useProvidersDetails } from "../../../hooks/use-provider-details"; import { useExitStakeStore } from "../../../providers/exit-stake-store"; import { defaultFormattedNumber } from "../../../utils"; @@ -98,7 +96,7 @@ export const usePositionDetails = () => { const trackEvent = useTrackEvent(); - const baseToken = useBaseToken(integrationData); + const baseToken = integrationData.map((val) => val.token); const yieldValidators = useYieldValidators({ enabled: integrationData.isJust(), @@ -166,7 +164,7 @@ export const usePositionDetails = () => { validatorAddressesHandling, } = usePendingActions(); - const liquidTokensToNativeConversion = useMemo( + const shareToAmountConversions = useMemo( () => Maybe.fromRecord({ integrationData, @@ -175,18 +173,15 @@ export const usePositionDetails = () => { }).map((v) => [...v.positionBalancesByType.values()].reduce((acc, curr) => { curr - .filter( - (yb) => - !yb.token.isPoints && - !!yb.validator?.pricePerShare && - !equalTokens(yb.token, v.baseToken) - ) + .filter((yb) => yb.shareAmount && yb.amount && !yb.token.isPoints) .forEach((yb) => { acc.set( yb.token.symbol, `1 ${yb.token.symbol} = ${defaultFormattedNumber( - new BigNumber(yb.validator?.pricePerShare ?? 0) - )} ${v.baseToken.symbol}` + new BigNumber(yb.shareAmount ?? 0).dividedBy( + new BigNumber(yb.amount ?? 0) + ) + )} ${yb.shareToken?.symbol}` ); }); @@ -222,7 +217,7 @@ export const usePositionDetails = () => { providersDetails, personalizedRewardRate, pendingActions, - liquidTokensToNativeConversion, + shareToAmountConversions, validatorAddressesHandling, onValidatorsSubmit, onPendingActionAmountChange, 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 8dfd58c1..04b39cb5 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -44,7 +44,7 @@ const PositionDetails = () => { onPendingActionClick, pendingActions, providersDetails, - liquidTokensToNativeConversion, + shareToAmountConversions, validatorAddressesHandling, onValidatorsSubmit, unstakeToken, @@ -190,7 +190,7 @@ const PositionDetails = () => { )) )} - {liquidTokensToNativeConversion + {shareToAmountConversions .map((val) => ( val.token); const positionBalancesRemote = usePositionBalances({ balanceId, From f5fc039c31dba188acc0f1b70794bacbabf8a84c Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 26 Mar 2026 12:37:20 +0100 Subject: [PATCH 23/23] fix: solana autoconnect --- packages/widget/src/providers/misc/solana-connector.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/widget/src/providers/misc/solana-connector.ts b/packages/widget/src/providers/misc/solana-connector.ts index 4a9982ec..6f5dab81 100644 --- a/packages/widget/src/providers/misc/solana-connector.ts +++ b/packages/widget/src/providers/misc/solana-connector.ts @@ -105,6 +105,16 @@ const createSolanaConnector = ({ if (isDisconnected) return false; + const recentConnectorId = + await config.storage?.getItem("recentConnectorId"); + + if ( + recentConnectorId && + recentConnectorId === solanaWallet.adapter.name + ) { + await solanaWallet.adapter.autoConnect(); + } + return !!( solanaWallet.adapter.connected && solanaWallet.adapter.publicKey?.toBase58()