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/.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 new file mode 100644 index 00000000..bcdce047 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# 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` + + + +## 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/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/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/.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/package.json b/packages/widget/package.json index 8d8f9017..53f78997 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://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", @@ -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", @@ -160,4 +164,4 @@ "public" ] } -} \ No newline at end of file +} 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/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/common/check-gas-amount.ts b/packages/widget/src/common/check-gas-amount.ts index 7cd1041f..c96cbbdf 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 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"; +import { tokenGetTokenBalances } from "./private-api"; 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/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/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/private-api.ts b/packages/widget/src/common/private-api.ts index 23df6ebb..1e2b0dd8 100644 --- a/packages/widget/src/common/private-api.ts +++ b/packages/widget/src/common/private-api.ts @@ -1,11 +1,18 @@ import { + type BalanceResponseDto, + type BalancesRequestDto, customFetch, + type PriceRequestDto, + type PriceResponseDto, type TokenBalanceScanDto, type TokenBalanceScanResponseDto, - type ValidatorSearchResultDto, - type YieldBalanceScanRequestDto, - type YieldBalancesWithIntegrationIdDto, + type TokenGetTokensParams, + type TokenWithAvailableYieldsDto, + type TransactionVerificationMessageDto, + type TransactionVerificationMessageRequestDto, type YieldDto, + type YieldRewardsSummaryRequestDto, + type YieldRewardsSummaryResponseDto, } from "@stakekit/api-hooks"; /** @@ -25,23 +32,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 @@ -60,24 +50,51 @@ export const yieldYieldOpportunity = ( }); }; -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, +export const tokenGetTokens = ( + params?: TokenGetTokensParams, signal?: AbortSignal -) => { - return customFetch({ - url: "/v1/yields/validators", +) => + 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/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/index.tsx b/packages/widget/src/components/atoms/token-icon/index.tsx index a237dbe7..1c14f4bb 100644 --- a/packages/widget/src/components/atoms/token-icon/index.tsx +++ b/packages/widget/src/components/atoms/token-icon/index.tsx @@ -1,4 +1,5 @@ -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 { Atoms } from "../../../styles/theme/atoms.css"; import { NetworkLogoImage } from "./network-icon-image"; @@ -12,8 +13,8 @@ export const TokenIcon = ({ tokenNetworkLogoHw, hideNetwork, }: { - token: TokenDto; - metadata?: YieldMetadataDto; + token: TokenDto | YieldTokenDto; + metadata?: Pick; tokenLogoHw?: Atoms["hw"]; tokenNetworkLogoHw?: Atoms["hw"]; hideNetwork?: boolean; 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/provider-icon/index.tsx b/packages/widget/src/components/atoms/token-icon/provider-icon/index.tsx index d7968906..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 @@ -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?: 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 94930944..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 @@ -1,12 +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"; export const useVariantTokenUrls = ( - token: TokenDto, - metadata?: YieldMetadataDto + token: TokenDto | YieldTokenDto, + metadata?: Pick ): { mainUrl: string | undefined; fallbackUrl: string | undefined; @@ -35,7 +39,7 @@ export const useVariantTokenUrls = ( const tokenMappingResult = Maybe.fromNullable(tokenIconMapping) .chainNullable((mapping) => { if (typeof mapping === "function") { - return mapping(token); + 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 a17d64f8..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 @@ -1,13 +1,14 @@ -import type { TokenDto, YieldMetadataDto } from "@stakekit/api-hooks"; import type { Networks } from "@stakekit/common"; import type { ReactElement } from "react"; +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; - metadata?: YieldMetadataDto; + token: TokenDto | YieldTokenDto; + metadata?: Pick; hideNetwork?: boolean; children: (props: TokenIconContainerReturnType) => ReactElement; }; 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/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/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..97feef83 100644 --- a/packages/widget/src/components/molecules/reward-token-details/index.tsx +++ b/packages/widget/src/components/molecules/reward-token-details/index.tsx @@ -1,12 +1,11 @@ -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"; -import { ImageFallback } from "../../atoms/image-fallback"; import { Text } from "../../atoms/typography/text"; import { inlineText } from "./style.css"; @@ -19,7 +18,7 @@ export const RewardTokenDetails = ({ | { type: "stake" | "unstake"; pendingAction?: never } | { type: "pendingAction"; - pendingAction: ActionTypes; + pendingAction: YieldPendingActionType; } )) => { const i18nKey: ComponentProps["i18nKey"] = (() => { @@ -29,7 +28,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; } @@ -51,12 +50,10 @@ export const RewardTokenDetails = ({ alignSelf="flex-start" > - } + fallbackName={rt.providerName} /> 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..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 @@ -1,8 +1,19 @@ -import type { YieldDto } from "@stakekit/api-hooks"; 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 { + getYieldCommission, + getYieldProviderDetails, + getYieldRewardTokens, + getYieldRewardType, + getYieldTVL, + type Yield, +} from "../../../domain/types/yields"; import { APToPercentage, formatNumber, fromWei } from "../../../utils"; import { getRewardRateFormatted } from "../../../utils/formatters"; import { Box } from "../../atoms/box"; @@ -16,8 +27,8 @@ export const SelectOpportunityListItem = ({ onYieldSelect, testId, }: { - item: YieldDto; - onYieldSelect: (item: YieldDto) => void; + item: Yield; + onYieldSelect: (item: Yield) => void; testId?: string; }) => { const onItemClick: ComponentProps["onItemClick"] = ({ @@ -29,9 +40,37 @@ export const SelectOpportunityListItem = ({ const { t } = useTranslation(); + const campaignRate = getRewardRateBreakdown( + getYieldRewardRateDetails(item) + ).find((rewardRate) => rewardRate.key === "campaign"); + + const totalRateFormatted = getRewardRateFormatted({ + rewardRate: item.rewardRate.total, + rewardType: getYieldRewardType(item), + }); + + const primaryRateFormatted = getRewardRateFormatted({ + rewardRate: campaignRate + ? item.rewardRate.total - campaignRate.rate + : item.rewardRate.total, + rewardType: getYieldRewardType(item), + }); + + const provider = getYieldProviderDetails(item) ?? undefined; + const rewardTokens = getYieldRewardTokens(item); + const tvl = getYieldTVL(item); + const commission = getYieldCommission(item); + return ( - + [0]["token"]} + /> - - - {getRewardRateFormatted({ - rewardRate: item.rewardRate, - rewardType: item.rewardType, - })} - + + {primaryRateFormatted} + + {campaignRate ? ( + + {t("details.apy_composition.up_to", { + value: totalRateFormatted, + })} + + ) : null} - {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(tvl) .map((tvl) => tvl.reduce( (acc, curr) => acc.plus(curr.value), @@ -83,7 +125,7 @@ export const SelectOpportunityListItem = ({ .orDefault(item.token.symbol)} - {Maybe.fromNullable(item.metadata.rewardTokens) + {Maybe.fromNullable(rewardTokens.length ? rewardTokens : null) .map((): ReactNode | string => ( {item.token.symbol} @@ -92,7 +134,7 @@ export const SelectOpportunityListItem = ({ .extractNullable()} - {Maybe.fromNullable(item.metadata.commission) + {Maybe.fromNullable(commission) .map((commission) => APToPercentage( commission.reduce((acc, curr) => acc + curr.value, 0) diff --git a/packages/widget/src/components/molecules/select-validator/index.tsx b/packages/widget/src/components/molecules/select-validator/index.tsx index 47acd9bd..f748eba4 100644 --- a/packages/widget/src/components/molecules/select-validator/index.tsx +++ b/packages/widget/src/components/molecules/select-validator/index.tsx @@ -1,7 +1,8 @@ -import type { ValidatorDto, YieldDto } from "@stakekit/api-hooks"; import type { PropsWithChildren } from "react"; import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import type { ValidatorDto } from "../../../domain/types/validators"; +import type { Yield } from "../../../domain/types/yields"; import type { SelectModalProps } from "../../atoms/select-modal"; import { SelectModal } from "../../atoms/select-modal"; import type { GroupedItem } from "./select-validator-list"; @@ -12,8 +13,8 @@ type SelectValidatorProps = PropsWithChildren< selectedValidators: Set; onItemClick: (item: ValidatorDto) => void; onViewMoreClick?: () => void; - validators: YieldDto["validators"]; - selectedStake: YieldDto; + validators: ValidatorDto[]; + selectedStake: Yield; multiSelect: boolean; } & ( | { onSearch: (value: string) => void; searchValue: string } 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..200054dc 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, @@ -29,38 +31,32 @@ export const useMetaInfo = ({ marketCap, tokenSymbol, }: { - [Key in keyof Pick< - ValidatorDto, - | "stakedBalance" - | "votingPower" - | "commission" - | "address" - | "website" - | "nominatorCount" - | "subnetName" - | "marketCap" - | "tokenSymbol" - >]: ValidatorDto[Key] | undefined; -} & { - stakedBalanceToken: YieldDto["token"] | 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; }) => { 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 5a03b4c9..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 @@ -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, @@ -12,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, @@ -44,7 +44,7 @@ export const SelectValidatorList = ({ selectedValidators: Set; onItemClick: (item: ValidatorDto) => void; onViewMoreClick: () => void; - selectedStake: YieldDto; + selectedStake: Yield; tableData: ValidatorDto[]; groupedItems: GroupedItem[]; groupCounts: number[]; @@ -80,7 +80,7 @@ export const SelectValidatorList = ({ - {getRewardTypeFormatted(selectedStake.rewardType)} + {getRewardTypeFormatted(getYieldRewardType(selectedStake))} @@ -143,16 +143,10 @@ export const SelectValidatorList = ({ )} - } + wrapperProps={{ hw: "9" }} + imgProps={{ borderRadius: "full" }} + src={item.logoURI} + fallbackName={item.name || item.address} /> {getRewardRateFormatted({ - rewardRate: item.apr, - rewardType: selectedStake.rewardType, + rewardRate: item.rewardRate?.total, + rewardType: getYieldRewardType(selectedStake), })} @@ -212,7 +206,7 @@ export const SelectValidatorList = ({ & { - onItemClick: (yieldDto: YieldDto) => void; - providerYieldIds: YieldDto["id"][]; + onItemClick: (yieldDto: Yield) => void; + providerYieldIds: Yield["id"][]; } >; 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..e8dbe376 100644 --- a/packages/widget/src/domain/index.ts +++ b/packages/widget/src/domain/index.ts @@ -1,23 +1,31 @@ +import BigNumber from "bignumber.js"; +import { Left, type Maybe, Right } from "purify-ts"; +import type { Override } from "../types/utils"; import type { ActionDto, - PendingActionDto, - 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"; -import type { TokenString } from "./types/tokens"; +} from "./types/action"; +import type { AnyPendingActionDto } from "./types/pending-action"; +import { + isPendingActionValidatorAddressesRequired, + isPendingActionValidatorAddressRequired, +} from "./types/pending-action"; +import type { TokenDto, TokenString } from "./types/tokens"; +import type { Yield } from "./types/yields"; 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 tokenString = (token: TokenLike): TokenString => { + return `${token.network}-${token.address?.toLowerCase() ?? ""}` as TokenString; }; -export const equalTokens = (a: TokenDto, b: TokenDto) => +export const equalTokens = (a: TokenLike, b: TokenLike) => tokenString(a) === tokenString(b) && a.symbol === b.symbol; export const stakeTokenSameAsGasToken = ({ @@ -25,8 +33,8 @@ export const stakeTokenSameAsGasToken = ({ yieldDto, }: { stakeToken: TokenDto; - yieldDto: YieldDto; -}) => equalTokens(stakeToken, getGasFeeToken(yieldDto)); + yieldDto: Yield; +}) => equalTokens(stakeToken, yieldDto.mechanics.gasFeeToken); export const getMaxAmount = ({ availableAmount, @@ -46,10 +54,6 @@ export const getMaxAmount = ({ ); }; -export const getBaseToken = (yieldDto: YieldDto) => yieldDto.metadata.token; -export const getGasFeeToken = (yieldDto: YieldDto) => - yieldDto.metadata.gasFeeToken; - /** * * @summary Get stake transactions available for signing or tx status check. @@ -81,11 +85,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/action.ts b/packages/widget/src/domain/types/action.ts index 33f09887..dec8fe40 100644 --- a/packages/widget/src/domain/types/action.ts +++ b/packages/widget/src/domain/types/action.ts @@ -1,6 +1,191 @@ -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 { getYieldMetadataTokens, type Yield } 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 ?? []), + ...getYieldMetadataTokens(yieldDto), + ].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: TransactionDto +): 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)) { + return null; + } + + const amount = parsed.amount ?? null; + const token = parsed.token; + + if (!amount || !token) { + return null; + } + + return { + amount, + token, + }; + } catch { + return null; + } +}; + +export const getActionGasFeeToken = ( + yieldDto: Yield, + gasFeeToken?: TokenDto +): TokenDto => gasFeeToken ?? yieldDto.mechanics.gasFeeToken; + +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/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/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 new file mode 100644 index 00000000..65fc18a8 --- /dev/null +++ b/packages/widget/src/domain/types/pending-action.ts @@ -0,0 +1,109 @@ +import type { PendingActionDto as LegacyPendingActionDto } from "@stakekit/api-hooks"; +import type { components } from "../../types/yield-api-schema"; + +export type YieldPendingActionDto = components["schemas"]["PendingActionDto"]; +export type YieldPendingActionType = + | YieldPendingActionDto["type"] + | NonNullable; + +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..ecc5786f 100644 --- a/packages/widget/src/domain/types/positions.ts +++ b/packages/widget/src/domain/types/positions.ts @@ -1,14 +1,25 @@ -import type { - BalanceTypes, - TokenDto, - YieldBalanceDto, - YieldBalancesWithIntegrationIdDto, -} from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; +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< - BalanceTypes, + YieldBalanceType, (YieldBalanceDto & { tokenPriceInUsd: BigNumber; })[] @@ -16,12 +27,19 @@ 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"]; + rewardRate?: YieldRewardRateDto | null; balanceData: Map< - YieldBalanceDto["groupId"], + BalanceDataKey, { balances: YieldBalanceDto[] } & ( | { type: "validators"; validatorsAddresses: string[] } | { type: "default" } @@ -30,22 +48,64 @@ 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[], + 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; + + 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); + + 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/domain/types/price.ts b/packages/widget/src/domain/types/price.ts index b63a7433..cbdb4801 100644 --- a/packages/widget/src/domain/types/price.ts +++ b/packages/widget/src/domain/types/price.ts @@ -1,9 +1,17 @@ -import type { TokenDto } from "@stakekit/api-hooks"; +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; + address?: string; +}; + export type Price = { price: number | undefined; price24H: number | undefined; @@ -12,7 +20,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 +32,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/reward-rate.ts b/packages/widget/src/domain/types/reward-rate.ts new file mode 100644 index 00000000..4d4b3fe4 --- /dev/null +++ b/packages/widget/src/domain/types/reward-rate.ts @@ -0,0 +1,81 @@ +import type { components } from "../../types/yield-api-schema"; +import type { Yield } from "./yields"; + +export type RewardTypes = "apr" | "apy" | "variable"; +export type YieldRewardDto = components["schemas"]["RewardDto"]; +export type YieldRewardRateDto = components["schemas"]["RewardRateDto"]; + +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: Yield | null | undefined +): YieldRewardRateDto | undefined => yieldDto?.rewardRate; + +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/domain/types/rewards.ts b/packages/widget/src/domain/types/rewards.ts index 0dac8575..84fbc6d2 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,7 +52,7 @@ 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"]}`; }[] >; 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 115a5b71..35cb9b2e 100644 --- a/packages/widget/src/domain/types/stake.ts +++ b/packages/widget/src/domain/types/stake.ts @@ -1,8 +1,4 @@ -import type { - AmountArgumentOptionsDto, - TokenBalanceScanResponseDto, - 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"; @@ -11,7 +7,8 @@ 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, isBittensorStaking, type Yield } from "./yields"; const amountGreaterThanZero = (val: TokenBalanceScanResponseDto) => new BigNumber(val.amount).isGreaterThan(0); @@ -23,7 +20,7 @@ const hasYieldsAndAmount = (val: TokenBalanceScanResponseDto) => hasYields(val) && amountGreaterThanZero(val); export type PreferredTokenYieldsPerNetwork = { - [Key in SupportedSKChains]?: Record; + [Key in SupportedSKChains]?: Record; }; export const getInitialToken = (args: { @@ -85,11 +82,12 @@ 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: { initQueryParams: Maybe; - yieldDto: YieldDto; + yieldDto: Yield; tokenBalanceAmount: BigNumber; positionsData: PositionsData; }) => @@ -117,7 +115,7 @@ const balanceValidForYield = ({ positionsData, }: { tokenBalanceAmount: BigNumber; - yieldDto: YieldDto; + yieldDto: Yield; positionsData: PositionsData; }) => tokenBalanceAmount.isGreaterThanOrEqualTo( @@ -126,7 +124,7 @@ const balanceValidForYield = ({ export const getInitSelectedValidators = (args: { initQueryParams: Maybe; - yieldDto: YieldDto; + validators: ValidatorDto[]; }) => args.initQueryParams .chainNullable((params) => params.validator) @@ -135,46 +133,46 @@ 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()); -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 + yieldDto.mechanics.gasFeeToken.network as Networks ) ) .filter((set) => set.has(yieldDto.id)) .isJust(); export const getMinStakeAmount = ( - yieldDto: YieldDto, + 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 === "staked")) + val.some((v) => v.balances.some((b) => b.type === "active")) ) .orDefault(false); @@ -189,11 +187,11 @@ export const getMinStakeAmount = ( }; export const getMinUnstakeAmount = ( - yieldDto: YieldDto, + 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..0cfabcf8 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") { 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..02044941 --- /dev/null +++ b/packages/widget/src/domain/types/validators.ts @@ -0,0 +1,4 @@ +import type { components } from "../../types/yield-api-schema"; + +export type YieldValidatorDto = components["schemas"]["ValidatorDto"]; +export type ValidatorDto = YieldValidatorDto; diff --git a/packages/widget/src/domain/types/wallets/generic-wallet.ts b/packages/widget/src/domain/types/wallets/generic-wallet.ts index 0f3dd1b2..94590353 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; 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 e33c0169..5ba4a0ad 100644 --- a/packages/widget/src/domain/types/yields.ts +++ b/packages/widget/src/domain/types/yields.ts @@ -1,11 +1,39 @@ -import type { 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 { List, Maybe } from "purify-ts"; +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 { TokenString, 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]: { @@ -27,56 +55,194 @@ export type ValidatorsConfig = Map< } >; -export const filterMapValidators = ( - validatorsConfig: ValidatorsConfig, - yieldDto: YieldDto -): YieldDto => { +export const filterValidators = ({ + validatorsConfig, + validators, + network, + yieldId, +}: { + validatorsConfig: ValidatorsConfig; + validators: ValidatorDto[]; + network: Yield["token"]["network"]; + yieldId?: Yield["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 filtered; +}; - return { - ...yieldDto, - validators: yieldDto.validators.flatMap((v) => { - if (allowed && !allowed.has(v.address)) return []; - if (blocked?.has(v.address)) return []; +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; - const isPreferred = - preferred?.has(v.address) || - !!(mergePreferredWithDefault && v.preferred); + return { days: Math.round(seconds / 86400) }; +}; - if (preferredOnly) { - return isPreferred ? [{ ...v, preferred: true }] : []; - } +export const getBaseYieldType = ( + yieldDto: Yield +): LegacyYieldType | "liquid-staking" => { + if (yieldDto.__fallback__.metadata.type === "liquid-staking") { + return "liquid-staking"; + } - return [{ ...v, preferred: isPreferred }]; - }), + switch (yieldDto.mechanics.type) { + case "staking": + case "restaking": + case "lending": + return yieldDto.mechanics.type; + default: + return "vault"; + } +}; + +export const getYieldActionArg = ( + yieldDto: Yield, + type: YieldActionType, + name: YieldArgumentName +): YieldArgumentConfig | null => { + const field = yieldDto.mechanics.arguments?.[type]?.fields?.find( + (item) => item.name === name + ); + + if (!field) { + return null; + } + + return { + required: !!field.required, + minimum: toNumber(field.minimum), + maximum: toNumber(field.maximum), + ...(field.options ? { options: field.options } : {}), }; }; -export const getExtendedYieldType = (yieldDto: YieldDto) => +export const isYieldActionArgRequired = ( + yieldDto: Yield, + type: YieldActionType, + name: YieldArgumentName +) => !!getYieldActionArg(yieldDto, type, name)?.required; + +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 = tokenString(token); + + 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 getYieldProviderDetails = (yieldDto: Yield) => + 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); + +export const getYieldWarmupPeriod = (yieldDto: Yield) => + secondsToDays(yieldDto.mechanics.warmupPeriod?.seconds); + +export const getYieldWithdrawPeriod = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.withdrawPeriod; + +export const getYieldCommission = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.commission; + +export const getYieldTVL = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.tvl; + +export const getYieldLockupPeriod = (yieldDto: Yield) => + yieldDto.__fallback__.metadata.lockupPeriod; + +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) ? "native_staking" : isPooledStaking(yieldDto) ? "pooled_staking" - : yieldDto.metadata.type; + : getBaseYieldType(yieldDto); export const getYieldTypeLabels = ( - yieldDto: YieldDto, + yieldDto: Yield, t: TFunction ): YieldTypeLabelsMap[keyof YieldTypeLabelsMap] => { const map = { @@ -132,7 +298,7 @@ export const getYieldTypeLabels = ( return map.pooled_staking; } - return map[yieldDto.metadata.type]; + return map[getExtendedYieldType(yieldDto)]; }; const yieldTypesSortRank: { [Key in ExtendedYieldType]: number } = { @@ -145,33 +311,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"; @@ -184,24 +360,10 @@ const zeroRewardRateYieldIdWhitelist = new Set([ ]); 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; -}; + 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 b577c9a2..0153d79c 100644 --- a/packages/widget/src/hooks/api/use-activity-actions.ts +++ b/packages/widget/src/hooks/api/use-activity-actions.ts @@ -1,72 +1,79 @@ -import { - type ActionDto, - type ActionList200, - ActionStatus, - actionList, - getActionListQueryKey, -} from "@stakekit/api-hooks"; 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 { useValidatorsConfig } from "../use-validators-config"; +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"; -export const useActivityActions = () => { - const { address, network, isLedgerLive } = useSKWallet(); - const queryClient = useSKQueryClient(); +const PAGE_SIZE = 50; + +type ActivityActionItem = { + actionData: ActionDto; + yieldData: Yield; +}; - const validatorsConfig = useValidatorsConfig(); +type UseActivityActionsResult = ReturnType & { + allItems: ActivityActionItem[] | undefined; +}; + +export const useActivityActions = (): UseActivityActionsResult => { + const { address, isLedgerLive, network } = useSKWallet(); + const queryClient = useSKQueryClient(); + const yieldApiFetchClient = useYieldApiFetchClient(); const query = useInfiniteQuery({ enabled: !!address && !!network, - queryKey: getActionListQueryKey({ - network: network!, - walletAddress: address!, - }), - queryFn: async ({ pageParam = 1 }) => { + queryKey: ["activity-actions", address, network], + queryFn: async ({ pageParam = 0 }) => { return ( await EitherAsync(() => - actionList({ - page: pageParam, - walletAddress: address!, + listActions({ + address: address!, + fetchClient: yieldApiFetchClient, + limit: PAGE_SIZE, + offset: pageParam, network: network!, - sort: "createdAtDesc", }) ) .mapLeft(() => new Error("Could not get action list")) - .map((actionList) => ({ - ...actionList, - data: actionList.data.filter( - (x) => x.status !== ActionStatus.CREATED - ), - })) .chain(async (actionList) => EitherAsync.all( - (actionList.data as ActionList200["data"]).map((action) => + (actionList.items ?? []).map((action) => getYieldOpportunity({ - yieldId: action.integrationId, + yieldId: action.yieldId, queryClient, isLedgerLive, - validatorsConfig, + yieldApiFetchClient, }) .map((yieldData) => ({ - actionData: action as typeof action & ActionDto, + actionData: action as ActionDto, yieldData, })) .chainLeft(() => EitherAsync(() => Promise.resolve(null))) ) ) .map((res) => res.filter((x) => x !== null)) - .map((res) => res.filter((x) => !!x.actionData.inputToken)) + .map((res) => + res.filter( + (x) => + !!getActionInputToken({ + actionDto: x.actionData, + yieldDto: x.yieldData, + }) + ) + ) .map((data) => ({ ...actionList, data })) ) ).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-default-tokens.ts b/packages/widget/src/hooks/api/use-default-tokens.ts index 408ae0ee..4377fd7c 100644 --- a/packages/widget/src/hooks/api/use-default-tokens.ts +++ b/packages/widget/src/hooks/api/use-default-tokens.ts @@ -1,14 +1,15 @@ -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 { 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(); diff --git a/packages/widget/src/hooks/api/use-multi-yields.ts b/packages/widget/src/hooks/api/use-multi-yields.ts index b2b263bb..ae586763 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,21 +31,24 @@ 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"; +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"; 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(); @@ -61,10 +63,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, }); @@ -98,20 +102,22 @@ 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(); const validatorsConfig = useValidatorsConfig(); const argsRef = useSavedRef({ isLedgerLive, queryClient: useSKQueryClient(), + yieldApiFetchClient, network, isConnected, }); @@ -147,6 +153,7 @@ export const getFirstEligibleYield = ( const multipleYields$ = (args: { isLedgerLive: boolean; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; isConnected: boolean; network: SKWallet["network"]; yieldIds: string[]; @@ -159,14 +166,14 @@ const multipleYields$ = (args: { isLedgerLive: args.isLedgerLive, yieldId: v, queryClient: args.queryClient, - validatorsConfig: args.validatorsConfig, + yieldApiFetchClient: args.yieldApiFetchClient, }) ) ) ).pipe( map((v) => (v.isRight() ? v.extract() : null)), filter( - (v): v is YieldDto => + (v): v is Yield => !!( v && defaultFiltered({ @@ -182,6 +189,7 @@ const multipleYields$ = (args: { const firstEligibleYield$ = (args: { isLedgerLive: boolean; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; isConnected: boolean; network: SKWallet["network"]; yieldIds: string[]; @@ -191,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) => { @@ -227,7 +235,7 @@ const firstEligibleYield$ = (args: { defaultIfEmpty(null) ); - return new Observable((subscriber) => { + return new Observable((subscriber) => { successStream.subscribe({ complete: () => subscriber.complete(), next: (v) => subscriber.next(v ?? defaultYield), @@ -237,7 +245,7 @@ const firstEligibleYield$ = (args: { }; type SelectorInputData = { - data: YieldDto[]; + data: Yield[]; isConnected: boolean; network: SKWallet["network"]; isLedgerLive: boolean; @@ -254,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" && @@ -280,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 b048feec..12615397 100644 --- a/packages/widget/src/hooks/api/use-prices.ts +++ b/packages/widget/src/hooks/api/use-prices.ts @@ -1,8 +1,14 @@ -import type { PriceRequestDto, PriceResponseDto } from "@stakekit/api-hooks"; -import { useTokenGetTokenPrices } from "@stakekit/api-hooks"; +import { useQuery } from "@tanstack/react-query"; import { useCallback } from "react"; import { createSelector } from "reselect"; -import type { Prices } from "../../domain/types/price"; +import { tokenGetTokenPrices } from "../../common/private-api"; +import type { StakeKitErrorDto } from "../../domain/types/errors"; +import type { + PriceRequestDto, + PriceResponseDto, + Prices, +} from "../../domain/types/price"; +import type { YieldTokenDto } from "../../providers/yield-api-client-provider/types"; import { priceResponseDtoToPrices } from "../../utils/mappers"; const defaultParam: PriceRequestDto = { @@ -17,28 +23,46 @@ const pricesSelector = createSelector( (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: PriceRequestDto | null | undefined, + priceRequestDto: PriceRequestInput | null | undefined, opts?: { enabled?: boolean; select?: (val: Prices) => T; } ) => { - return useTokenGetTokenPrices(priceRequestDto ?? defaultParam, { - 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] - ), - }, + const requestDto = priceRequestDto + ? ({ + ...priceRequestDto, + tokenList: priceRequestDto.tokenList.map((token) => ({ + ...token, + network: + token.network as PriceRequestDto["tokenList"][number]["network"], + })), + } satisfies PriceRequestDto) + : defaultParam; + + 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 86e14f6d..3df8ac21 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"; diff --git a/packages/widget/src/hooks/api/use-tokens-prices.ts b/packages/widget/src/hooks/api/use-tokens-prices.ts index 43a716be..5e9672f1 100644 --- a/packages/widget/src/hooks/api/use-tokens-prices.ts +++ b/packages/widget/src/hooks/api/use-tokens-prices.ts @@ -1,9 +1,8 @@ -import type { PriceRequestDto, TokenDto, YieldDto } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; import { config } from "../../config"; -import { useBaseToken } from "../use-base-token"; -import { useGasFeeToken } from "../use-gas-fee-token"; +import type { TokenDto, YieldTokenDto } from "../../domain/types/tokens"; +import type { Yield } from "../../domain/types/yields"; import { usePrices } from "./use-prices"; /** @@ -13,21 +12,18 @@ export const useTokensPrices = ({ token, yieldDto, }: { - token: Maybe; - yieldDto: Maybe; + token: Maybe; + yieldDto: Maybe; }) => { - const baseToken = useBaseToken(yieldDto); - const gasFeeToken = useGasFeeToken(yieldDto); - const priceRequestDto = useMemo( () => - Maybe.fromRecord({ baseToken, gasFeeToken, token }) - .map((val) => ({ + Maybe.fromRecord({ yieldDto, token }) + .map((val) => ({ currency: config.currency, - tokenList: [val.token, val.baseToken, val.gasFeeToken], + tokenList: [val.token, val.token, val.yieldDto.mechanics.gasFeeToken], })) .extractNullable(), - [baseToken, gasFeeToken, token] + [yieldDto, 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 eed36cef..91798551 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 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 { 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/api/use-yield-opportunity/get-yield-opportunity.ts b/packages/widget/src/hooks/api/use-yield-opportunity/get-yield-opportunity.ts index 0c248f47..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 @@ -1,19 +1,14 @@ -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 { - filterMapValidators, - getComputedRewardRate, - isBittensorStaking, - isEthenaUsdeStaking, - type ValidatorsConfig, -} from "../../../domain/types/yields"; +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"; type Params = { yieldId: string; isLedgerLive: boolean; - validatorsConfig: ValidatorsConfig; + yieldApiFetchClient: YieldApiFetchClient; signal?: AbortSignal; }; @@ -50,38 +45,47 @@ const fn = ({ isLedgerLive, yieldId, signal, - validatorsConfig, + yieldApiFetchClient, }: Params & { signal?: AbortSignal; -}) => - EitherAsync(() => - yieldYieldOpportunity( - yieldId, - { - ledgerWalletAPICompatible: isLedgerLive, - }, - signal - ) - ) - .map((y) => filterMapValidators(validatorsConfig, y)) +}) => { + return EitherAsync(async () => { + const [newYieldResult, legacyYieldResult] = await Promise.all([ + getResponseData( + yieldApiFetchClient.GET("/v1/yields/{yieldId}", { + params: { + path: { + yieldId, + }, + }, + signal, + }) + ), + yieldYieldOpportunity( + yieldId, + { ledgerWalletAPICompatible: isLedgerLive }, + signal + ), + ]); + + return { + ...newYieldResult, + __fallback__: legacyYieldResult, + } satisfies Yield; + }) .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 + } satisfies Yield) + : 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..a7a9d596 --- /dev/null +++ b/packages/widget/src/hooks/api/use-yield-validators.ts @@ -0,0 +1,103 @@ +import { useQuery } from "@tanstack/react-query"; +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"; +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?: Yield["token"]["network"]; + validatorsConfig: ValidatorsConfig; + yieldApiFetchClient: ReturnType; + signal?: AbortSignal; +}; + +const getYieldValidatorsQueryKey = ({ yieldId }: Pick) => [ + "yield-validators", + yieldId, +]; + +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 ?? [] + ); + + 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?: Yield["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/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-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-base-token.ts b/packages/widget/src/hooks/use-base-token.ts deleted file mode 100644 index be048069..00000000 --- a/packages/widget/src/hooks/use-base-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { YieldDto } from "@stakekit/api-hooks"; -import type { Maybe } from "purify-ts"; -import { useMemo } from "react"; -import { getBaseToken } from "../domain"; - -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..e60e72ee 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"]; @@ -51,9 +54,11 @@ export const useEstimatedRewards = ({ .dividedBy(val.providersDetails.length), })) .map((val) => ({ + rewardRateAverage: val.rewardRateAverage, + rewardType: getYieldRewardType(val.selectedStake), percentage: getRewardRateFormatted({ rewardRate: val.rewardRateAverage.toNumber(), - rewardType: val.selectedStake.rewardType, + rewardType: getYieldRewardType(val.selectedStake), }), yearly: val.rewardRateAverage.isGreaterThan(0) ? formatNumber( 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 deleted file mode 100644 index cd85ba37..00000000 --- a/packages/widget/src/hooks/use-gas-fee-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { YieldDto } from "@stakekit/api-hooks"; -import type { Maybe } from "purify-ts"; -import { useMemo } from "react"; -import { getGasFeeToken } from "../domain"; - -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..e593fb19 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: { @@ -50,12 +51,16 @@ export const useGasWarningCheck = ( 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, diff --git a/packages/widget/src/hooks/use-geo-block.ts b/packages/widget/src/hooks/use-geo-block.ts index 6b55e5df..4d02881c 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 @@ -19,23 +21,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-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-max-min-yield-amount.ts b/packages/widget/src/hooks/use-max-min-yield-amount.ts index 7b2463d9..2db678e9 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 } @@ -52,11 +52,7 @@ export const useMaxMinYieldAmount = ({ 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]); 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..613e8641 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,10 +16,21 @@ 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) => { + const balanceData = + val.positionData.balanceData.get(val.balanceId) ?? + 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 1ad3b2b5..f88c60e3 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,44 @@ 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, + rewardRate: val.rewardRate, 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 +72,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-provider-details.ts b/packages/widget/src/hooks/use-provider-details.ts index 9975dee5..fd4349df 100644 --- a/packages/widget/src/hooks/use-provider-details.ts +++ b/packages/widget/src/hooks/use-provider-details.ts @@ -1,13 +1,18 @@ -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 { + getYieldProviderDetails, getYieldProviderYieldIds, + getYieldRewardType, isYieldWithProviderOptions, + type Yield, } from "../domain/types/yields"; 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; @@ -16,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"]; @@ -29,27 +34,31 @@ export const getProviderDetails = ({ validatorAddress, yields, 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 = val.rewardRate.total; + const rewardType = getYieldRewardType(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(), })) @@ -59,7 +68,7 @@ export const getProviderDetails = ({ name: val.metadata.name, rewardRateFormatted, rewardRate, - rewardType: val.rewardType, + rewardType, address: validatorAddress.extract(), }) ); @@ -69,8 +78,11 @@ export const getProviderDetails = ({ validatorAddress .chain>((addr) => List.find( - (v) => v.address === addr || v.providerId === addr, - yieldDto.validators + (v) => + v.address === addr || + v.providerId === addr || + v.provider?.id === addr, + validatorsData ?? [] ).map((validator) => { const { rewardRate, rewardType } = Maybe.fromRecord({ _: Maybe.fromFalsy(isYieldWithProviderOptions(yieldDto)), @@ -79,26 +91,29 @@ export const getProviderDetails = ({ .chain(({ selectedProviderYieldId }) => yields.chain(List.find((v) => v.id === selectedProviderYieldId)) ) - .map((v) => v.rewardRate + v.rewardRate) + .map((v) => v.rewardRate.total + v.rewardRate.total) .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, + rewardRate: validator.rewardRate?.total, + rewardType: getYieldRewardType(yieldDto), }); return { - logo: validator.image, + logo: validator.logoURI, name: validator.name ?? validator.address, rewardRateFormatted: getRewardRateFormatted({ rewardRate, rewardType, }), rewardRate, - rewardType: yieldDto.rewardType, + rewardType: getYieldRewardType(yieldDto), address: validator.address, - stakedBalance: validator.stakedBalance, + stakedBalance: validator.tvl, votingPower: validator.votingPower, commission: validator.commission, status: validator.status, @@ -115,15 +130,51 @@ export const useProvidersDetails = ({ integrationData, validatorsAddresses, selectedProviderYieldId, + validatorsData, }: { - integrationData: Maybe; + 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 +188,8 @@ export const useProvidersDetails = ({ validatorAddress: Maybe.of(v), yields: Maybe.fromNullable(yields.data), selectedProviderYieldId, + validatorsData: + resolvedValidatorsData.extractNullable() ?? undefined, }) ) ).chain((val) => @@ -150,6 +203,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-reward-token-details/get-reward-token-symbols.tsx b/packages/widget/src/hooks/use-reward-token-details/get-reward-token-symbols.tsx index 2b01f97f..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 @@ -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[]) => 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..dbab7cb1 100644 --- a/packages/widget/src/hooks/use-reward-token-details/index.ts +++ b/packages/widget/src/hooks/use-reward-token-details/index.ts @@ -1,5 +1,9 @@ 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"; @@ -10,9 +14,10 @@ export const useRewardTokenDetails = ( () => 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, diff --git a/packages/widget/src/hooks/use-rewards-summary.ts b/packages/widget/src/hooks/use-rewards-summary.ts index e85447b5..ac206ede 100644 --- a/packages/widget/src/hooks/use-rewards-summary.ts +++ b/packages/widget/src/hooks/use-rewards-summary.ts @@ -1,20 +1,20 @@ -import { - type AddressesDto, - type YieldDto, - 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, } 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"][], + yieldIds: Yield["id"][], opts?: { select?: (val: RewardsSummaryResult) => T } ) => { const filteredYieldIds = useMemo( @@ -51,8 +51,7 @@ export const useMultiRewardsSummary = ( ).then((res) => res.reduce( (acc, next) => { - acc[next.yieldId] = - next.data as unknown as YieldRewardsSummaryResponseDto; + acc[next.yieldId] = next.data; return acc; }, {} as Record< @@ -64,7 +63,7 @@ export const useMultiRewardsSummary = ( }); }; -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 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/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..d7a609b0 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 { 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 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"; @@ -20,17 +21,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 +48,10 @@ const SummaryContext = createContext< }, StakeKitErrorDto >; - averageApyQuery: UseQueryResult; + averageApyQuery: { + data: BigNumber | undefined; + isLoading: boolean; + }; availableBalanceSumQuery: UseQueryResult; } | undefined @@ -65,7 +71,7 @@ export const SummaryProvider = ({ 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])), [] ), }); @@ -86,84 +92,57 @@ 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, + yieldDto.token + ); + + 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( { @@ -193,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, }; @@ -246,81 +223,56 @@ export const SummaryProvider = ({ } ); - const averageApyQuery = usePrices( - { - currency: config.currency, - tokenList: useMemo(() => { - if (!multiYieldsMapQuery.data) return []; + const averageApyQuery = useMemo(() => { + if (!multiYieldsMapQuery.data) { + return { + data: undefined as undefined, + isLoading: multiYieldsMapQuery.isLoading, + }; + } - return positionsData.data.flatMap((v) => { - const yieldDto = multiYieldsMapQuery.data.get(v.integrationId); + const { totalWeightedApy, totalValue } = positionsData.data.reduce( + (acc, p) => { + const yieldDto = multiYieldsMapQuery.data.get(p.integrationId); - if (!yieldDto) return []; + if (!yieldDto) return acc; - const baseToken = getBaseToken(yieldDto); + const positionTotalAmount = getPositionTotalAmount( + p.balancesWithAmount, + yieldDto.token + ); - 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 usdAmount = positionTotalAmount.amountUsd; - 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 rewardRate = yieldDto.rewardRate.total; - if (totalValue.gt(0)) { - return totalWeightedApy.div(totalValue); - } + if (rewardRate > 0 && usdAmount.gt(0)) { + return { + totalWeightedApy: acc.totalWeightedApy.plus( + usdAmount.times(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-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/hooks/use-yield-meta-info.tsx b/packages/widget/src/hooks/use-yield-meta-info.tsx index b614624b..4ecbb5dd 100644 --- a/packages/widget/src/hooks/use-yield-meta-info.tsx +++ b/packages/widget/src/hooks/use-yield-meta-info.tsx @@ -1,10 +1,23 @@ -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 { TokenDto, YieldTokenDto } from "../domain/types/tokens"; +import type { ValidatorDto } from "../domain/types/validators"; +import { + getBaseYieldType, + getYieldCooldownPeriod, + getYieldLockupPeriod, + getYieldProviderDetails, + getYieldRewardTokens, + getYieldRewardType, + getYieldWarmupPeriod, + getYieldWithdrawPeriod, + hasYieldFeeConfigurationEnabled, + isEthenaUsdeStaking, + type Yield, +} from "../domain/types/yields"; import { capitalizeFirstLowerRest } from "../utils/text"; export const useYieldMetaInfo = ({ @@ -12,11 +25,11 @@ export const useYieldMetaInfo = ({ validators, tokenDto, }: { - selectedStake: Maybe; + selectedStake: Maybe; validators: { [Key in keyof Pick]?: ValidatorDto[Key]; }[]; - tokenDto: Maybe; + tokenDto: Maybe; }) => { const { t } = useTranslation(); @@ -38,27 +51,26 @@ export const useYieldMetaInfo = ({ typeof ifNotFound >(({ selectedStake: y, tokenDto }) => { const sv = validatorsFormatted.extract(); - - const haveFeeConfigurationEnabled = y.feeConfigurations.length > 0; - + const haveFeeConfigurationEnabled = hasYieldFeeConfigurationEnabled(y); const stakeToken = tokenDto.symbol; - const rewardTokens = - y.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; + 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 ( - y.metadata.rewardSchedule === "campaign" && + rewardSchedule === "campaign" && stakeToken.toUpperCase() === "SUSD" ) { return { @@ -98,7 +110,7 @@ export const useYieldMetaInfo = ({ const def = { campaign: - y.metadata.rewardSchedule === "campaign" ? ( + rewardSchedule === "campaign" ? ( ) : null, - lockupPeriod: y.metadata.lockupPeriod?.days + lockupPeriod: lockupPeriodDays ? t("details.lockup_period", { - count: y.metadata.lockupPeriod?.days, + count: lockupPeriodDays, }) : null, extra: - y.rewardType === "variable" + getYieldRewardType(y) === "variable" ? t("details.reward_type_varialbe", { symbol: capitalizeFirstLowerRest(y.token.symbol), }) - : y.metadata.token.network === MiscNetworks.Tezos + : y.token.network === MiscNetworks.Tezos ? t("details.extra_tezos") : undefined, }; - switch (y.metadata.type) { + switch (yieldType) { case "staking": { return { description: null, @@ -242,12 +254,12 @@ export const useYieldMetaInfo = ({ }), withdrawnTime: y.status.exit ? cooldownPeriodDays > 0 - ? (y.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: y.metadata.withdrawPeriod?.days, + count: withdrawPeriodDays, }) : 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/pages-dashboard/activity/action-list-item/index.tsx b/packages/widget/src/pages-dashboard/activity/action-list-item/index.tsx index f1bf0061..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 @@ -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 { 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 { 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,14 @@ export const ActionListItem = ({ justifyContent="flex-start" alignItems="center" > - + { 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..1b0c93a2 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 { diff --git a/packages/widget/src/pages-dashboard/activity/position-balances.tsx b/packages/widget/src/pages-dashboard/activity/position-balances.tsx index 6faab230..a6be06c0 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 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 "../../domain/types/positions"; +import { getBaseYieldType, type Yield } from "../../domain/types/yields"; import { defaultFormattedNumber } from "../../utils"; export const PositionBalances = ({ @@ -11,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 = getBaseYieldType(integrationData); const balanceTypeContext = yieldType === "vault" || yieldType === "lending" 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..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 @@ -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"; @@ -25,12 +26,15 @@ export const UtilaSelectValidatorSection = () => { ) : ( Maybe.fromRecord({ selectedStake, validatorsData }) - .filter((val) => !!val.selectedStake.validators.length) + .filter((val) => !!val.validatorsData.length) .map((val) => { const selectedValidatorsArr = [...selectedValidators.values()]; - const multiSelect = - !!val.selectedStake.args.enter.args?.validatorAddresses?.required; + const multiSelect = isYieldActionArgRequired( + val.selectedStake, + "enter", + "validatorAddresses" + ); return ( - - - } + wrapperProps={{ hw: "5", marginLeft: "1" }} + imgProps={{ borderRadius: "full" }} + src={v.logoURI} + fallbackName={nameOrAddress} /> 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..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,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 { getYieldProviderDetails } from "../../../../domain/types/yields"; import { columnContainer, listItem, @@ -61,7 +62,14 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), 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..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 @@ -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, - }) - ) + totalAmountUsd + .filter((v) => v.isGreaterThan(0)) .map(defaultFormattedNumber), - [totalAmount, baseToken, prices, tokenToDisplay] + [totalAmountUsd] ); const rewardsAmountPriceFormatted = useMemo( 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..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 @@ -1,13 +1,14 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; import { Maybe } from "purify-ts"; import { useTranslation } from "react-i18next"; 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 { 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"; +import type { YieldPendingActionType } from "../../../providers/yield-api-client-provider/types"; import { container } from "./styles.css"; export const positionDetailsActionsHasContent = ( @@ -34,6 +35,7 @@ export const PositionDetailsActions = () => { const { isLoading, integrationData, + validatorsData, positionBalancesByType, unstakeToken, providersDetails, @@ -102,7 +104,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} @@ -150,7 +152,7 @@ export const PositionDetailsActions = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${v.integrationData.metadata.type}` + `position_details.unstake_label.${getBaseYieldType(v.integrationData)}` )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} @@ -173,7 +175,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-dashboard/position-details/components/position-details-info.tsx b/packages/widget/src/pages-dashboard/position-details/components/position-details-info.tsx index 0e666712..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 @@ -7,9 +7,9 @@ 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 { 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"; @@ -21,8 +21,7 @@ export const PositionDetailsInfo = () => { integrationData, positionBalancesByType, providersDetails, - positionLabel, - liquidTokensToNativeConversion, + shareToAmountConversions, } = usePositionDetails(); const { t } = useTranslation(); @@ -51,38 +50,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) => @@ -91,7 +58,7 @@ export const PositionDetailsInfo = () => { {...p} key={p.address ?? idx} stakeType={t( - `position_details.stake_type.${val.integrationData.metadata.type}` + `position_details.stake_type.${getBaseYieldType(val.integrationData)}` )} integrationData={val.integrationData} /> @@ -134,7 +101,7 @@ export const PositionDetailsInfo = () => { )} - {liquidTokensToNativeConversion + {shareToAmountConversions .filter((val) => val.size > 0) .map((val) => ( - - - } + fallbackName={nameOrAddress} /> 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..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,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 { getYieldProviderDetails } from "../../../domain/types/yields"; import { usePositionDetails } from "../../../pages/position-details/hooks/use-position-details"; import { topHeaderYieldName } from "./styles.css"; @@ -18,7 +19,12 @@ export const TopHeader = () => { <> {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 529d2a95..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 @@ -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 { 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"; @@ -34,12 +39,23 @@ export const useActivityComplete = () => { 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.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })), [selectedYield] ); @@ -47,8 +63,10 @@ export const useActivityComplete = () => { 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..f30093b2 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"; 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 c2621c1f..cd1e4f9a 100644 --- a/packages/widget/src/pages/complete/pages/common.page.tsx +++ b/packages/widget/src/pages/complete/pages/common.page.tsx @@ -1,19 +1,15 @@ -import type { - ActionTypes, - TokenDto, - YieldDto, - YieldMetadataDto, -} from "@stakekit/api-hooks"; 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"; 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"; +import type { YieldPendingActionType } from "../../../domain/types/pending-action"; +import type { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import { type ExtendedYieldType, isEthenaUsdeStaking, @@ -28,11 +24,11 @@ import { } from "../state"; type Props = { - token: Maybe; - metadata: Maybe; + token: Maybe; + metadata: Maybe["metadata"]>; network: string; amount: string; - pendingActionType?: ActionTypes; + pendingActionType?: YieldPendingActionType; providersDetails: Maybe< { logo: string | undefined; @@ -40,7 +36,7 @@ type Props = { }[] >; yieldType: Maybe; - integrationId: YieldDto["id"]; + integrationId: string; }; export const CompletePageComponent = ({ @@ -133,7 +129,7 @@ export const CompletePageComponent = ({ tokenNetwork: network, pendingAction: t( `complete.pending_action.${ - pendingActionType?.toLowerCase() as Lowercase + pendingActionType?.toLowerCase() as Lowercase }` as const, { context: isEthenaUsdeStaking(integrationId) @@ -160,15 +156,10 @@ export const CompletePageComponent = ({ > {v.logo && ( - } + fallbackName={v.name || v.logo} /> )} @@ -201,12 +192,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 b47d1c11..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,6 +2,7 @@ import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +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"; @@ -44,14 +45,18 @@ export const PendingCompletePage = () => { selectedProviderYieldId: Maybe.empty(), }); - const metadata = integrationData.map((d) => d.metadata); + 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( () => - 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..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,6 +2,7 @@ import { useSelector } from "@xstate/store/react"; import BigNumber from "bignumber.js"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +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"; @@ -27,20 +28,27 @@ export const StakeCompletePage = () => { [enterRequest.selectedToken] ); - const metadata = selectedStake.map((y) => y.metadata); + const metadata = selectedStake.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })); 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..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,6 +1,7 @@ import { useSelector } from "@xstate/store/react"; import { Maybe } from "purify-ts"; import { useMemo } from "react"; +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"; @@ -42,11 +43,15 @@ export const UnstakeCompletePage = () => { [exitRequest.unstakeToken] ); - const metadata = integrationData.map((d) => d.metadata); + 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.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/complete/state/index.tsx b/packages/widget/src/pages/complete/state/index.tsx index 8e6a6902..c5455d3b 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: { diff --git a/packages/widget/src/pages/components/meta-info/index.tsx b/packages/widget/src/pages/components/meta-info/index.tsx index 408be515..406da3ca 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; }; 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..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 @@ -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 { getYieldProviderDetails } 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,14 @@ export const ActionListItem = ({ justifyContent="flex-start" alignItems="center" > - + { const providersDetails = useProvidersDetails({ integrationData, - validatorsAddresses: Maybe.of(action.actionData.validatorAddresses ?? []), + validatorsAddresses: Maybe.of( + getActionValidatorAddresses(action.actionData) ?? [] + ), selectedProviderYieldId: Maybe.empty(), }); 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(); 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..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 @@ -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"; @@ -55,9 +54,9 @@ export const ActivityPageContextProvider = ({ ); const path = - data.actionData.type === ActionTypes.UNSTAKE + data.actionData.type === "UNSTAKE" ? "unstake" - : data.actionData.type === ActionTypes.STAKE + : data.actionData.type === "STAKE" ? "stake" : "pending"; 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/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..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,10 +5,13 @@ 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 { 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 +29,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([])); @@ -38,16 +41,25 @@ export const SelectProvider = () => { 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 ? ( ) : ( - Maybe.fromRecord({ - selectedStake, - providerYieldIdOptions, - selectedProviderYield, - }) + providerSelection .map((val) => ( @@ -71,32 +83,15 @@ export const SelectProvider = () => { > - - - } + wrapperProps={{ hw: "5" }} + imgProps={{ borderRadius: "full" }} + src={val.provider.logoURI} + fallbackName={val.provider.name} /> - {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..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 @@ -194,7 +194,10 @@ export const SelectTokenSection = () => { }} data-state={errorBalance ? "error" : "valid"} className={clsx( - combineRecipeWithVariant({ rec: selectTokenBalance, variant }) + combineRecipeWithVariant({ + rec: selectTokenBalance, + variant, + }) )} > {selectedTokenAvailableAmount 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..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 @@ -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"; 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..4f72b879 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 @@ -2,7 +2,10 @@ 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 { isYieldWithProviderOptions } from "../../../../../domain/types/yields"; +import { + isYieldActionArgRequired, + isYieldWithProviderOptions, +} from "../../../../../domain/types/yields"; import { SelectValidatorTrigger } from "./select-validator-trigger"; import { useSelectValidator } from "./use-select-validator"; @@ -27,12 +30,15 @@ export const SelectValidatorSection = () => { ) : ( Maybe.fromRecord({ selectedStake, validatorsData }) - .filter((val) => !!val.selectedStake.validators.length) + .filter((val) => !!val.validatorsData.length) .map((val) => { const selectedValidatorsArr = [...selectedValidators.values()]; - const multiSelect = - !!val.selectedStake.args.enter.args?.validatorAddresses?.required; + const multiSelect = isYieldActionArgRequired( + val.selectedStake, + "enter", + "validatorAddresses" + ); return ( - - - } + wrapperProps={{ hw: "5" }} + imgProps={{ borderRadius: "full" }} + src={sv.logoURI} + fallbackName={nameOrAddress} /> 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(() => ( { 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 031a622a..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,12 +3,13 @@ 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 { 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 +18,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}`, "" @@ -76,15 +82,10 @@ export const SelectYieldRewardDetails = () => { gap="1" > - } + fallbackName={rt.providerName} /> {isMorphoProvider(rt.providerName) && ( @@ -113,6 +114,17 @@ export const SelectYieldRewardDetails = () => { earnYearly={earnYearly} /> )} + + {rewardRateDetails + .map((rewardRate) => ( + + )) + .extractNullable()} ); 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..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,8 +1,12 @@ 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, + getYieldProviderDetails, + isYieldActionArgRequired, +} from "../../../../../domain/types/yields"; import { useEarnPageContext } from "../../state/earn-page-context"; export const StakedVia = () => { @@ -14,12 +18,13 @@ export const StakedVia = () => { .filter( (val) => !!( - val.metadata.type === "staking" && - !val.validators.length && - val.metadata.provider + getBaseYieldType(val) === "staking" && + !isYieldActionArgRequired(val, "enter", "validatorAddress") && + !isYieldActionArgRequired(val, "enter", "validatorAddresses") && + getYieldProviderDetails(val) ) ) - .chainNullable((val) => val.metadata.provider) + .chainNullable((val) => getYieldProviderDetails(val)) .map((val) => ( { } + fallbackName={val.name} /> )) 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 b08348e3..2672b005 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"; @@ -16,6 +10,7 @@ import { useDeferredValue, useEffect, useMemo, + useRef, useState, } from "react"; import { useTranslation } from "react-i18next"; @@ -26,24 +21,33 @@ import { stakeTokenSameAsGasToken, 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, + 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"; 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,8 +96,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const dispatch = useEarnPageDispatch(); const { t } = useTranslation(); - - const baseToken = useBaseToken(selectedStake); + const initParams = useInitParams(); const { externalProviders, variant } = useSettings(); @@ -118,7 +121,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const pointsRewardTokens = useMemo( () => selectedStake - .chainNullable((val) => val.metadata.rewardTokens) + .map(getYieldRewardTokens) .map((val) => val.filter((rt) => rt.isPoints)), [selectedStake] ); @@ -143,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, @@ -155,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( @@ -236,7 +239,9 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { const selectedStakeData = useMemo>( () => Maybe.of(multiYields) - .map((val) => [...val].sort((a, b) => b.rewardRate - a.rewardRate)) + .map((val) => + [...val].sort((a, b) => b.rewardRate.total - a.rewardRate.total) + ) .map((val) => val.filter(isNonZeroRewardRateYield)) .chain((yieldDtos) => Maybe.of(deferredStakeSearch) @@ -250,7 +255,7 @@ 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) @@ -276,7 +281,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { items: [curr], }); } else { - acc.get(extendedYieldType)!.items.push(curr); + acc.get(extendedYieldType)?.items.push(curr); } return acc; @@ -286,7 +291,7 @@ export const EarnPageContextProvider = ({ children }: PropsWithChildren) => { { type: ExtendedYieldType; title: ReturnType["title"]; - items: YieldDto[]; + items: Yield[]; } >() ) @@ -314,30 +319,101 @@ 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) => + !!( + isYieldIntegrationAggregator(stake) || + isYieldActionArgRequired(stake, "enter", "validatorAddress") || + isYieldActionArgRequired(stake, "enter", "validatorAddresses") + ) + ) + .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( - (a, b) => (b.apr ?? 0) - (a.apr ?? 0) + (a, b) => + (b.rewardRate?.total ?? 0) - (a.rewardRate?.total ?? 0) ); } return validators; }) ), - [deferredValidatorSearch, selectedStake, variant] + [deferredValidatorSearch, selectedStake, variant, yieldValidators.data] ); const onYieldSearch: SelectModalProps["onSearch"] = (val) => @@ -363,7 +439,7 @@ 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 }) ); @@ -395,6 +471,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, @@ -430,7 +507,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; @@ -583,7 +660,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..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 @@ -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,8 +12,9 @@ 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 { 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"; @@ -51,12 +51,12 @@ const getInitialState = (): State => ({ selectedStakeId: Maybe.empty(), selectedValidators: new Map(), stakeAmount: new BigNumber(0), + useMaxAmount: false, tronResource: Maybe.empty(), selectedProviderYieldId: Maybe.empty(), }); export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { - const initParams = useInitParams(); const { network, isConnected } = useSKWallet(); const getInitYield = useGetInitYield(); @@ -75,7 +75,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 +95,6 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { ) .map(() => onYieldSelectState({ - initParams: Maybe.fromNullable(initParams.data), yieldDto: action.data, positionsData: positionsData.data, }) @@ -154,6 +152,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { return { ...state, stakeAmount: action.data, + useMaxAmount: false, }; } @@ -161,6 +160,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { return { ...state, stakeAmount: action.data, + useMaxAmount: true, }; } @@ -184,6 +184,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { selectedStakeId, selectedValidators, stakeAmount: _stakeAmount, + useMaxAmount, tronResource, selectedProviderYieldId, } = state; @@ -233,7 +234,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { ); const setYield = useCallback( - (yieldDto: YieldDto) => dispatch({ type: "yield/select", data: yieldDto }), + (yieldDto: Yield) => dispatch({ type: "yield/select", data: yieldDto }), [] ); @@ -344,6 +345,7 @@ export const EarnPageStateProvider = ({ children }: PropsWithChildren) => { selectedStake, selectedValidators, stakeAmount, + useMaxAmount, actions, tronResource, stakeAmountGreaterThanAvailableAmount, @@ -362,6 +364,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..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,18 +12,19 @@ import type { Action } from "../../../../types/utils"; import type { SelectedStakeData } from "../types"; export type State = { - selectedToken: Maybe; + selectedToken: Maybe; selectedStakeId: Maybe< TokenBalanceScanResponseDto["availableYields"][number] >; selectedValidators: Map; 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>; @@ -39,7 +38,7 @@ type SelectTronResourceAction = Action<"tronResource/select", TronResourceType>; type ProviderYieldIdSelectAction = Action< "providerYieldId/select", - YieldDto["id"] + Yield["id"] >; export type Actions = @@ -56,7 +55,7 @@ export type Actions = export type ExtraData = { actions: { onMaxClick: () => void }; - selectedStake: Maybe; + selectedStake: Maybe; stakeAmountLessThanMin: boolean; stakeAmountGreaterThanMax: boolean; stakeAmountGreaterThanAvailableAmount: boolean; @@ -127,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-get-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-get-init-yield.ts index fb594af6..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 @@ -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"; 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..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 @@ -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 = () => 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..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 @@ -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"; @@ -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 b32a301c..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 @@ -1,10 +1,3 @@ -import { - type AddressWithTokenDtoAdditionalAddresses, - type PendingActionDto, - type YieldBalanceDto, - type YieldDto, - yieldGetSingleYieldBalances, -} 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"; @@ -12,13 +5,19 @@ import { PAMultiValidatorsRequired, PASingleValidatorRequired, } from "../../../../domain"; -import type { ValidatorsConfig } from "../../../../domain/types/yields"; +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 { 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,11 +26,10 @@ export const usePendingActionDeepLink = () => { useSKWallet(); const queryClient = useSKQueryClient(); + const yieldApiFetchClient = useYieldApiFetchClient(); const { externalProviders } = useSettings(); - const validatorsConfig = useValidatorsConfig(); - return useQuery({ staleTime: Number.POSITIVE_INFINITY, gcTime: Number.POSITIVE_INFINITY, @@ -49,8 +47,8 @@ export const usePendingActionDeepLink = () => { additionalAddresses, address: addr, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }) ) ).unsafeCoerce(), @@ -62,21 +60,21 @@ const fn = ({ additionalAddresses, address, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }: { isLedgerLive: boolean; address: string; additionalAddresses: AddressWithTokenDtoAdditionalAddresses | null; queryClient: QueryClient; + yieldApiFetchClient: ReturnType; externalProviders: ReturnType["externalProviders"]; - validatorsConfig: ValidatorsConfig; }) => getInitParams({ isLedgerLive, queryClient, + yieldApiFetchClient, externalProviders, - validatorsConfig, }).chain((val) => { const initQueryParams = Maybe.of(val) .filter( @@ -94,19 +92,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 +125,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 +141,7 @@ const fn = ({ return Right({ pendingAction, balance, - balanceId: balance.groupId ?? "default", + balanceId: getPositionBalanceDataKey(balance), }); } } @@ -143,21 +154,21 @@ const fn = ({ isLedgerLive, yieldId: data.yieldId, queryClient, - validatorsConfig, + yieldApiFetchClient, }).map((yieldOp) => ({ ...val, yieldOp })) ) .chain< Error, | { type: "positionDetails"; - yieldOp: YieldDto; - pendingAction: PendingActionDto; + yieldOp: Yield; + pendingAction: YieldPendingActionDto; balance: YieldBalanceDto; balanceId: string; } | { type: "review"; - yieldOp: YieldDto; + yieldOp: Yield; balance: YieldBalanceDto; balanceId: string; pendingActionDto: GetEitherRight< 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..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 @@ -1,17 +1,22 @@ -import type { - ActionRequestDto, - 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, + isYieldIntegrationAggregator, + type Yield, +} from "../../../../domain/types/yields"; import { useSKWallet } from "../../../../providers/sk-wallet"; +import { withAdditionalAddresses } from "../../../../providers/yield-api-client-provider/request-helpers"; import { useEarnPageState } from "./earn-page-state-context"; export const useStakeEnterRequestDto = () => { const { selectedStake, stakeAmount, + useMaxAmount, selectedValidators, tronResource, selectedToken, @@ -26,32 +31,44 @@ export const useStakeEnterRequestDto = () => { selectedStake, selectedToken, }).map<{ - gasFeeToken: YieldDto["token"]; - dto: ActionRequestDto; + addresses: AddressesDto; + gasFeeToken: Yield["token"]; + dto: YieldCreateActionDto; selectedValidators: Map; - selectedStake: YieldDto; + selectedStake: Yield; }>(({ 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()]; - 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, @@ -63,21 +80,26 @@ export const useStakeEnterRequestDto = () => { return { selectedValidators, selectedStake: selectedStake, - gasFeeToken: selectedStake.metadata.gasFeeToken, + gasFeeToken: selectedStake.mechanics.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), + useMaxAmount: useMaxAmount || undefined, + providerId: selectedProviderYieldId.extract(), + ...validatorsOrProvider, + }, + }), }, }; }), @@ -89,6 +111,7 @@ export const useStakeEnterRequestDto = () => { selectedToken, selectedValidators, stakeAmount, + 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..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 @@ -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 = ({ 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..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 @@ -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(); 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..c874a776 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 { getYieldActionArg, type Yield } from "../../../../domain/types/yields"; import type { State } from "./types"; export const onYieldSelectState = ({ yieldDto, positionsData, - initParams, }: { - yieldDto: YieldDto; + yieldDto: Yield; positionsData: PositionsData; - initParams: Maybe; }): Pick< State, | "selectedStakeId" @@ -26,16 +20,13 @@ 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 + 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..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,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 { getYieldProviderDetails } from "../../../../domain/types/yields"; import { usePositionListItem } from "../hooks/use-position-list-item"; import type { usePositions } from "../hooks/use-positions"; import { @@ -57,7 +58,14 @@ export const PositionsListItem = memo( > {item.token.mapOrDefault( (val) => ( - + ), 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..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,8 +1,8 @@ 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"; import { useProvidersDetails } from "../../../../hooks/use-provider-details"; import { defaultFormattedNumber } from "../../../../utils"; @@ -41,7 +41,7 @@ export const usePositionListItem = ( .map((val) => getRewardRateFormatted({ rewardRate: val.rewardRateAverage.toNumber(), - rewardType: val.integrationData.rewardType, + rewardType: getYieldRewardType(val.integrationData), }) ), [integrationData, providersDetails] @@ -59,19 +59,21 @@ export const usePositionListItem = ( const tokenToDisplay = item.token; const baseToken = useMemo( - () => integrationData.map((y) => getBaseToken(y)), + () => integrationData.map((y) => y.token), [integrationData] ); - const totalAmount = useMemo( + const amounts = useMemo( () => - tokenToDisplay.map((val) => - getPositionTotalAmount({ - token: val, - balances: item.balancesWithAmount, - }) - ), - [item.balancesWithAmount, tokenToDisplay] + 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( @@ -85,6 +87,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..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 @@ -1,17 +1,16 @@ -import type { - YieldBalanceDto, - YieldBalanceLabelDto, - YieldBalancesWithIntegrationIdDto, -} 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"; +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"; 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 +71,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 +92,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 +104,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,19 +131,11 @@ 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 = ( - params?: YieldFindValidatorsParams -) => { - return ["/v1/yields/validators", ...(params ? [params] : [])] as const; }; 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..b311bf66 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,6 +12,9 @@ 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 { defaultFormattedNumber, formatNumber } from "../../../utils"; import { priceTxt } from "../styles.css"; @@ -27,12 +29,12 @@ 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; - yieldDto: YieldDto; + unstakeToken: TokenDto | YieldTokenDto; + yieldDto: Yield; validators: { [Key in keyof Pick< ValidatorDto, @@ -217,11 +219,11 @@ const UnstakeInfo = ({ yieldDto, unstakeToken, }: { - yieldDto: YieldDto; + yieldDto: Yield; 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..bc41ca5f 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 { YieldBalanceDto, YieldDto } from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { isPast } from "date-fns"; import { useMemo } from "react"; @@ -6,6 +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 "../../../domain/types/positions"; +import { getBaseYieldType, type Yield } from "../../../domain/types/yields"; import { defaultFormattedNumber } from "../../../utils"; import { formatDurationUntilDate } from "../../../utils/date"; @@ -14,16 +15,14 @@ export const PositionBalances = ({ integrationData, }: { yieldBalance: YieldBalanceDto & { tokenPriceInUsd: BigNumber }; - integrationData: YieldDto; + integrationData: Yield; }) => { const { t } = useTranslation(); const durationUntilDate = useMemo(() => { if ( !yieldBalance.date || - (yieldBalance.type !== "unstaking" && - yieldBalance.type !== "unlocking" && - yieldBalance.type !== "preparing") + (yieldBalance.type !== "entering" && yieldBalance.type !== "exiting") ) { return null; } @@ -43,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 = getBaseYieldType(integrationData); 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 8cbdaa93..cfe1d7fb 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"; @@ -11,9 +10,10 @@ 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"; +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"; @@ -24,16 +24,19 @@ export const ProviderDetails = ({ integrationData, logo, ...providerDetails -}: { +}: Omit< + GetMaybeJust>[0], + "rewardType" +> & { isFirst: boolean; stakeType: string; - integrationData: YieldDto; + integrationData: Yield; logo: string | undefined; name: string; rewardRateFormatted: string; rewardRate: number | undefined; - rewardType: RewardTypes; -} & GetMaybeJust>[0]) => { + rewardType?: RewardTypes; +}) => { const { t } = useTranslation(); const nameOrAddress = providerDetails.name ?? providerDetails ?? ""; @@ -52,18 +55,10 @@ export const ProviderDetails = ({ - - - } + fallbackName={nameOrAddress} /> 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..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 @@ -1,27 +1,26 @@ -import type { - ActionTypes, - PendingActionDto, - YieldBalanceDto, - 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, + YieldPendingActionType, +} 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; }; onPendingActionClick: ReturnType< typeof usePositionDetails >["onPendingActionClick"]; - yieldId: YieldDto["id"]; + yieldId: Yield["id"]; }; export const StaticActionBlock = ({ @@ -53,7 +52,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) @@ -91,7 +90,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-pending-actions.ts b/packages/widget/src/pages/position-details/hooks/use-pending-actions.ts index 820a3802..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 @@ -1,9 +1,3 @@ -import type { - PendingActionDto, - ValidatorDto, - YieldBalanceDto, - 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 +7,18 @@ import { PAMultiValidatorsRequired, 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"; 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, @@ -39,7 +39,7 @@ export const usePendingActions = () => { positionBalancePrices, } = useUnstakeOrPendingActionState(); - const baseToken = useBaseToken(integrationData); + const baseToken = integrationData.map((val) => val.token); const pendingActionDispatch = 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", { @@ -234,8 +234,8 @@ export const usePendingActions = () => { yieldBalance, selectedValidators, }: { - integrationData: YieldDto; - pendingActionDto: PendingActionDto; + 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 57c40ca6..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 @@ -1,13 +1,14 @@ -import type { TokenDto } from "@stakekit/api-hooks"; import { useMutation } from "@tanstack/react-query"; 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 { 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"; @@ -43,7 +44,7 @@ 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] @@ -52,7 +53,7 @@ export const usePositionDetails = () => { 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)), @@ -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, @@ -90,19 +92,20 @@ 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(); - const baseToken = useBaseToken(integrationData); + const baseToken = integrationData.map((val) => val.token); + + 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, @@ -110,36 +113,34 @@ export const usePositionDetails = () => { return b.type === "validators" ? b.validatorsAddresses : []; }), selectedProviderYieldId: Maybe.empty(), + validatorsData: Maybe.fromNullable(yieldValidators.data), }); - const canUnstake = integrationData.filter((d) => !!d.args.exit).isJust(); + const personalizedRewardRate = useMemo( + () => + positionBalances.data + .map((balanceData) => balanceData.rewardRate) + .filter( + (rewardRate) => + !!getRewardRateBreakdown(rewardRate).find( + (item) => item.key === "campaign" + ) + ) + .extractNullable(), + [positionBalances.data] + ); + + const canUnstake = integrationData.filter((d) => !!d.status.exit).isJust(); const onUnstakeAmountChange = (value: BigNumber) => dispatch({ type: "unstake/amount/change", data: value }); 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 = () => { @@ -163,7 +164,7 @@ export const usePositionDetails = () => { validatorAddressesHandling, } = usePendingActions(); - const liquidTokensToNativeConversion = useMemo( + const shareToAmountConversions = useMemo( () => Maybe.fromRecord({ integrationData, @@ -172,18 +173,15 @@ export const usePositionDetails = () => { }).map((v) => [...v.positionBalancesByType.values()].reduce((acc, curr) => { curr - .filter( - (yb) => - !yb.token.isPoints && - yb.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.pricePerShare) - )} ${v.baseToken.symbol}` + new BigNumber(yb.shareAmount ?? 0).dividedBy( + new BigNumber(yb.amount ?? 0) + ) + )} ${yb.shareToken?.symbol}` ); }); @@ -198,10 +196,12 @@ export const usePositionDetails = () => { const isLoading = positionBalances.isLoading || positionBalancePrices.isLoading || - yieldOpportunity.isLoading; + yieldOpportunity.isLoading || + yieldValidators.isLoading; return { integrationData, + validatorsData: yieldValidators.data ?? [], reducedStakedOrLiquidBalance, positionBalancesByType, canUnstake, @@ -215,13 +215,13 @@ export const usePositionDetails = () => { isLoading, onPendingActionClick, providersDetails, + personalizedRewardRate, pendingActions, - liquidTokensToNativeConversion, + shareToAmountConversions, validatorAddressesHandling, 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..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 @@ -1,13 +1,24 @@ -import type { ActionRequestDto, 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, + isYieldIntegrationAggregator, + type Yield, +} from "../../../domain/types/yields"; import { useSKWallet } from "../../../providers/sk-wallet"; +import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; import { useUnstakeOrPendingActionState } from "../state"; export const useStakeExitRequestDto = () => { const { address, additionalAddresses } = useSKWallet(); - const { unstakeAmount, integrationData, stakedOrLiquidBalances } = - useUnstakeOrPendingActionState(); + const { + unstakeAmount, + unstakeUseMaxAmount, + integrationData, + stakedOrLiquidBalances, + } = useUnstakeOrPendingActionState(); return useMemo( () => @@ -16,52 +27,64 @@ export const useStakeExitRequestDto = () => { integrationData, stakedOrLiquidBalances, }).map<{ - gasFeeToken: YieldDto["token"]; - dto: ActionRequestDto; + addresses: AddressesDto; + gasFeeToken: Yield["token"]; + 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) { + if (isYieldIntegrationAggregator(val.integrationData)) { 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 + getYieldActionArg( + val.integrationData, + "exit", + "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 + getYieldActionArg(val.integrationData, "exit", "validatorAddress") + ?.required ) { return List.find( - (b) => !!b.validatorAddress, + (b) => !!b.validator?.address, val.stakedOrLiquidBalances ).map((b) => { const subnetId = Maybe.fromNullable( - val.integrationData.args.exit?.args?.subnetId?.required + getYieldActionArg(val.integrationData, "exit", "subnetId") + ?.required ) - .chainNullable(() => - val.integrationData.validators.find( - (v) => v.address === b.validatorAddress - ) - ) + .chainNullable(() => b.validator) .map((validator) => validator.subnetId) .extract(); return { - validatorAddress: b.validatorAddress, + validatorAddress: b.validator?.address, subnetId, }; }); @@ -72,17 +95,22 @@ export const useStakeExitRequestDto = () => { .orDefault({}); return { - gasFeeToken: val.integrationData.metadata.gasFeeToken, + gasFeeToken: val.integrationData.mechanics.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), + useMaxAmount: unstakeUseMaxAmount || undefined, + ...validatorsOrProvider, + }, + }), }, }; }), @@ -92,6 +120,7 @@ export const useStakeExitRequestDto = () => { stakedOrLiquidBalances, 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 82b76cb8..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 @@ -1,20 +1,20 @@ -import type { TransactionVerificationMessageDto } from "@stakekit/api-hooks"; -import { - actionExit, - 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"; +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"; 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 +26,30 @@ 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,13 +136,10 @@ const getMachine = ( Just: (val) => { ref.current.trackEvent("unstakeClicked", { yieldId: val.integrationData.id, - amount: val.requestDto.args.amount, + amount: val.requestDto.arguments?.amount, }); - if ( - val.integrationData.args.exit?.args?.signatureVerification - ?.required - ) { + if (hasYieldExitSignatureVerification(val.integrationData)) { self.send({ type: "__GET_VERIFICATION_MESSAGE__", val }); } else { self.send({ type: "__SUBMIT__", val }); @@ -172,9 +179,9 @@ const getMachine = ( val.network, { addresses: { - address: val.address, + address: val.addresses.address, additionalAddresses: - val.additionalAddresses ?? undefined, + val.addresses.additionalAddresses ?? undefined, }, } ) @@ -270,40 +277,53 @@ 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({ + fetchClient: ref.current.yieldApiFetchClient, + requestDto: val.requestDto, + }) + ) .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/use-validator-addresses-handling.ts b/packages/widget/src/pages/position-details/hooks/use-validator-addresses-handling.ts index e24391c2..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 @@ -1,10 +1,11 @@ -import type { - PendingActionDto, - ValidatorDto, - YieldBalanceDto, -} 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, +} 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..10aa7caa 100644 --- a/packages/widget/src/pages/position-details/hooks/utils.ts +++ b/packages/widget/src/pages/position-details/hooks/utils.ts @@ -1,16 +1,30 @@ -import type { - PendingActionDto, - PendingActionRequestDto, - ValidatorDto, - YieldBalanceDto, - 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 type { Yield } from "../../../domain/types/yields"; +import { withAdditionalAddresses } from "../../../providers/yield-api-client-provider/request-helpers"; +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: YieldBalanceDto["type"]; +}; + export const preparePendingActionRequestDto = ({ pendingActionsState, additionalAddresses, @@ -23,16 +37,16 @@ export const preparePendingActionRequestDto = ({ pendingActionsState: State["pendingActions"]; address: SKWallet["address"]; additionalAddresses: SKWallet["additionalAddresses"]; - pendingActionDto: PendingActionDto; - yieldBalance: YieldBalanceDto; - integration: YieldDto; + pendingActionDto: AnyPendingActionDto; + yieldBalance: AnyYieldBalanceDto; + integration: Yield; selectedValidators: ValidatorDto["address"][]; }): Either< Error, { - requestDto: PendingActionRequestDto; - integrationData: YieldDto; - gasFeeToken: YieldDto["token"]; + requestDto: YieldCreateManageActionDto; + integrationData: Yield; + gasFeeToken: Yield["token"]; address: NonNullable; additionalAddresses: | NonNullable @@ -42,17 +56,17 @@ export const preparePendingActionRequestDto = ({ Maybe.fromNullable(address) .toEither(new Error("missing address")) .map((val) => { - const args: PendingActionRequestDto["args"] = { + const args: NonNullable = { 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 YieldPendingActionType, }) ) ) @@ -62,23 +76,27 @@ 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(""); } } return { requestDto: { - args, - integrationId: integration.id, + action: pendingActionDto.type as YieldPendingActionType, + address: val, + arguments: withAdditionalAddresses({ + additionalAddresses, + argumentsDto: args, + }), passthrough: pendingActionDto.passthrough, - type: pendingActionDto.type, + yieldId: integration.id, }, address: val, additionalAddresses: additionalAddresses ?? undefined, - gasFeeToken: integration.metadata.gasFeeToken, + 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 c45311b8..04b39cb5 100644 --- a/packages/widget/src/pages/position-details/position-details.page.tsx +++ b/packages/widget/src/pages/position-details/position-details.page.tsx @@ -1,16 +1,22 @@ -import type { ActionTypes } from "@stakekit/api-hooks"; 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"; 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 { + 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"; +import { getRewardRateFormatted } from "../../utils/formatters"; import { PageContainer } from "../components/page-container"; import { AmountBlock } from "./components/amount-block"; import { PositionBalances } from "./components/position-balances"; @@ -24,6 +30,7 @@ const PositionDetails = () => { const { onPendingActionAmountChange, integrationData, + validatorsData, isLoading, reducedStakedOrLiquidBalance, positionBalancesByType, @@ -37,16 +44,16 @@ const PositionDetails = () => { onPendingActionClick, pendingActions, providersDetails, - liquidTokensToNativeConversion, + shareToAmountConversions, validatorAddressesHandling, onValidatorsSubmit, unstakeToken, canUnstake, unstakeAmountError, - positionLabel, unstakeMaxAmount, unstakeMinAmount, unstakeIsGreaterOrLessIntegrationLimitError, + personalizedRewardRate, } = usePositionDetails(); useTrackPage("positionDetails", { @@ -87,7 +94,13 @@ const PositionDetails = () => { alignItems="center" > @@ -108,41 +121,38 @@ const PositionDetails = () => { )) .extractNullable()} - {positionLabel - .map((l) => ( + {personalizedRewardRate ? ( + - - - - + + {t("position_details.personalized_apy")} + - - { - t( - `position_details.labels.${l.type}.details`, - l.params - ) as string - } - - + + {getRewardRateFormatted({ + rewardRate: personalizedRewardRate.total, + rewardType: getRewardTypeFromRateType( + personalizedRewardRate.rateType + ), + })} + - )) - .extractNullable()} + + + + ) : null} {providersDetails @@ -152,8 +162,14 @@ 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}` + `position_details.stake_type.${getBaseYieldType(integrationData)}` )} integrationData={integrationData} /> @@ -174,7 +190,7 @@ const PositionDetails = () => { )) )} - {liquidTokensToNativeConversion + {shareToAmountConversions .map((val) => ( { } label={t( `position_details.pending_action_button.${ - val.pendingActionDto.type.toLowerCase() as Lowercase + val.pendingActionDto.type.toLowerCase() as Lowercase }` )} onMaxClick={null} @@ -276,7 +292,7 @@ const PositionDetails = () => { unstakeAmountError={unstakeAmountError} onMaxClick={onMaxClick} label={t( - `position_details.unstake_label.${integrationData.metadata.type}` + `position_details.unstake_label.${getBaseYieldType(integrationData)}` )} formattedAmount={unstakeFormattedAmount} balance={reducedStakedOrLiquidBalance} @@ -300,7 +316,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/position-details/state/index.tsx b/packages/widget/src/pages/position-details/state/index.tsx index 45e59edc..cefcbc02 100644 --- a/packages/widget/src/pages/position-details/state/index.tsx +++ b/packages/widget/src/pages/position-details/state/index.tsx @@ -1,10 +1,3 @@ -import type { - ActionTypes, - PendingActionDto, - PriceRequestDto, - TokenDto, - YieldBalanceDto, -} from "@stakekit/api-hooks"; import BigNumber from "bignumber.js"; import { List, Maybe } from "purify-ts"; import type { Dispatch, PropsWithChildren } from "react"; @@ -17,16 +10,21 @@ import { useRef, } from "react"; import { config } from "../../../config"; -import { isForceMaxAmount } from "../../../domain/types/stake"; -import { isERC4626 } from "../../../domain/types/yields"; +import { getPendingActionAmountConfig } from "../../../domain/types/pending-action"; +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"; -import { useBaseToken } from "../../../hooks/use-base-token"; 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, + YieldPendingActionType, + YieldTokenDto, +} from "../../../providers/yield-api-client-provider/types"; import type { Actions, BalanceTokenActionType, @@ -58,7 +56,7 @@ export const UnstakeOrPendingActionProvider = ({ [yieldOpportunity.data] ); - const baseToken = useBaseToken(integrationData); + const baseToken = integrationData.map((val) => val.token); const positionBalancesRemote = usePositionBalances({ balanceId, @@ -89,7 +87,7 @@ export const UnstakeOrPendingActionProvider = ({ positionBalances: positionBalances.data, baseToken, }) - .map((val) => ({ + .map((val) => ({ currency: config.currency, tokenList: [ val.baseToken, @@ -105,9 +103,7 @@ export const UnstakeOrPendingActionProvider = ({ * @summary Position balance by type */ const positionBalancesByType = usePositionBalanceByType({ - baseToken, positionBalancesData: positionBalances.data, - prices: positionBalancePrices, }); const stakedOrLiquidBalances = useStakedOrLiquidBalance( @@ -120,15 +116,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 +137,7 @@ export const UnstakeOrPendingActionProvider = ({ () => stakedOrLiquidBalances .chain((balances) => List.head(balances)) - .map((v) => ({ - ...v.token, - pricePerShare: v.pricePerShare, - })), + .map((v) => v.token), [stakedOrLiquidBalances] ); @@ -155,19 +150,22 @@ 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( (d) => - !!(!isForceMax && (d.args.exit?.args?.amount?.required || isERC4626(d))) + !!( + !isForceMax && + (getYieldActionArg(d, "exit", "amount")?.required || isERC4626(d)) + ) ); const positionBalancesByTypePendingActions = useMemo( () => new Map< BalanceTokenActionType, - { pendingAction: PendingActionDto; balance: YieldBalanceDto } + { pendingAction: YieldPendingActionDto; balance: YieldBalanceDto } >( positionBalancesByType .map((pbbt) => @@ -201,8 +199,8 @@ export const UnstakeOrPendingActionProvider = ({ }: { state: State["pendingActions"]; balanceType: YieldBalanceDto["type"]; - token: TokenDto; - actionType: ActionTypes; + token: YieldTokenDto; + actionType: YieldPendingActionType; amount: BigNumber; }) => { const key = getBalanceTokenActionType({ actionType, balanceType, token }); @@ -213,20 +211,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); @@ -244,6 +235,7 @@ export const UnstakeOrPendingActionProvider = ({ return { ...state, unstakeAmount: action.data, + unstakeUseMaxAmount: false, }; } @@ -251,6 +243,7 @@ export const UnstakeOrPendingActionProvider = ({ return { ...state, unstakeAmount: maxEnterOrExitAmount, + unstakeUseMaxAmount: true, }; } @@ -271,10 +264,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( () => @@ -346,6 +344,7 @@ export const UnstakeOrPendingActionProvider = ({ unstakeAmountError, unstakeToken, unstakeAmount, + unstakeUseMaxAmount, pendingActions, positionBalancePrices, reducedStakedOrLiquidBalance, @@ -364,6 +363,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 60c4957c..0fcbec22 100644 --- a/packages/widget/src/pages/position-details/state/types.ts +++ b/packages/widget/src/pages/position-details/state/types.ts @@ -1,14 +1,17 @@ -import type { - ActionTypes, - TokenDto, - YieldBalanceDto, - 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"; @@ -19,14 +22,14 @@ type UnstakeAmountChange = Action<"unstake/amount/change", BigNumber>; type UnstakeAmountMax = Action<"unstake/amount/max">; export type BalanceTokenActionType = - `${YieldBalanceDto["type"]}-${TokenString}-${ActionTypes}`; + `${YieldBalanceType}-${TokenString}-${YieldPendingActionType}`; export type PendingActionAmountChange = Action< "pendingAction/amount/change", { - balanceType: YieldBalanceDto["type"]; - token: TokenDto; - actionType: ActionTypes; + balanceType: YieldBalanceType; + token: TokenDto | YieldTokenDto; + actionType: YieldPendingActionType; amount: BigNumber; } >; @@ -38,24 +41,25 @@ export type Actions = export type State = { unstakeAmount: BigNumber; + unstakeUseMaxAmount: boolean; pendingActions: Map; }; export type ExtraData = { - pendingActionType: Maybe; - integrationData: Maybe; + pendingActionType: Maybe; + integrationData: Maybe; positionBalances: ReturnType; yieldOpportunity: ReturnType; positionBalancesByType: Maybe; 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..91208f70 100644 --- a/packages/widget/src/pages/position-details/state/utils.ts +++ b/packages/widget/src/pages/position-details/state/utils.ts @@ -1,9 +1,10 @@ -import type { - ActionTypes, - TokenDto, - YieldBalanceDto, -} from "@stakekit/api-hooks"; import { tokenString } from "../../../domain"; +import type { TokenDto } from "../../../domain/types/tokens"; +import type { + YieldBalanceType, + YieldPendingActionType, + YieldTokenDto, +} from "../../../providers/yield-api-client-provider/types"; import type { BalanceTokenActionType } from "./types"; export const getBalanceTokenActionType = ({ @@ -11,8 +12,8 @@ export const getBalanceTokenActionType = ({ balanceType, token, }: { - balanceType: YieldBalanceDto["type"]; - token: TokenDto; - actionType: ActionTypes; + balanceType: YieldBalanceType; + token: TokenDto | YieldTokenDto; + actionType: YieldPendingActionType; }): BalanceTokenActionType => `${balanceType}-${tokenString(token)}-${actionType}`; 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..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 @@ -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 { 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"; @@ -29,21 +32,29 @@ export const useActionReview = () => { (state) => state.context.selectedAction ).unsafeCoerce(); - const inputToken = useMemo( - () => Maybe.of(selectedAction.inputToken), - [selectedAction] - ) as Maybe; - const selectedYield = useSelector( activityContext, (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)), + .map((tx) => + tx.sort((a, b) => (a.stepIndex ?? 0) - (b.stepIndex ?? 0)) + ), [selectedAction] ); @@ -58,7 +69,7 @@ export const useActionReview = () => { ); const unstakeTitle = useMemo(() => { - switch (selectedYield.metadata.type) { + switch (getBaseYieldType(selectedYield)) { case "staking": case "liquid-staking": return t("position_details.unstake") as string; @@ -72,9 +83,9 @@ export const useActionReview = () => { () => t( `position_details.pending_action_button.${ - selectedAction.type.toLowerCase() as Lowercase - }` as const - ), + selectedAction.type.toLowerCase() as Lowercase + }` as never + ) as string, [selectedAction.type, t] ); @@ -114,7 +125,7 @@ export const useActionReview = () => { (tx) => tx.status === TransactionStatus.WAITING_FOR_SIGNATURE, txs ).chain((tx) => - List.findIndex((i) => i === 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) diff --git a/packages/widget/src/pages/review/hooks/use-fees.ts b/packages/widget/src/pages/review/hooks/use-fees.ts index 73d1a94b..d8b80b98 100644 --- a/packages/widget/src/pages/review/hooks/use-fees.ts +++ b/packages/widget/src/pages/review/hooks/use-fees.ts @@ -1,9 +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 { TokenDto, YieldTokenDto } from "../../../domain/types/tokens"; import { bpsToAmount, bpsToPercentage } from "../../../utils"; import { getFeesInUSD } from "../../../utils/formatters"; import type { FeesBps } from "../types"; @@ -11,13 +12,19 @@ import type { FeesBps } from "../types"; export const useFees = ({ amount, feeConfigDto, + yieldFee, prices, token, }: { prices: Maybe; - token: Maybe; + token: Maybe; amount: BigNumber; feeConfigDto: Maybe; + yieldFee?: { + deposit?: string; + management?: string; + performance?: string; + } | null; }): { depositFee: Maybe; managementFee: Maybe; @@ -40,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 @@ -49,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( @@ -62,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( @@ -75,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..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 @@ -1,23 +1,22 @@ -import { - type ActionTypes, - actionPending, - useActionPendingGasEstimate, -} from "@stakekit/api-hooks"; -import { useMutation } from "@tanstack/react-query"; +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 { 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"; 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 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"; @@ -25,26 +24,42 @@ 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({ + fetchClient: yieldApiFetchClient, + requestDto: pendingRequest.requestDto, + }), + }); const pendingTxGas = useMemo( () => - Maybe.fromNullable(actionPendingGasEstimate.data?.amount).map(BigNumber), - [actionPendingGasEstimate.data] + Maybe.fromNullable(actionPreviewQuery.data) + .map((actionDto) => + 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] ); 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" }); @@ -116,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, @@ -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..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 @@ -1,17 +1,15 @@ -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 { getTransactionGasEstimate } from "../../../domain/types/action"; +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"; @@ -20,6 +18,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 +34,44 @@ 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({ + fetchClient: yieldApiFetchClient, + requestDto: enterRequest.requestDto, + }), + }); const stakeEnterTxGas = useMemo( () => - Maybe.fromNullable(actionEnterGasEstimation.data?.amount).map(BigNumber), - [actionEnterGasEstimation.data] + Maybe.fromNullable(actionPreviewQuery.data) + .map((actionDto) => + 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] ); 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 +87,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 +127,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), @@ -123,30 +149,19 @@ export const useStakeReview = () => { ), }); - const metadata = selectedStake.map((y) => y.metadata); + const metadata = selectedStake.map((yieldDto) => ({ + logoURI: yieldDto.metadata.logoURI, + name: yieldDto.metadata.name, + provider: getYieldProviderDetails(yieldDto) ?? undefined, + })); const navigate = useNavigate(); 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"); @@ -191,9 +206,9 @@ export const useStakeReview = () => { const commissionFee = useMemo( () => selectedStake - .chainNullable((y) => y.metadata.commission) + .chainNullable(getYieldCommission) .map((commission) => - commission.reduce((acc, curr) => acc + curr.value, 0) + commission.reduce((acc, curr) => acc + curr.value, 0) ) .map((val) => `${APToPercentage(val)}%`), [selectedStake] @@ -210,23 +225,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..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 @@ -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"; @@ -7,11 +7,18 @@ 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 { + getBaseYieldType, + 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"; 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 +31,32 @@ 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({ + fetchClient: yieldApiFetchClient, + requestDto: exitRequest.requestDto, + }), + }); const stakeExitTxGas = useMemo( - () => Maybe.fromNullable(actionExitGasEstimate.data?.amount).map(BigNumber), - [actionExitGasEstimate.data] + () => + Maybe.fromNullable(actionPreviewQuery.data) + .map((actionDto) => + 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] ); const interactedToken = useMemo( @@ -50,15 +75,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, }); @@ -67,7 +92,7 @@ export const useUnstakeActionReview = () => { const formattedAmount = useMemo(() => formatNumber(amount), [amount]); const title: Maybe = integrationData.map((d) => { - switch (d.metadata.type) { + switch (getBaseYieldType(d)) { case "staking": case "liquid-staking": return t("position_details.unstake") as string; @@ -90,9 +115,11 @@ export const useUnstakeActionReview = () => { ); 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, @@ -156,7 +183,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/review/pages/action-review.page.tsx b/packages/widget/src/pages/review/pages/action-review.page.tsx index 5dbf07fc..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,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 { 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"; @@ -40,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 0e62d7c8..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 @@ -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,6 +9,7 @@ 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 { useTrackEvent } from "../../../../hooks/tracking/use-track-event"; import { AnimationPage } from "../../../../navigation/containers/animation-page"; import { MetaInfo } from "../../../components/meta-info"; @@ -25,8 +25,8 @@ export type MetaInfoProps = type ReviewPageProps = { fee: string; title: string; - token: Maybe; - metadata: Maybe; + token: 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 fdfad48b..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 @@ -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,12 +7,16 @@ 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 { + TokenDto, + YieldTokenDto, +} from "../../../../../domain/types/tokens"; import { headingStyles } from "../../style.css"; type Props = { title: string; - token: Maybe; - metadata: Maybe; + token: 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 8cd53ff0..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,5 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; +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"; @@ -45,7 +46,11 @@ export const PendingReviewPage = () => { performanceFee={performanceFee} feeConfigLoading={feeConfigLoading} info={info} - metadata={integrationData.map((val) => val.metadata)} + 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 db55b463..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,5 +1,6 @@ import { Maybe } from "purify-ts"; import { useMemo } from "react"; +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"; @@ -50,7 +51,11 @@ export const UnstakeReviewPage = () => { performanceFee={performanceFee} feeConfigLoading={feeConfigLoading} info={info} - metadata={integrationData.map((d) => d.metadata)} + 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/steps/hooks/use-steps-machine.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts index f9fc8019..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 @@ -1,38 +1,28 @@ -import type { - ActionDto, - TransactionDto, - TransactionFormat, -} from "@stakekit/api-hooks"; -import { - transactionConstruct, - transactionGetTransaction, - transactionGetTransactionStatusFromId, - transactionSubmit, - transactionSubmitHash, -} 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 { 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"; -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; }; @@ -66,31 +52,31 @@ type SignRes = export const useStepsMachine = ({ transactions, - integrationId, + yieldId, actionMeta, }: { transactions: ActionDto["transactions"]; - integrationId: ActionDto["integrationId"]; + yieldId: ActionDto["yieldId"]; 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 ?? 0) - (b.stepIndex ?? 0)), [transactions] ); const machineParams = useSavedRef({ transactions: sortedTransactions, - integrationId, - isLedgerLive, + yieldId, trackEvent, signMessage, signTransaction, actionMeta, - preferredTransactionFormat, + yieldApiFetchClient, }); return useMachine(useState(() => getMachine(machineParams))[0]); @@ -100,26 +86,18 @@ const getMachine = ( ref: Readonly< RefObject<{ transactions: ActionDto["transactions"]; - integrationId: ActionDto["integrationId"]; - isLedgerLive: boolean; + yieldId: ActionDto["yieldId"]; 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 + ref.current.yieldId ); return setup({ @@ -139,11 +117,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 +211,82 @@ 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) { + const unsignedMessage = + typeof tx.unsignedTransaction === "string" + ? tx.unsignedTransaction + : JSON.stringify(tx.unsignedTransaction); + + return ref.current + .signMessage(unsignedMessage) + .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 as Parameters< + typeof ref.current.signTransaction + >[0]["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 +363,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 +381,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 +456,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 +508,7 @@ const getMachine = ( ...val.meta, signError: null, txCheckError: null, - url: v.url, + url: v.url ?? null, done: true, }, } @@ -618,7 +577,7 @@ const getMachine = ( const getInitContext = ( transactions: ActionDto["transactions"], - integrationId: ActionDto["integrationId"] + yieldId: ActionDto["yieldId"] ) => { if (!transactions.length) { return { @@ -632,16 +591,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, + }; + } const currentTxMeta = { idx: currentTxIdx, @@ -652,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..ae4ad2ba 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) => ({ @@ -45,12 +48,12 @@ export const useSteps = ({ ) .orDefault([]), }), - [session, providersDetails] + [session, providersDetails, inputToken] ); const [machineState, send, actorRef] = useStepsMachine({ transactions: session.transactions, - integrationId: session.integrationId, + yieldId: session.yieldId, actionMeta, }); 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..1b41c0bd 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"; @@ -24,13 +28,20 @@ export const ActivityStepsPage = () => { 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..ae34ece4 100644 --- a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx @@ -36,6 +36,7 @@ export const PendingStepsPage = () => { 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..27a0daa6 100644 --- a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx @@ -37,6 +37,7 @@ export const StakeStepsPage = () => { 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..56baef36 100644 --- a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx +++ b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx @@ -35,6 +35,7 @@ export const UnstakeStepsPage = () => { return ( diff --git a/packages/widget/src/providers/activity-provider/index.tsx b/packages/widget/src/providers/activity-provider/index.tsx index 23eaa620..4d7a167b 100644 --- a/packages/widget/src/providers/activity-provider/index.tsx +++ b/packages/widget/src/providers/activity-provider/index.tsx @@ -1,18 +1,19 @@ -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), 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/enter-stake-store/index.tsx b/packages/widget/src/providers/enter-stake-store/index.tsx index f1da4d09..2149e647 100644 --- a/packages/widget/src/providers/enter-stake-store/index.tsx +++ b/packages/widget/src/providers/enter-stake-store/index.tsx @@ -1,18 +1,20 @@ -import type { - ActionDto, - ActionRequestDto, - 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 { + 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: ActionRequestDto; - gasFeeToken: YieldDto["token"]; - selectedStake: YieldDto; + requestDto: YieldCreateActionDto; + addresses: AddressesDto; + gasFeeToken: Yield["token"]; + selectedStake: Yield; selectedValidators: Map; selectedToken: TokenDto; }; 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/exit-stake-store/index.tsx b/packages/widget/src/providers/exit-stake-store/index.tsx index 5542f1e9..3083df69 100644 --- a/packages/widget/src/providers/exit-stake-store/index.tsx +++ b/packages/widget/src/providers/exit-stake-store/index.tsx @@ -1,20 +1,22 @@ -import type { - ActionDto, - ActionRequestDto, - 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, +} 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: ActionRequestDto; - gasFeeToken: YieldDto["token"]; + requestDto: YieldCreateActionDto; + addresses: AddressesDto; + gasFeeToken: Yield["token"]; unstakeAmount: BigNumber; - integrationData: YieldDto; - unstakeToken: TokenDto; + integrationData: Yield; + 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/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() diff --git a/packages/widget/src/providers/pending-action-store/index.tsx b/packages/widget/src/providers/pending-action-store/index.tsx index 2350baff..6a612f39 100644 --- a/packages/widget/src/providers/pending-action-store/index.tsx +++ b/packages/widget/src/providers/pending-action-store/index.tsx @@ -1,21 +1,21 @@ -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 { + ActionDto, + YieldCreateManageActionDto, +} 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: PendingActionRequestDto; + requestDto: YieldCreateManageActionDto; addresses: AddressesDto; - pendingActionType: ActionTypes; - integrationData: YieldDto; - interactedToken: TokenDto; + pendingActionType: YieldPendingActionType; + integrationData: Yield; + 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..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"; @@ -29,6 +30,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..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, @@ -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/sk-wallet/use-additional-addresses.ts b/packages/widget/src/providers/sk-wallet/use-additional-addresses.ts index 1fd7590d..10d88740 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"; 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 d8edb078..381ade10 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[]; @@ -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,12 +114,13 @@ const buildWagmiConfig = async (opts: { getSubstrateConfig({ queryClient: opts.queryClient, forceWalletConnectOnly: opts.forceWalletConnectOnly, + yieldApiFetchClient: opts.yieldApiFetchClient, }), 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 +384,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 +417,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..bdccc1e3 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/actions.ts @@ -0,0 +1,137 @@ +import type { + ActionDto, + 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"; + +export const createEnterAction = async ({ + fetchClient, + requestDto, +}: { + fetchClient: YieldApiFetchClient; + requestDto: YieldCreateActionDto; +}): Promise => { + return getResponseData( + fetchClient.POST("/v1/actions/enter", { + body: requestDto, + }) + ); +}; + +export const createExitAction = async ({ + fetchClient, + requestDto, +}: { + fetchClient: YieldApiFetchClient; + requestDto: YieldCreateActionDto; +}): Promise => { + return getResponseData( + fetchClient.POST("/v1/actions/exit", { + body: requestDto, + }) + ); +}; + +export const createManageAction = async ({ + fetchClient, + requestDto, +}: { + fetchClient: YieldApiFetchClient; + requestDto: YieldCreateManageActionDto; +}): Promise => { + return getResponseData( + fetchClient.POST("/v1/actions/manage", { + body: requestDto, + }) + ); +}; + +export const listActions = async ({ + address, + fetchClient, + limit, + offset, + network, +}: { + address: string; + fetchClient: YieldApiFetchClient; + limit: number; + offset: number; + network: SupportedSKChains; +}) => + getResponseData( + fetchClient.GET("/v1/actions", { + params: { + query: { + address, + offset, + limit, + network, + }, + }, + }) + ); + +export const getTransaction = async ({ + fetchClient, + transactionId, +}: { + fetchClient: YieldApiFetchClient; + transactionId: string; +}) => + getResponseData( + fetchClient.GET("/v1/transactions/{transactionId}", { + params: { + path: { + transactionId, + }, + }, + }) + ); + +export const submitTransaction = async ({ + fetchClient, + signedTransaction, + transactionId, +}: { + fetchClient: YieldApiFetchClient; + signedTransaction: string; + transactionId: string; +}) => + getResponseData( + fetchClient.POST("/v1/transactions/{transactionId}/submit", { + params: { + path: { + transactionId, + }, + }, + body: { + signedTransaction, + }, + }) + ); + +export const submitTransactionHash = async ({ + fetchClient, + hash, + transactionId, +}: { + fetchClient: YieldApiFetchClient; + 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/index.tsx b/packages/widget/src/providers/yield-api-client-provider/index.tsx new file mode 100644 index 00000000..9c35b670 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/index.tsx @@ -0,0 +1,124 @@ +import type { i18n } from "i18next"; +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 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"; + +const QueryContext = createContext | undefined>( + undefined +); +const FetchContext = createContext(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 = createYieldApiFetchClient({ apiKey, i18n, url }); + + return { + fetchClient, + queryClient: createClient(fetchClient), + }; + }, [apiKey, i18n, 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/request-helpers.ts b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts new file mode 100644 index 00000000..ca233251 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/request-helpers.ts @@ -0,0 +1,34 @@ +import type { YieldActionArgumentsDto } from "../../domain/types/action"; +import type { AddressWithTokenDtoAdditionalAddresses } from "../../domain/types/addresses"; + +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 new file mode 100644 index 00000000..3f168ac8 --- /dev/null +++ b/packages/widget/src/providers/yield-api-client-provider/types.ts @@ -0,0 +1,32 @@ +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/translation/English/translations.json b/packages/widget/src/translation/English/translations.json index f4f13e8d..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" }, @@ -463,6 +473,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", @@ -481,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", @@ -509,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", @@ -540,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 0ca0efac..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" }, @@ -413,6 +423,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é", @@ -431,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", @@ -459,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", @@ -490,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 new file mode 100644 index 00000000..b8d0f3de --- /dev/null +++ b/packages/widget/src/types/yield-api-schema.d.ts @@ -0,0 +1,6206 @@ +/** + * 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/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; + 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}/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; + 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" + | "campaign_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" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_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, in human-readable token units (not the smallest denomination). + * @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; + /** @description Personalized reward rate breakdown for this balance position */ + rewardRate?: components["schemas"]["RewardRateDto"] | 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; + /** + * @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: { + /** + * @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 (maximum 25 queries per request) + * @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" + | "BATCH"; + /** + * @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 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 in human-readable token units, not the smallest denomination. Precision up to the token's decimal places is supported. + * @example [ + * "1.500000", + * "2.000000" + * ] + */ + 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 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 + */ + 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" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_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, in human-readable token units (not the smallest denomination). + * @example 1.0 + */ + amount: string | null; + /** + * @description Raw smallest-denomination 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 or reward rate + * @enum {string} + */ + sort?: + | "statusEnterAsc" + | "statusEnterDesc" + | "statusExitAsc" + | "statusExitDesc" + | "rewardRateAsc" + | "rewardRateDesc"; + }; + 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; + }; + 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 + * @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" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_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" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_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; + /** + * @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: { + /** + * @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 or reward rate */ + sort?: + | "statusEnterAsc" + | "statusEnterDesc" + | "statusExitAsc" + | "statusExitDesc" + | "rewardRateAsc" + | "rewardRateDesc"; + }; + 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_getBalanceHistory: { + parameters: { + 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 + * @example ethereum-eth-lido-staking + */ + yieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Returns a paginated time series of balance snapshots */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "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 */ + 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 solana-sol-native-multivalidator-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" + | "AUTO_SWEEP_UNSTAKE_REWARDS" + | "AUTO_SWEEP_WITHDRAW_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; + /** @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; + 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..64c3e4ce 100644 --- a/packages/widget/src/utils/formatters.ts +++ b/packages/widget/src/utils/formatters.ts @@ -1,8 +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 { RewardTypes } from "../domain/types/reward-rate"; +import type { TokenDto, YieldTokenDto } from "../domain/types/tokens"; +import type { Yield } from "../domain/types/yields"; import { APToPercentage, defaultFormattedNumber, formatNumber } from "."; export const formatCountryCode = ({ @@ -15,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) { @@ -27,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"; @@ -45,7 +48,7 @@ export const getGasFeeInUSD = ({ gas, prices, }: { - yieldDto: Maybe; + yieldDto: Maybe; gas: Maybe; prices: Maybe; }) => @@ -58,14 +61,14 @@ export const getGasFeeInUSD = ({ gasFeeInUSD: getTokenPriceInUSD({ amount: val.gas.toString(), prices: prices.orDefault(new Prices(new Map())), - token: val.yieldDto.metadata.gasFeeToken, + token: val.yieldDto.mechanics.gasFeeToken, pricePerShare: null, baseToken: null, }), })) .mapOrDefault( (val) => - `${formatNumber(val.gas, 10)} ${val.yieldDto.metadata.gasFeeToken.symbol} ${ + `${formatNumber(val.gas, 10)} ${val.yieldDto.mechanics.gasFeeToken.symbol} ${ val.gasFeeInUSD.isGreaterThan(0) ? ` ($${defaultFormattedNumber(val.gasFeeInUSD)})` : "" @@ -79,7 +82,7 @@ export const getFeesInUSD = ({ token, }: { amount: Maybe; - token: Maybe; + token: Maybe; prices: Maybe; }) => Maybe.fromRecord({ token, amount }) 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/utils/mappers.ts b/packages/widget/src/utils/mappers.ts index dee70454..91cf1781 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"; 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/worker.ts b/packages/widget/src/worker.ts index e4608e7e..80e878a0 100644 --- a/packages/widget/src/worker.ts +++ b/packages/widget/src/worker.ts @@ -1,190 +1,284 @@ import { HttpResponse, http } from "msw"; import { setupWorker } from "msw/browser"; +import { config } from "./config"; -// const validAddressAndNetwork = { -// address: "akash12z0hpqxj3txaf85zlla7zqffp7n9sl8wc3hlzh", -// network: "akash", -// }; +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", + 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(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: [ + { + 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(); -// }) 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"); + }); +}); diff --git a/packages/widget/tests/fixtures/index.ts b/packages/widget/tests/fixtures/index.ts index 4b375bc8..936cf867 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, @@ -7,10 +6,251 @@ import { getYieldV2ControllerGetYieldByIdResponseMock, } from "@stakekit/api-hooks/msw"; import { Just } from "purify-ts"; +import type { + YieldActionArgumentsDto, + YieldActionDto, + YieldDto as YieldApiYieldDto, + YieldBalanceDto, + YieldRewardRateDto, + YieldTransactionDto, + YieldValidatorDto, +} from "../../src/providers/yield-api-client-provider/types"; +import type { components } from "../../src/types/yield-api-schema"; + +type LegacyActionDto = ReturnType; +type LegacyTransactionDto = ReturnType< + typeof getTransactionControllerConstructResponseMock +>; +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"]; +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 +): YieldRewardRateDto => ({ + total: apyFaker(), + rateType: "APY", + components: [], + ...overrides, +}); + +export const yieldApiYieldFixture = ( + overrides?: Partial +): YieldApiYieldDto => + ({ + ...getYieldV2ControllerGetYieldByIdResponseMock(), + rewardRate: yieldRewardRateFixture(), + ...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, + { + 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"; + } -export const yieldFixture = (overrides?: Partial) => + 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) > 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?.map(mapLegacyValidatorToYieldValidator) ?? [], + ...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( (val) => @@ -31,23 +271,93 @@ 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 enterResponseFixture = (overrides?: Partial) => ({ +export const yieldValidatorsFixture = ( + validators?: LegacyYieldDto["validators"] +): YieldValidatorDto[] => + (validators ?? yieldFixture().validators).map((validator) => + mapLegacyValidatorToYieldValidator(validator) + ); + +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: LegacyTransactionDto, + 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: LegacyActionDto; + 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/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/param-validation.test.tsx b/packages/widget/tests/use-cases/deep-links-flow/param-validation.test.tsx index a9a0edc9..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 @@ -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"; @@ -60,7 +60,7 @@ describe("Deep link param validation", () => { it("Should validate pendingAction param", async () => { const setAndAssertIsValidPendingActionParam = async ( - pendingaction: ActionTypes | (string & {}), + pendingaction: ActionType | (string & {}), valid: boolean ) => { _setUrl({ pendingaction }); 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..e5df3511 100644 --- a/packages/widget/tests/use-cases/deep-links-flow/setup.ts +++ b/packages/widget/tests/use-cases/deep-links-flow/setup.ts @@ -1,19 +1,29 @@ -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, + 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"; +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", @@ -22,7 +32,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, @@ -35,7 +45,7 @@ export const setup = async (opts?: { const avaxNativeStaking = Just(yieldFixture()) .map( - (def): YieldDto => ({ + (def): ReturnType => ({ ...def, id: "avalanche-avax-native-staking", token, @@ -46,6 +56,25 @@ export const setup = async (opts?: { ) .unsafeCoerce(); + 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( (def) => @@ -76,27 +105,19 @@ 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, - }, - ] - : [], - }) satisfies YieldDto + validators: avaxLiquidStakingValidators, + }) satisfies ReturnType ) .unsafeCoerce(); - const avaxLiquidStakingBalances: YieldBalanceDto[] = [ + const avaxNativeStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: avaxNativeStaking, + }); + const avaxLiquidStakingYieldApi = yieldApiYieldFixtureFromLegacy({ + legacyYield: avaxLiquidStaking, + }); + + const avaxLiquidStakingBalances = [ { groupId: "b4684f63-fe54-540d-b0ae-06a2c2ecdb9e", type: "rewards", @@ -120,6 +141,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, @@ -155,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 () => { @@ -185,14 +219,38 @@ 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(); + + 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([ @@ -202,6 +260,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({ @@ -211,40 +281,60 @@ 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 }; + 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..c4623b95 100644 --- a/packages/widget/tests/use-cases/external-provider/setup.ts +++ b/packages/widget/tests/use-cases/external-provider/setup.ts @@ -1,11 +1,17 @@ -import type { TokenDto, YieldDto } from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; -import { yieldFixture } from "../../fixtures"; +import { + yieldApiYieldFixtureFromLegacy, + yieldFixture, + yieldValidatorsFixture, +} from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; 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 +20,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 +29,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 +38,7 @@ export const setup = () => { logoURI: "https://assets.stakek.it/tokens/sol.svg", }; - const tonToken: TokenDto = { + const tonToken: LegacyTokenDto = { network: "ton", name: "Toncoin", symbol: "TON", @@ -49,12 +55,13 @@ export const setup = () => { id: "avalanche-avax-native-staking", token: avalancheCToken, tokens: [avalancheCToken], + validators: [], metadata: { ...val.metadata, type: "staking", gasFeeToken: avalancheCToken, }, - }) satisfies YieldDto + }) satisfies ReturnType ) .unsafeCoerce(); @@ -66,12 +73,13 @@ export const setup = () => { id: "ethereum-eth-etherfi-staking", token: ether, tokens: [ether], + validators: [], metadata: { ...val.metadata, type: "staking", gasFeeToken: ether, }, - }) satisfies YieldDto + }) satisfies ReturnType ) .unsafeCoerce(); @@ -83,12 +91,13 @@ export const setup = () => { id: "solana-sol-native-staking", token: solanaToken, tokens: [solanaToken], + validators: [], metadata: { ...val.metadata, type: "staking", gasFeeToken: solanaToken, }, - }) satisfies YieldDto + }) satisfies ReturnType ) .unsafeCoerce(); @@ -100,23 +109,37 @@ export const setup = () => { id: "ton-native-staking", token: tonToken, tokens: [tonToken], + validators: [], metadata: { ...val.metadata, type: "staking", gasFeeToken: tonToken, }, - }) satisfies YieldDto + }) satisfies ReturnType ) .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 }, ]); }), @@ -160,25 +183,82 @@ export const setup = () => { ]); }), - http.get(`*/v1/yields/${etherNativeStaking.id}`, async () => { + http.get( + legacyApiRoute(`/v1/yields/${etherNativeStaking.id}`), + async () => { + await delay(); + + return HttpResponse.json(etherNativeStaking); + } + ), + http.get(yieldApiRoute(`/v1/yields/${etherNativeStaking.id}`), async () => { await delay(); - return HttpResponse.json(etherNativeStaking); + return HttpResponse.json(etherNativeStakingYieldApi); }), - http.get(`*/v1/yields/${avalancheAvaxNativeStaking.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(avalancheAvaxNativeStaking); + return HttpResponse.json(tonNativeStaking); }), - http.get(`*/v1/yields/${solanaNativeStaking.id}`, async () => { + http.get(yieldApiRoute(`/v1/yields/${tonNativeStaking.id}`), async () => { await delay(); - return HttpResponse.json(solanaNativeStaking); + return HttpResponse.json(tonNativeStakingYieldApi); }), - http.get(`*/v1/yields/${tonNativeStaking.id}`, async () => { + http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); - return HttpResponse.json(tonNativeStaking); + const yieldId = info.params.yieldId as string; + 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) ?? [] + ); + + 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..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 @@ -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({ 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 dd16f71a..c76477ae 100644 --- a/packages/widget/tests/use-cases/gas-warning-flow/setup.ts +++ b/packages/widget/tests/use-cases/gas-warning-flow/setup.ts @@ -1,24 +1,25 @@ -import type { - ActionDto, - ActionRequestDto, - TokenDto, - TransactionDto, - YieldDto, -} from "@stakekit/api-hooks"; import { delay, HttpResponse, http } from "msw"; import { Just } from "purify-ts"; import { vitest } from "vitest"; +import type { YieldCreateActionDto } from "../../../src/providers/yield-api-client-provider/types"; import { waitForMs } from "../../../src/utils"; import { enterResponseFixture, 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"; +type LegacyTokenDto = ReturnType["token"]; + export const setup = () => { - const avalancheCToken: TokenDto = { + const avalancheCToken: LegacyTokenDto = { name: "Avalanche C Chain", symbol: "AVAX", decimals: 18, @@ -26,7 +27,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", @@ -43,12 +44,13 @@ export const setup = () => { id: "avalanche-avax-native-staking", token: avalancheCToken, tokens: [avalancheCToken], + validators: [], metadata: { ...val.metadata, type: "staking", gasFeeToken: avalancheCToken, }, - } satisfies YieldDto, + } satisfies ReturnType, })) .map((val) => ({ ...val, @@ -69,7 +71,7 @@ export const setup = () => { gasEstimate: null, }, ], - } satisfies ActionDto, + } satisfies ReturnType, })) .unsafeCoerce(); @@ -80,12 +82,13 @@ export const setup = () => { id: "avalanche-c-usdc-aave-v3-lending", token: usdcToken, tokens: [usdcToken], + validators: [], metadata: { ...val.metadata, type: "staking", gasFeeToken: avalancheCToken, }, - } satisfies YieldDto, + } satisfies ReturnType, })) .map((val) => ({ ...val, @@ -109,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"; @@ -120,20 +131,23 @@ 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); 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 () => { @@ -182,7 +196,7 @@ export const setup = () => { }), http.get( - `*/v1/yields/${yieldWithSameGasAndStakeToken.yieldDto.id}`, + legacyApiRoute(`/v1/yields/${yieldWithSameGasAndStakeToken.yieldDto.id}`), async () => { await delay(); @@ -190,69 +204,86 @@ export const setup = () => { } ), http.get( - `*/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}`, + yieldApiRoute(`/v1/yields/${yieldWithSameGasAndStakeToken.yieldDto.id}`), async () => { await delay(); - return HttpResponse.json(yieldWithDifferentGasAndStakeToken.yieldDto); + return HttpResponse.json(yieldWithSameGasAndStakeTokenYieldApi); } ), - http.post("*/v1/actions/enter", async (info) => { - await delay(); - - const body = (await info.request.json()) as ActionRequestDto; - - 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) - ); + http.get( + legacyApiRoute( + `/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}` + ), + async () => { + await delay(); - if (!yieldWithAction) { - return new HttpResponse(null, { status: 400 }); + return HttpResponse.json(yieldWithDifferentGasAndStakeToken.yieldDto); } + ), + http.get( + yieldApiRoute( + `/v1/yields/${yieldWithDifferentGasAndStakeToken.yieldDto.id}` + ), + async () => { + await delay(); - const tx = yieldWithAction.actionDto.transactions.find( - (tx) => tx.id === transactionId - ); - - if (!tx) { - return new HttpResponse(null, { status: 400 }); + return HttpResponse.json(yieldWithDifferentGasAndStakeTokenYieldApi); } - + ), + http.get("*/v1/yields/:yieldId/validators", async (info) => { await delay(); + const yieldId = info.params.yieldId as string; + const validators = + yieldId === yieldWithSameGasAndStakeToken.yieldDto.id + ? yieldValidatorsFixture( + yieldWithSameGasAndStakeToken.yieldDto.validators + ) + : yieldValidatorsFixture( + yieldWithDifferentGasAndStakeToken.yieldDto.validators + ); + 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 ReturnType< + typeof enterResponseFixture + >, + address: body.address, + rawArguments: body.arguments ?? null, + transactions: selectedYield.actionDto.transactions.map((tx, index) => + yieldApiTransactionFixture( + tx as ReturnType, + { + gasEstimate: JSON.stringify({ + amount: gasAmount, + token: avalancheCToken, + }), + status: "CREATED", + stepIndex: index, + } + ) + ), + overrides: { + amount: body.arguments?.amount ?? null, + amountRaw: body.arguments?.amount ?? null, + }, + }), }); }) ); 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 e68efdbf..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,14 +1,16 @@ -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"; -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"; +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 +19,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 +41,7 @@ describe("Renders initial page", () => { type: "staking", gasFeeToken: avalancheCToken, }, - }) satisfies YieldDto + }) satisfies ReturnType ) .unsafeCoerce(); @@ -56,16 +58,23 @@ describe("Renders initial page", () => { type: "staking", gasFeeToken: ether, }, - }) satisfies YieldDto + }) satisfies ReturnType ) .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 }, ]); }), @@ -81,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(etherNativeStakingYieldApi); + } + ), + http.get( + legacyApiRoute(`/v1/yields/${avalancheAvaxNativeStaking.id}`), + async () => { + await delay(); - return HttpResponse.json(avalancheAvaxNativeStaking); - }) + 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 04b4ee7c..98c32812 100644 --- a/packages/widget/tests/use-cases/select-opportunity.test.tsx +++ b/packages/widget/tests/use-cases/select-opportunity.test.tsx @@ -1,16 +1,20 @@ -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"; 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"; +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 = { + window.history.pushState({}, "", "/"); + + const token: LegacyTokenDto = { network: "ethereum", name: "Ethereum", symbol: "ETH", @@ -18,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 () => { @@ -86,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 YieldDto; - }) - .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, + }) + ); + }), + ]; }) ); @@ -140,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 f3727aed..f785ce9e 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,15 @@ 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, + yieldApiYieldFixtureFromLegacy, + yieldValidatorsFixture, +} from "../../fixtures"; +import { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; import { worker } from "../../mocks/worker"; import { rkMockWallet } from "../../utils/mock-connector"; @@ -119,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", @@ -193,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 () => { @@ -241,9 +250,33 @@ export const setup = async () => { }, }); }), - http.get("*/v1/yields/avalanche-avax-liquid-staking", async () => { + 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(); - return HttpResponse.json(yieldOp); + + 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(); @@ -286,46 +319,58 @@ 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 + ? JSON.stringify(transactionConstruct.gasEstimate) + : 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", + }) + ); }) ); @@ -348,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/staking-flow/staking-flow.test.tsx b/packages/widget/tests/use-cases/staking-flow/staking-flow.test.tsx index 354d8f10..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 @@ -74,7 +74,13 @@ describe("Staking flow", () => { .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 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..8f671260 --- /dev/null +++ b/packages/widget/tests/use-cases/trust-incentive-apy/setup.ts @@ -0,0 +1,368 @@ +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 { legacyApiRoute, yieldApiRoute } from "../../mocks/api-routes"; +import { worker } from "../../mocks/worker"; +import { rkMockWallet } from "../../utils/mock-connector"; + +type LegacyTokenDto = ReturnType["token"]; + +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 ({ + personalizedRewardRate: providedPersonalizedRewardRate, + useRewardRateWithoutCampaign, +}: { + personalizedRewardRate?: YieldRewardRateDto; + useRewardRateWithoutCampaign?: boolean; +} = {}) => { + const account = "0xB6c5273e79E2aDD234EBC07d87F3824e0f94B2F7"; + + const token: LegacyTokenDto = { + name: "USDA", + symbol: "USDA", + decimals: 18, + network: "avalanche-c", + coinGeckoId: "angle-usd", + logoURI: "https://assets.stakek.it/tokens/usda.svg", + }; + + const rewardToken: LegacyTokenDto = { + name: "United Stables", + symbol: "U", + decimals: 18, + network: token.network, + address: "0x58D97B57BB95320F9a05dC918Aef65434969c2B2", + logoURI: "https://assets.stakek.it/tokens/usda.svg", + }; + + const morphoToken: LegacyTokenDto = { + 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 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"; + + const legacyYieldBase = yieldFixture(); + const rawYieldBase = yieldApiYieldFixture(); + + const legacyYield: ReturnType = { + ...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(yieldApiRoute("/v1/networks"), async () => { + await delay(); + return HttpResponse.json([{ id: 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(legacyApiRoute(`/v1/yields/${yieldId}`), async () => { + await delay(); + 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(); + 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..5a3fa564 --- /dev/null +++ b/packages/widget/tests/use-cases/trust-incentive-apy/trust-incentive-apy.test.tsx @@ -0,0 +1,136 @@ +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.2%") + ) + .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 4.55%")) + .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(); + }); + + 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(); + }); +}); 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 }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6db09c3a..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) @@ -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 @@ -610,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] @@ -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 @@ -10687,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)': @@ -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: {}