From 4556a61e5d380b8f52c286b04b9511a4fd51e358 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 27 Mar 2026 16:50:12 +0100 Subject: [PATCH 1/3] feat(frontend): replace KongSwap with Juno API --- .env.development | 1 - .env.production | 1 - src/frontend/src/lib/rest/api.rest.ts | 29 +++++++++++++ src/frontend/src/lib/rest/kongswap.rest.ts | 30 ------------- src/frontend/src/lib/schemas/api.schema.ts | 15 +++++++ .../src/lib/schemas/exchange.schema.ts | 3 -- .../src/lib/schemas/kongswap.schema.ts | 43 ------------------- src/frontend/src/lib/types/api.ts | 4 ++ src/frontend/src/lib/types/kongswap.ts | 4 -- .../src/lib/workers/exchange.worker.ts | 21 +++------ src/frontend/src/vite-env.d.ts | 1 - 11 files changed, 53 insertions(+), 99 deletions(-) create mode 100644 src/frontend/src/lib/rest/api.rest.ts delete mode 100644 src/frontend/src/lib/rest/kongswap.rest.ts create mode 100644 src/frontend/src/lib/schemas/api.schema.ts delete mode 100644 src/frontend/src/lib/schemas/kongswap.schema.ts create mode 100644 src/frontend/src/lib/types/api.ts delete mode 100644 src/frontend/src/lib/types/kongswap.ts diff --git a/.env.development b/.env.development index 9ab3b3ac74..759f23b18b 100644 --- a/.env.development +++ b/.env.development @@ -2,6 +2,5 @@ VITE_BN_REGISTRATIONS_URL=https://icp0.io/registrations VITE_BN_CUSTOM_DOMAINS_URL=https://icp0.io/custom-domains/v1 VITE_JUNO_CDN_URL= VITE_CYCLE_EXPRESS_URL=https://cycle.express/ -VITE_KONGSWAP_API_URL= VITE_ICP_EXPLORER_URL=https://dashboard.internetcomputer.org VITE_EMULATOR_ADMIN_URL=http://localhost:5999 \ No newline at end of file diff --git a/.env.production b/.env.production index 579f290272..f61b7543da 100644 --- a/.env.production +++ b/.env.production @@ -2,5 +2,4 @@ VITE_BN_REGISTRATIONS_URL=https://icp0.io/registrations VITE_BN_CUSTOM_DOMAINS_URL=https://icp0.io/custom-domains/v1 VITE_JUNO_CDN_URL=https://cdn.juno.build VITE_CYCLE_EXPRESS_URL=https://cycle.express/ -VITE_KONGSWAP_API_URL=https://api.kongswap.io VITE_ICP_EXPLORER_URL=https://dashboard.internetcomputer.org \ No newline at end of file diff --git a/src/frontend/src/lib/rest/api.rest.ts b/src/frontend/src/lib/rest/api.rest.ts new file mode 100644 index 0000000000..616d9fcf02 --- /dev/null +++ b/src/frontend/src/lib/rest/api.rest.ts @@ -0,0 +1,29 @@ +import { JUNO_API_URL } from '$lib/constants/app.constants'; +import { ApiExchangePriceResponseSchema } from '$lib/schemas/api.schema'; +import type { ApiExchangePriceResponse } from '$lib/types/api'; +import { assertResponseOk } from '$lib/utils/rest.utils'; +import { isEmptyString } from '@dfinity/utils'; +import type { PrincipalText } from '@junobuild/schema'; + +export const fetchExchangePrice = async ({ + ledgerId +}: { + ledgerId: PrincipalText; +}): Promise => { + if (isEmptyString(JUNO_API_URL)) { + return undefined; + } + + const response = await fetch(`${JUNO_API_URL}/v1/exchange/price/${ledgerId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + assertResponseOk(response, 'Fetching Juno API failed.'); + + const data = await response.json(); + + return ApiExchangePriceResponseSchema.parse(data); +}; diff --git a/src/frontend/src/lib/rest/kongswap.rest.ts b/src/frontend/src/lib/rest/kongswap.rest.ts deleted file mode 100644 index 4f632a0578..0000000000 --- a/src/frontend/src/lib/rest/kongswap.rest.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { KongSwapTokenSchema } from '$lib/schemas/kongswap.schema'; -import type { KongSwapToken } from '$lib/types/kongswap'; -import { assertResponseOk } from '$lib/utils/rest.utils'; -import { isEmptyString } from '@dfinity/utils'; -import type { PrincipalText } from '@junobuild/schema'; - -export const fetchKongSwapToken = async ({ - ledgerId -}: { - ledgerId: PrincipalText; -}): Promise => { - const KONGSWAP_API_URL = import.meta.env.VITE_KONGSWAP_API_URL; - - if (isEmptyString(KONGSWAP_API_URL)) { - return undefined; - } - - const response = await fetch(`${KONGSWAP_API_URL}/api/tokens/${ledgerId}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }); - - assertResponseOk(response, 'Fetching KongSwap failed.'); - - const data = await response.json(); - - return KongSwapTokenSchema.parse(data); -}; diff --git a/src/frontend/src/lib/schemas/api.schema.ts b/src/frontend/src/lib/schemas/api.schema.ts new file mode 100644 index 0000000000..cb3580f861 --- /dev/null +++ b/src/frontend/src/lib/schemas/api.schema.ts @@ -0,0 +1,15 @@ +import * as z from 'zod'; + +const BinanceTickerPriceSchema = z.strictObject({ + symbol: z.string(), + price: z.string() +}); + +const ExchangePriceSchema = z.strictObject({ + ...BinanceTickerPriceSchema.shape, + fetchedAt: z.iso.datetime() +}); + +export const ApiExchangePriceResponseSchema= z.strictObject({ + price: ExchangePriceSchema +}) \ No newline at end of file diff --git a/src/frontend/src/lib/schemas/exchange.schema.ts b/src/frontend/src/lib/schemas/exchange.schema.ts index 4d90241905..9bdadb9f6e 100644 --- a/src/frontend/src/lib/schemas/exchange.schema.ts +++ b/src/frontend/src/lib/schemas/exchange.schema.ts @@ -2,8 +2,5 @@ import * as z from 'zod'; export const ExchangePriceSchema = z.object({ usd: z.number(), - usdMarketCap: z.number().optional(), - usdVolume24h: z.number().optional(), - usdChange24h: z.number().optional(), updatedAt: z.number() // Represents Date.getTime() }); diff --git a/src/frontend/src/lib/schemas/kongswap.schema.ts b/src/frontend/src/lib/schemas/kongswap.schema.ts deleted file mode 100644 index d0b81fa56b..0000000000 --- a/src/frontend/src/lib/schemas/kongswap.schema.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { PrincipalTextSchema } from '@junobuild/schema'; -import * as z from 'zod'; - -const DateTimeSchema = z - .string() - .refine((val) => !isNaN(new Date(val).getTime()), { message: 'Invalid ISO date' }); - -const KongSwapTokenMetricsSchema = z.object({ - token_id: z.number(), - total_supply: z.number().nullable(), - market_cap: z.number().nullable(), - price: z.number().nullable(), - updated_at: DateTimeSchema, - volume_24h: z.number().nullable(), - tvl: z.number().nullable(), - price_change_24h: z.number().nullable(), - previous_price: z.number().nullable(), - is_verified: z.boolean() -}); - -const KongSwapTokenBaseSchema = z.object({ - token_id: z.number(), - name: z.string(), - symbol: z.string(), - canister_id: PrincipalTextSchema, - address: z.string().nullable(), - decimals: z.number(), - fee: z.number(), - fee_fixed: z.string().nullable(), - has_custom_logo: z.boolean(), - icrc1: z.boolean(), - icrc2: z.boolean(), - icrc3: z.boolean(), - is_removed: z.boolean(), - logo_url: z.string().nullable(), - logo_updated_at: DateTimeSchema.nullable(), - token_type: z.string() -}); - -export const KongSwapTokenSchema = z.object({ - token: KongSwapTokenBaseSchema, - metrics: KongSwapTokenMetricsSchema -}); diff --git a/src/frontend/src/lib/types/api.ts b/src/frontend/src/lib/types/api.ts new file mode 100644 index 0000000000..5a531fc30c --- /dev/null +++ b/src/frontend/src/lib/types/api.ts @@ -0,0 +1,4 @@ +import { ApiExchangePriceResponseSchema } from '$lib/schemas/api.schema'; +import type * as z from 'zod'; + +export type ApiExchangePriceResponse = z.infer; diff --git a/src/frontend/src/lib/types/kongswap.ts b/src/frontend/src/lib/types/kongswap.ts deleted file mode 100644 index 6752d9cd2a..0000000000 --- a/src/frontend/src/lib/types/kongswap.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { KongSwapTokenSchema } from '$lib/schemas/kongswap.schema'; -import type * as z from 'zod'; - -export type KongSwapToken = z.infer; diff --git a/src/frontend/src/lib/workers/exchange.worker.ts b/src/frontend/src/lib/workers/exchange.worker.ts index 5a6aa3313c..26be331fcd 100644 --- a/src/frontend/src/lib/workers/exchange.worker.ts +++ b/src/frontend/src/lib/workers/exchange.worker.ts @@ -1,12 +1,11 @@ import { ICP_LEDGER_CANISTER_ID, SYNC_TOKENS_TIMER_INTERVAL } from '$lib/constants/app.constants'; import { PRICE_VALIDITY_TIMEFRAME } from '$lib/constants/exchange.constants'; -import { fetchKongSwapToken } from '$lib/rest/kongswap.rest'; +import { fetchExchangePrice } from '$lib/rest/api.rest'; import { exchangeIdbStore } from '$lib/stores/app/idb.store'; import type { CanisterIdText } from '$lib/types/canister'; import type { ExchangePrice } from '$lib/types/exchange'; -import type { KongSwapToken } from '$lib/types/kongswap'; import type { PostMessageDataResponseExchange, PostMessageRequest } from '$lib/types/post-message'; -import { isNullish, nonNullish } from '@dfinity/utils'; +import { isNullish } from '@dfinity/utils'; import { del, entries, set } from 'idb-keyval'; export const onExchangeMessage = async ({ data: dataMsg }: MessageEvent) => { @@ -85,32 +84,22 @@ const syncExchange = async () => { }; const exchangeRateICPToUsd = async (): Promise => { - const icp = await findICPToken(); + const icp = await fetchExchangePrice({ ledgerId: ICP_LEDGER_CANISTER_ID }); if (isNullish(icp)) { return undefined; } const { - metrics: { price, updated_at, market_cap, volume_24h, price_change_24h } + price: { price, fetchedAt } } = icp; - if (isNullish(price)) { - return undefined; - } - return { usd: Number(price), - ...(nonNullish(market_cap) && { usdMarketCap: Number(market_cap) }), - ...(nonNullish(volume_24h) && { usdVolume24h: Number(volume_24h) }), - ...(nonNullish(price_change_24h) && { usdChange24h: Number(price_change_24h) }), - updatedAt: nonNullish(updated_at) ? new Date(updated_at).getTime() : new Date().getTime() + updatedAt: new Date(fetchedAt).getTime() }; }; -const findICPToken = async (): Promise => - await fetchKongSwapToken({ ledgerId: ICP_LEDGER_CANISTER_ID }); - const syncExchangePrice = async (exchangePrice: ExchangePrice) => { // Save information in indexed-db as well to load previous values on navigation and refresh await set(ICP_LEDGER_CANISTER_ID, exchangePrice, exchangeIdbStore); diff --git a/src/frontend/src/vite-env.d.ts b/src/frontend/src/vite-env.d.ts index 055f0edae4..70bba9ecea 100644 --- a/src/frontend/src/vite-env.d.ts +++ b/src/frontend/src/vite-env.d.ts @@ -17,7 +17,6 @@ interface ImportMetaEnv { readonly VITE_BN_CUSTOM_DOMAINS_URL: string | '' | undefined; readonly VITE_JUNO_CDN_URL: string | '' | undefined; readonly VITE_CYCLE_EXPRESS_URL: string | '' | undefined; - readonly VITE_KONGSWAP_API_URL: string | '' | undefined; readonly VITE_ICP_EXPLORER_URL: string | '' | undefined; readonly VITE_EMULATOR_ADMIN_URL: string | '' | undefined; } From 92243e036e7883baf5f0ce0e6bf9aef56e2f8150 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 27 Mar 2026 16:55:25 +0100 Subject: [PATCH 2/3] chore: fmt --- src/frontend/src/lib/schemas/api.schema.ts | 4 ++-- src/frontend/src/lib/types/api.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/lib/schemas/api.schema.ts b/src/frontend/src/lib/schemas/api.schema.ts index cb3580f861..dc0809d87a 100644 --- a/src/frontend/src/lib/schemas/api.schema.ts +++ b/src/frontend/src/lib/schemas/api.schema.ts @@ -10,6 +10,6 @@ const ExchangePriceSchema = z.strictObject({ fetchedAt: z.iso.datetime() }); -export const ApiExchangePriceResponseSchema= z.strictObject({ +export const ApiExchangePriceResponseSchema = z.strictObject({ price: ExchangePriceSchema -}) \ No newline at end of file +}); diff --git a/src/frontend/src/lib/types/api.ts b/src/frontend/src/lib/types/api.ts index 5a531fc30c..4512e21ad7 100644 --- a/src/frontend/src/lib/types/api.ts +++ b/src/frontend/src/lib/types/api.ts @@ -1,4 +1,4 @@ -import { ApiExchangePriceResponseSchema } from '$lib/schemas/api.schema'; +import type { ApiExchangePriceResponseSchema } from '$lib/schemas/api.schema'; import type * as z from 'zod'; export type ApiExchangePriceResponse = z.infer; From b268d275c4f633ebe20f6bad20d5397157af6650 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 27 Mar 2026 18:11:39 +0100 Subject: [PATCH 3/3] feat: api 1.1 --- src/frontend/src/lib/schemas/api.schema.ts | 2 +- src/frontend/src/lib/workers/exchange.worker.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/lib/schemas/api.schema.ts b/src/frontend/src/lib/schemas/api.schema.ts index dc0809d87a..e871315b45 100644 --- a/src/frontend/src/lib/schemas/api.schema.ts +++ b/src/frontend/src/lib/schemas/api.schema.ts @@ -11,5 +11,5 @@ const ExchangePriceSchema = z.strictObject({ }); export const ApiExchangePriceResponseSchema = z.strictObject({ - price: ExchangePriceSchema + exchange: ExchangePriceSchema }); diff --git a/src/frontend/src/lib/workers/exchange.worker.ts b/src/frontend/src/lib/workers/exchange.worker.ts index 26be331fcd..c0e8abd97a 100644 --- a/src/frontend/src/lib/workers/exchange.worker.ts +++ b/src/frontend/src/lib/workers/exchange.worker.ts @@ -91,7 +91,7 @@ const exchangeRateICPToUsd = async (): Promise => { } const { - price: { price, fetchedAt } + exchange: { price, fetchedAt } } = icp; return {