diff --git a/.changeset/light-berries-begin.md b/.changeset/light-berries-begin.md new file mode 100644 index 000000000..9057755e7 --- /dev/null +++ b/.changeset/light-berries-begin.md @@ -0,0 +1,9 @@ +--- +"@carrot-kpi/shared-state": minor +"@carrot-kpi/host-frontend": minor +"@carrot-kpi/react": minor +"@carrot-kpi/sdk": minor +"@carrot-kpi/ui": minor +--- + +New environments handling diff --git a/packages/frontend/craco.config.js b/packages/frontend/craco.config.js index 13f1a8f28..ef46a0b83 100644 --- a/packages/frontend/craco.config.js +++ b/packages/frontend/craco.config.js @@ -6,11 +6,12 @@ const { EsbuildPlugin } = require("esbuild-loader"); const { join } = require("node:path"); const shared = require("./shared-dependencies.json"); const { getEnv } = require("./utils/env"); +const { Environment } = require("@carrot-kpi/shared-state"); -const ALLOWED_ENVIRONMENTS = ["prod", "staging", "dev", "library"]; +const ALLOWED_ENVIRONMENTS = Object.values(Environment); const environment = process.env.ENVIRONMENT; -if (ALLOWED_ENVIRONMENTS.indexOf(environment) < 0) { +if (!Object.values(Environment).includes(environment)) { console.error( `Invalid environment "${environment}" specified. Allowed values are: ${ALLOWED_ENVIRONMENTS.join( ", ", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 4a39cafbf..e32fa0cc1 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -35,10 +35,9 @@ "size-limit": "size-limit", "prepare-fathom": "prepare-fathom", "config-react-env": "./scripts/config-react-env.js", - "start:production": "yarn prepare-fathom && ENVIRONMENT=prod craco start", + "start:production": "yarn prepare-fathom && ENVIRONMENT=production craco start", "start:staging": "yarn prepare-fathom && ENVIRONMENT=staging craco start", - "start:dev": "yarn prepare-fathom && ENVIRONMENT=dev craco start", - "start": "craco start", + "start:dev": "yarn prepare-fathom && ENVIRONMENT=development craco start", "synpress:run": "env-cmd -f ../../.env.e2e synpress run --browser chrome --configFile synpress.config.js", "test:e2e": "start-server-and-test 'yarn start:staging' http-get://127.0.0.1:3000 'yarn synpress:run'" }, diff --git a/packages/frontend/rollup.config.mjs b/packages/frontend/rollup.config.mjs index cfafdf1eb..52b8c8fa9 100644 --- a/packages/frontend/rollup.config.mjs +++ b/packages/frontend/rollup.config.mjs @@ -11,6 +11,7 @@ import json from "@rollup/plugin-json"; import replace from "@rollup/plugin-replace"; import esbuild from "rollup-plugin-esbuild"; import { createRequire } from "module"; +import { Environment } from "@carrot-kpi/shared-state"; const require = createRequire(import.meta.url); const { getEnv } = require("./utils/env"); @@ -25,7 +26,7 @@ export default [ replace({ preventAssignment: true, values: { - __ENVIRONMENT__: JSON.stringify("library"), + __ENVIRONMENT__: JSON.stringify(Environment.Local), __INFURA_PROJECT_ID__: getEnv("INFURA_PROJECT_ID", true), __WALLETCONNECT_PROJECT_ID__: JSON.stringify(""), __FATHOM_SITE_ID__: JSON.stringify(""), diff --git a/packages/frontend/src/components/authenticate.tsx b/packages/frontend/src/components/authenticate.tsx index 5c219957a..87479bf20 100644 --- a/packages/frontend/src/components/authenticate.tsx +++ b/packages/frontend/src/components/authenticate.tsx @@ -5,13 +5,14 @@ import { t } from "i18next"; import { useAccount, useSignMessage } from "wagmi"; import { useSetDataManagerJWT } from "../hooks/useSetDataManagerJWT"; import { WalletDisconnected } from "./wallet-disconnected"; +import { SERVICE_URLS } from "@carrot-kpi/sdk"; interface AuthenticateProps { onCancel: () => void; } export const Authenticate = ({ onCancel }: AuthenticateProps) => { - const { chain, address } = useAccount(); + const { address } = useAccount(); const setDataManagerJWT = useSetDataManagerJWT(); const [loading, setLoading] = useState(false); @@ -20,11 +21,11 @@ export const Authenticate = ({ onCancel }: AuthenticateProps) => { useEffect(() => { const fetchData = async () => { - if (!chain || !address || !signedLoginMessage) return; + if (!address || !signedLoginMessage) return; setLoading(true); try { const response = await fetch( - `${chain.serviceUrls.dataManager}/token`, + `${SERVICE_URLS[__ENVIRONMENT__].dataManager}/token`, { method: "POST", headers: { @@ -50,15 +51,15 @@ export const Authenticate = ({ onCancel }: AuthenticateProps) => { } }; fetchData(); - }, [chain, address, setDataManagerJWT, signedLoginMessage]); + }, [address, setDataManagerJWT, signedLoginMessage]); const handleSign = useCallback(() => { const fetchSignableMessage = async () => { - if (!chain || !address) return; + if (!address) return; setLoading(true); try { const response = await fetch( - `${chain.serviceUrls.dataManager}/login-message/${address}`, + `${SERVICE_URLS[__ENVIRONMENT__].dataManager}/login-message/${address}`, ); if (!response.ok) throw new Error(await response.text()); const { message } = (await response.json()) as { @@ -74,7 +75,7 @@ export const Authenticate = ({ onCancel }: AuthenticateProps) => { } }; fetchSignableMessage(); - }, [chain, address, signMessage]); + }, [address, signMessage]); return (
diff --git a/packages/frontend/src/components/campaign-creation-form.tsx b/packages/frontend/src/components/campaign-creation-form.tsx index 4d38e0816..8d27edbdf 100644 --- a/packages/frontend/src/components/campaign-creation-form.tsx +++ b/packages/frontend/src/components/campaign-creation-form.tsx @@ -199,7 +199,6 @@ export function CampaignCreationForm>() { } const resolvedTemplates = await Fetcher.resolveTemplates({ ipfsGatewayURL, - dataCDNURL: chain.serviceUrls.dataCdn, preferDecentralization, templates: templates, }); diff --git a/packages/frontend/src/components/chain-select/chain-select.tsx b/packages/frontend/src/components/chain-select/chain-select.tsx index f066171ac..874c2dd16 100644 --- a/packages/frontend/src/components/chain-select/chain-select.tsx +++ b/packages/frontend/src/components/chain-select/chain-select.tsx @@ -7,6 +7,7 @@ import { NetworksPopover } from "./networks-popover"; import { Typography } from "@carrot-kpi/ui"; import { useTranslation } from "react-i18next"; import { useClickAway } from "react-use"; +import { Environment } from "@carrot-kpi/shared-state"; export interface ChainSelectProps { compact?: boolean; @@ -47,7 +48,7 @@ export const ChainSelect = ({ compact = true }: ChainSelectProps) => { return (
- {__ENVIRONMENT__ !== "library" && ( + {__ENVIRONMENT__ !== Environment.Local && ( {
{ {chain?.name || t("connect.wallet.unknown")} )} - {__ENVIRONMENT__ !== "library" && chains.length > 1 && ( + {__ENVIRONMENT__ !== Environment.Local && chains.length > 1 && ( )}
diff --git a/packages/frontend/src/components/connect-wallet/preferences.tsx b/packages/frontend/src/components/connect-wallet/preferences.tsx index c85adbf01..8f57c824a 100644 --- a/packages/frontend/src/components/connect-wallet/preferences.tsx +++ b/packages/frontend/src/components/connect-wallet/preferences.tsx @@ -8,6 +8,7 @@ import { } from "@carrot-kpi/react"; import { useTranslation } from "react-i18next"; import { useAccount } from "wagmi"; +import { Environment } from "@carrot-kpi/shared-state"; // TODO: move this in the UI lib with additional functionality (like variants for info, warning and error feedback) export const Preferences = () => { @@ -88,31 +89,32 @@ export const Preferences = () => { />
*/} - {!!chain?.serviceUrls.subgraph && __ENVIRONMENT__ !== "dev" && ( -
-
- - {t("preferences.decentralization")} + {!!chain?.subgraphUrl && + __ENVIRONMENT__ !== Environment.Development && ( +
+
+ + {t("preferences.decentralization")} + + +
+ + {t("preferences.decentralization.info")} -
- - {t("preferences.decentralization.info")} - -
- )} + )} {/* TODO: this setting is applied to both dev and staging environments but it's simply called "staging" mode. We need to find a name that is compatible with the fact that this setting should be available in both the dev and staging environments. Maybe preview mode? */} - {__ENVIRONMENT__ !== "prod" && ( + {__ENVIRONMENT__ !== Environment.Production && (
= { [sepolia.id as number]: http(), @@ -61,9 +47,9 @@ export const SUPPORTED_CHAIN_TRANSPORT: Record = { }; export const CARROT_DOMAIN = - __ENVIRONMENT__ === "prod" + __ENVIRONMENT__ === Environment.Production ? "carrot.community" - : __ENVIRONMENT__ === "staging" + : __ENVIRONMENT__ === Environment.Staging ? "staging.carrot.community" : "dev.carrot.community"; diff --git a/packages/frontend/src/hooks/useSearchResolvedKPITokens.ts b/packages/frontend/src/hooks/useSearchResolvedKPITokens.ts index 61e6814b3..d915c1596 100644 --- a/packages/frontend/src/hooks/useSearchResolvedKPITokens.ts +++ b/packages/frontend/src/hooks/useSearchResolvedKPITokens.ts @@ -26,7 +26,6 @@ export function useSearchResolvedKPITokens(searchQuery: string) { return ( await Fetcher.resolveKPITokens({ ipfsGatewayURL, - dataCDNURL: chain.serviceUrls.dataCdn, preferDecentralization, kpiTokens: [kpiToken], }) diff --git a/packages/frontend/src/pages/home/hero.tsx b/packages/frontend/src/pages/home/hero.tsx index 2aaf64055..5bb3acb17 100644 --- a/packages/frontend/src/pages/home/hero.tsx +++ b/packages/frontend/src/pages/home/hero.tsx @@ -14,6 +14,7 @@ import { NAVBAR_LINKS } from "../../constants"; import VideoPoster from "../../images/video-poster.png"; import type { KPIToken } from "@carrot-kpi/sdk"; import { Navbar } from "../../components/ui/navbar"; +import { Environment } from "@carrot-kpi/shared-state"; const plusIconStyles = cva(["invisible", "md:visible", "absolute"], { variants: { @@ -142,7 +143,7 @@ export const Hero = ({ featuredKPITokens }: HeroProps) => { - {__ENVIRONMENT__ !== "library" && ( + {__ENVIRONMENT__ !== Environment.Local && ( )}
diff --git a/packages/frontend/src/react-app-env.d.ts b/packages/frontend/src/react-app-env.d.ts index d99af303a..910862c83 100644 --- a/packages/frontend/src/react-app-env.d.ts +++ b/packages/frontend/src/react-app-env.d.ts @@ -7,9 +7,10 @@ import { type CarrotChain, } from "./constants"; import type { Config } from "wagmi"; +import type { Environment } from "@carrot-kpi/shared-state"; declare global { - const __ENVIRONMENT__: "dev" | "staging" | "prod" | "library"; + const __ENVIRONMENT__: Environment; const __INFURA_PROJECT_ID__: string; const __WALLETCONNECT_PROJECT_ID__: string | undefined; const __FATHOM_SITE_ID__: string | undefined; diff --git a/packages/frontend/src/standalone-entrypoint.tsx b/packages/frontend/src/standalone-entrypoint.tsx index b7635db1c..5067ccd24 100644 --- a/packages/frontend/src/standalone-entrypoint.tsx +++ b/packages/frontend/src/standalone-entrypoint.tsx @@ -10,9 +10,12 @@ import { SUPPORTED_CHAINS, } from "./constants"; import { HostStateProvider } from "./state"; -import { ReactSharedStateProvider } from "@carrot-kpi/shared-state"; import { - useSetDevMode, + Environment, + ReactSharedStateProvider, +} from "@carrot-kpi/shared-state"; +import { + useSetEnvironment, useSetIPFSGatewayURL, useSetTemplatePreviewMode, } from "@carrot-kpi/react"; @@ -60,18 +63,19 @@ export const config = createConfig({ }); export const Root = () => { - const setDevMode = useSetDevMode(); + const setEnvironment = useSetEnvironment(); const setTemplatePreviewMode = useSetTemplatePreviewMode(); const setIPFSGatewayURL = useSetIPFSGatewayURL(); - setDevMode(false); - setTemplatePreviewMode(__ENVIRONMENT__ !== "prod"); + setEnvironment(__ENVIRONMENT__); + setTemplatePreviewMode(__ENVIRONMENT__ !== Environment.Production); setIPFSGatewayURL(IPFS_GATEWAY_URL); return ( ); }; @@ -89,7 +93,10 @@ root.render( , ); -if (__ENVIRONMENT__ === "prod" && "serviceWorker" in navigator) { +if ( + __ENVIRONMENT__ !== Environment.Development && + "serviceWorker" in navigator +) { navigator.serviceWorker .register("./sw.js") .then(() => { diff --git a/packages/frontend/src/state/api.ts b/packages/frontend/src/state/api.ts index a41c4ad3b..5c35d9330 100644 --- a/packages/frontend/src/state/api.ts +++ b/packages/frontend/src/state/api.ts @@ -3,6 +3,7 @@ import { type FeaturedBlacklistedKPITokens, type KPIToken, type SupportedChain, + DATA_CDN_URL, } from "@carrot-kpi/sdk"; import { fetchBaseQuery } from "@reduxjs/toolkit/query/react"; import type { Address, PublicClient, Transport } from "viem"; @@ -41,7 +42,7 @@ export const staticApi = createCarrotApi({ }, }; const response = await fetch( - `${chain.serviceUrls.staticCdn}/featured-blacklisted-kpi-tokens.json`, + `${DATA_CDN_URL}/static/featured-blacklisted-kpi-tokens.json`, ); if (!response.ok) return { diff --git a/packages/frontend/src/sw.ts b/packages/frontend/src/sw.ts index 87cc0fd97..74e42b204 100644 --- a/packages/frontend/src/sw.ts +++ b/packages/frontend/src/sw.ts @@ -1,11 +1,10 @@ import { precacheAndRoute } from "workbox-precaching"; import { CID } from "multiformats/cid"; -import { SERVICE_URLS } from "@carrot-kpi/sdk"; +import { DATA_CDN_URL } from "@carrot-kpi/sdk"; declare const self: ServiceWorkerGlobalScope; const IPFS_CACHE_NAME = "ipfs-cache"; -const CDN_URLS = Object.values(SERVICE_URLS).map((urls) => urls.dataCdn); // TODO: make Workbox and precaching work (as of now all precachable entries are // excluded in the workbox config using craco) @@ -29,7 +28,7 @@ self.addEventListener("activate", () => { const cidFromUrl = (url: URL): CID | null => { // handle requests in the format $DATA_CDN_URL/cid/optional-path?whatever - if (CDN_URLS.includes(url.hostname)) { + if (DATA_CDN_URL.includes(url.hostname)) { const potentialCid = url.pathname.split("/")[1]; return CID.asCID(potentialCid); } diff --git a/packages/frontend/src/updaters/library-mode-shared-state.ts b/packages/frontend/src/updaters/library-mode-shared-state.ts index 00e5e0d2c..5b89044de 100644 --- a/packages/frontend/src/updaters/library-mode-shared-state.ts +++ b/packages/frontend/src/updaters/library-mode-shared-state.ts @@ -1,10 +1,11 @@ import { - useSetDevMode, + useSetEnvironment, useSetIPFSGatewayURL, useSetKPITokenTemplateBaseURL, useSetOracleTemplateBaseURL, useSetTemplatePreviewMode, } from "@carrot-kpi/react"; +import { Environment } from "@carrot-kpi/shared-state"; interface LibraryModeSharedStateUpdaterProps { ipfsGatewayURL: string; @@ -19,13 +20,13 @@ export const LibraryModeSharedStateUpdater = ({ ipfsGatewayURL, enableTemplatePreviewMode = false, }: LibraryModeSharedStateUpdaterProps) => { - const setDevMode = useSetDevMode(); + const setEnvironment = useSetEnvironment(); const setTemplatePreviewMode = useSetTemplatePreviewMode(); const setIPFSGatewayURL = useSetIPFSGatewayURL(); const setKPITokenTemplateBaseURL = useSetKPITokenTemplateBaseURL(); const setOracleTemplateBaseURL = useSetOracleTemplateBaseURL(); - setDevMode(true); + setEnvironment(Environment.Local); setTemplatePreviewMode(enableTemplatePreviewMode); setIPFSGatewayURL(ipfsGatewayURL); setKPITokenTemplateBaseURL(kpiTokenTemplateBaseURL); diff --git a/packages/react/package.json b/packages/react/package.json index bf7b2beec..d48fbf420 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -44,10 +44,10 @@ "import": "./dist/es/components/template-component.mjs", "default": "./dist/cjs/components/template-component.cjs" }, - "./useDevMode": { - "types": "./dist/types/hooks/useDevMode.d.ts", - "import": "./dist/es/hooks/useDevMode.mjs", - "default": "./dist/cjs/hooks/useDevMode.cjs" + "./useEnvironment": { + "types": "./dist/types/hooks/useEnvironment.d.ts", + "import": "./dist/es/hooks/useEnvironment.mjs", + "default": "./dist/cjs/hooks/useEnvironment.cjs" }, "./useERC20TokenPrice": { "types": "./dist/types/hooks/useERC20TokenPrice.d.ts", @@ -129,11 +129,6 @@ "import": "./dist/es/hooks/usePreferDecentralization.mjs", "default": "./dist/cjs/hooks/usePreferDecentralization.cjs" }, - "./useProdMode": { - "types": "./dist/types/hooks/useProdMode.d.ts", - "import": "./dist/es/hooks/useProdMode.mjs", - "default": "./dist/cjs/hooks/useProdMode.cjs" - }, "./useResolvedKPIToken": { "types": "./dist/types/hooks/useResolvedKPIToken.d.ts", "import": "./dist/es/hooks/useResolvedKPIToken.mjs", @@ -154,10 +149,10 @@ "import": "./dist/es/hooks/useResolvedTemplates.mjs", "default": "./dist/cjs/hooks/useResolvedTemplates.cjs" }, - "./useSetDevMode": { - "types": "./dist/types/hooks/useSetDevMode.d.ts", - "import": "./dist/es/hooks/useSetDevMode.mjs", - "default": "./dist/cjs/hooks/useSetDevMode.cjs" + "./useSetEnvironment": { + "types": "./dist/types/hooks/useSetEnvironment.d.ts", + "import": "./dist/es/hooks/useSetEnvironment.mjs", + "default": "./dist/cjs/hooks/useSetEnvironment.cjs" }, "./useSetIPFSGatewayURL": { "types": "./dist/types/hooks/useSetIPFSGatewayURL.d.ts", @@ -245,8 +240,8 @@ "template-component": [ "./dist/types/components/template-component.d.ts" ], - "useDevMode": [ - "./dist/types/hooks/useDevMode.d.ts" + "useEnvironment": [ + "./dist/types/hooks/useEnvironment.d.ts" ], "useERC20TokenPrice": [ "./dist/types/hooks/useERC20TokenPrice.d.ts" @@ -290,9 +285,6 @@ "usePreferDecentralization": [ "./dist/types/hooks/usePreferDecentralization.d.ts" ], - "useProdMode": [ - "./dist/types/hooks/useProdMode.d.ts" - ], "useResolvedKPIToken": [ "./dist/types/hooks/useResolvedKPIToken.d.ts" ], @@ -305,8 +297,8 @@ "useResolvedTemplates": [ "./dist/types/hooks/useResolvedTemplates.d.ts" ], - "useSetDevMode": [ - "./dist/types/hooks/useSetDevMode.d.ts" + "useSetEnvironment": [ + "./dist/types/hooks/useSetEnvironment.d.ts" ], "useSetIPFSGatewayURL": [ "./dist/types/hooks/useSetIPFSGatewayURL.d.ts" diff --git a/packages/react/rollup.config.mjs b/packages/react/rollup.config.mjs index b1c693bd0..63b2e5280 100644 --- a/packages/react/rollup.config.mjs +++ b/packages/react/rollup.config.mjs @@ -13,7 +13,7 @@ export default [ "src/components/oracle-creation-form.tsx", "src/components/oracle-page.tsx", "src/components/template-component.tsx", - "src/hooks/useDevMode.ts", + "src/hooks/useEnvironment.ts", "src/hooks/useERC20TokenPrice.ts", "src/hooks/useIPFSGatewayURL.ts", "src/hooks/useJSONUploader.ts", @@ -27,11 +27,10 @@ export default [ "src/hooks/useOracleTemplates.ts", "src/hooks/usePagination.ts", "src/hooks/usePreferDecentralization.ts", - "src/hooks/useProdMode.ts", "src/hooks/useResolvedKPIToken.ts", "src/hooks/useResolvedTemplate.ts", "src/hooks/useResolvedTemplates.ts", - "src/hooks/useSetDevMode.ts", + "src/hooks/useSetEnvironment.ts", "src/hooks/useSetIPFSGatewayURL.ts", "src/hooks/useSetKPITokenTemplateBaseURL.ts", "src/hooks/useSetOracleTemplateBaseURL.ts", diff --git a/packages/react/src/hooks/useDevMode.ts b/packages/react/src/hooks/useDevMode.ts deleted file mode 100644 index 4d1050a33..000000000 --- a/packages/react/src/hooks/useDevMode.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { type State, useSelector } from "@carrot-kpi/shared-state"; - -export const useDevMode = () => { - return useSelector( - (state) => state.preferences.devMode, - ); -}; diff --git a/packages/react/src/hooks/useEnvironment.ts b/packages/react/src/hooks/useEnvironment.ts new file mode 100644 index 000000000..cc47d3814 --- /dev/null +++ b/packages/react/src/hooks/useEnvironment.ts @@ -0,0 +1,7 @@ +import { type State, useSelector } from "@carrot-kpi/shared-state"; + +export const useEnvironment = () => { + return useSelector( + (state) => state.preferences.environment, + ); +}; diff --git a/packages/react/src/hooks/useJSONUploader.ts b/packages/react/src/hooks/useJSONUploader.ts index 79d512bbd..61ab313b7 100644 --- a/packages/react/src/hooks/useJSONUploader.ts +++ b/packages/react/src/hooks/useJSONUploader.ts @@ -1,8 +1,8 @@ import { useSelector, type State } from "@carrot-kpi/shared-state"; +import { SERVICE_URLS } from "@carrot-kpi/sdk"; import type { SerializableObject } from "../types/templates"; import { useCallback } from "react"; -import { useDevMode } from "./useDevMode"; -import { useAccount } from "wagmi"; +import { useEnvironment } from "./useEnvironment"; export type JsonUploader> = ( content: S, @@ -11,11 +11,10 @@ export type JsonUploader> = ( export const useJSONUploader = < S extends SerializableObject, >(): JsonUploader => { - const devMode = useDevMode(); + const environment = useEnvironment(); const dataManagerJWT = useSelector( (state) => state.auth.dataManagerJWT, ); - const { chain } = useAccount(); const localUploader = useCallback(async (content: S): Promise => { const formData = new FormData(); @@ -42,9 +41,8 @@ export const useJSONUploader = < const remoteUploader = useCallback( async (data: S): Promise => { - if (!chain) throw new Error("undefined chain"); const response = await fetch( - `${chain.serviceUrls.dataManager}/data/s3/json`, + `${SERVICE_URLS[environment].dataManager}/data/s3/json`, { method: "POST", headers: { @@ -62,8 +60,8 @@ export const useJSONUploader = < const { cid } = (await response.json()) as { cid: string }; return cid; }, - [dataManagerJWT, chain], + [dataManagerJWT, environment], ); - return devMode ? localUploader : remoteUploader; + return environment === "local" ? localUploader : remoteUploader; }; diff --git a/packages/react/src/hooks/useProdMode.ts b/packages/react/src/hooks/useProdMode.ts deleted file mode 100644 index eb20a284a..000000000 --- a/packages/react/src/hooks/useProdMode.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useTemplatePreviewMode } from "./useTemplatePreviewMode"; -import { useDevMode } from "./useDevMode"; - -export const useProdMode = () => { - const devMode = useDevMode(); - const templatePreviewMode = useTemplatePreviewMode(); - - return !devMode && !templatePreviewMode; -}; diff --git a/packages/react/src/hooks/useResolvedKPIToken.ts b/packages/react/src/hooks/useResolvedKPIToken.ts index 1c91ecd86..f196d6fdb 100644 --- a/packages/react/src/hooks/useResolvedKPIToken.ts +++ b/packages/react/src/hooks/useResolvedKPIToken.ts @@ -40,7 +40,6 @@ export function useResolvedKPIToken(params?: ResolvedKPITokenParams): { const resolved = ( await Fetcher.resolveKPITokens({ ipfsGatewayURL, - dataCDNURL: chain.serviceUrls.dataCdn, preferDecentralization, kpiTokens: [params.kpiToken], }) diff --git a/packages/react/src/hooks/useResolvedKPITokens.ts b/packages/react/src/hooks/useResolvedKPITokens.ts index f60e68afe..9b218832d 100644 --- a/packages/react/src/hooks/useResolvedKPITokens.ts +++ b/packages/react/src/hooks/useResolvedKPITokens.ts @@ -42,8 +42,6 @@ export function useResolvedKPITokens(params?: ResolvedKPITokensParams): { const resolved = ( await Fetcher.resolveKPITokens({ ipfsGatewayURL, - dataCDNURL: - publicClient.chain.serviceUrls.dataCdn, preferDecentralization, kpiTokens: [kpiToken], }) diff --git a/packages/react/src/hooks/useResolvedTemplate.ts b/packages/react/src/hooks/useResolvedTemplate.ts index 51c17fc70..6e8123c71 100644 --- a/packages/react/src/hooks/useResolvedTemplate.ts +++ b/packages/react/src/hooks/useResolvedTemplate.ts @@ -28,7 +28,6 @@ export function useResolvedTemplate(params?: ResolvedTemplateParams): { try { const resolved = await Fetcher.resolveTemplates({ ipfsGatewayURL, - dataCDNURL: chain.serviceUrls.dataCdn, preferDecentralization, templates: [params.template], }); diff --git a/packages/react/src/hooks/useResolvedTemplates.ts b/packages/react/src/hooks/useResolvedTemplates.ts index d4e629f32..2ef82e15e 100644 --- a/packages/react/src/hooks/useResolvedTemplates.ts +++ b/packages/react/src/hooks/useResolvedTemplates.ts @@ -30,7 +30,6 @@ export function useResolvedTemplates(params?: ResolvedTemplatesParams): { try { const resolved = await Fetcher.resolveTemplates({ ipfsGatewayURL, - dataCDNURL: chain.serviceUrls.dataCdn, preferDecentralization, templates: params.templates, }); diff --git a/packages/react/src/hooks/useSetDevMode.ts b/packages/react/src/hooks/useSetDevMode.ts deleted file mode 100644 index ca4ab67c1..000000000 --- a/packages/react/src/hooks/useSetDevMode.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { setDevMode, useDispatch } from "@carrot-kpi/shared-state"; - -export const useSetDevMode = () => { - const dispatch = useDispatch(); - - return (devMode: boolean) => { - dispatch(setDevMode(devMode)); - }; -}; diff --git a/packages/react/src/hooks/useSetEnvironment.ts b/packages/react/src/hooks/useSetEnvironment.ts new file mode 100644 index 000000000..3201c365d --- /dev/null +++ b/packages/react/src/hooks/useSetEnvironment.ts @@ -0,0 +1,13 @@ +import { + Environment, + setEnvironment, + useDispatch, +} from "@carrot-kpi/shared-state"; + +export const useSetEnvironment = () => { + const dispatch = useDispatch(); + + return (environment: Environment) => { + dispatch(setEnvironment(environment)); + }; +}; diff --git a/packages/react/src/hooks/useTemplateModule.ts b/packages/react/src/hooks/useTemplateModule.ts index 2c455eb30..99c44447e 100644 --- a/packages/react/src/hooks/useTemplateModule.ts +++ b/packages/react/src/hooks/useTemplateModule.ts @@ -8,9 +8,8 @@ import { useState, } from "react"; import { useFederatedModuleContainer } from "./useFederatedModuleContainer"; -import { type State, useSelector } from "@carrot-kpi/shared-state"; +import { type State, useSelector, Environment } from "@carrot-kpi/shared-state"; import { useIPFSGatewayURL } from "./useIPFSGatewayURL"; -import { useAccount } from "wagmi"; import type { RemoteComponentProps, SerializableObject, @@ -18,6 +17,7 @@ import type { TemplateType, } from "../types/templates"; import { useTemplatePreviewMode } from "./useTemplatePreviewMode"; +import { useEnvironment } from "./useEnvironment"; type ComponentType< E extends TemplateEntity, @@ -64,24 +64,26 @@ export const useTemplateModule = < ? state.preferences.kpiTokenTemplateBaseURL : state.preferences.oracleTemplateBaseURL, ); - const { chain } = useAccount(); + const environment = useEnvironment(); const templatePreviewMode = useTemplatePreviewMode(); const ipfsGatewayURL = useIPFSGatewayURL(); const baseURL = useMemo(() => { - if (!template || !chain) return undefined; + if (!template) return undefined; let root: string; if (customBaseURL) root = customBaseURL; else if ( + environment !== Environment.Local && + environment !== Environment.Production && templatePreviewMode && - template.specification.previewUrl?.[chain.environment] + template.specification.previewUrl?.[environment] ) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - root = template.specification.previewUrl[chain.environment]!; + root = template.specification.previewUrl[environment]!; else root = `${ipfsGatewayURL}/ipfs/${template.specification.cid}`; return root.endsWith("/") ? `${root}${type}` : `${root}/${type}`; }, [ - chain, + environment, customBaseURL, templatePreviewMode, ipfsGatewayURL, diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 57d039918..614ccdcf4 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,7 +6,7 @@ export * from "./components/oracle-page"; export * from "./components/template-component"; export * from "./hooks/useJSONUploader"; -export * from "./hooks/useDevMode"; +export * from "./hooks/useEnvironment"; export * from "./hooks/useERC20TokenPrice"; export * from "./hooks/useIPFSGatewayURL"; export * from "./hooks/useKPIToken"; @@ -21,12 +21,11 @@ export * from "./hooks/useOracleTemplateFeatureEnabledFor"; export * from "./hooks/useOracleTemplates"; export * from "./hooks/usePagination"; export * from "./hooks/usePreferDecentralization"; -export * from "./hooks/useProdMode"; export * from "./hooks/useResolvedKPIToken"; export * from "./hooks/useResolvedKPITokens"; export * from "./hooks/useResolvedTemplate"; export * from "./hooks/useResolvedTemplates"; -export * from "./hooks/useSetDevMode"; +export * from "./hooks/useSetEnvironment"; export * from "./hooks/useSetIPFSGatewayURL"; export * from "./hooks/useSetKPITokenTemplateBaseURL"; export * from "./hooks/useSetOracleTemplateBaseURL"; diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 392a3964b..9259cafe2 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -156,6 +156,7 @@ }, "dependencies": { "@carrot-kpi/contracts": "^0.10.1", + "@carrot-kpi/shared-state": "*", "decimal.js-light": "^2.5.1", "multiformats": "^13.0.1", "viem": "^2.7.1" @@ -164,7 +165,6 @@ "@carrot-kpi/shared-state": "*" }, "devDependencies": { - "@carrot-kpi/shared-state": "*", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@size-limit/file": "^11.0.2", diff --git a/packages/sdk/src/commons.ts b/packages/sdk/src/commons.ts index e736ceaa7..958aebddd 100644 --- a/packages/sdk/src/commons.ts +++ b/packages/sdk/src/commons.ts @@ -13,36 +13,34 @@ import { } from "viem"; import { sepolia, arbitrumSepolia } from "viem/chains"; import { ChainId, DEPLOYMENT_ADDRESSES } from "@carrot-kpi/contracts"; +import { Environment } from "@carrot-kpi/shared-state"; // reexport chain id export { ChainId } from "@carrot-kpi/contracts"; export const CACHER = new Cacher("carrot-kpi-sdk"); -export enum Environment { - Development = "development", - Staging = "staging", -} - export interface ServiceUrls { - staticCdn: string; - dataCdn: string; dataManager: string; } export const SERVICE_URLS: Record = { + [Environment.Local]: { + dataManager: "https://data-manager.api.dev.carrot.community", + }, [Environment.Development]: { - staticCdn: "https://static.dev.carrot.community", - dataCdn: "https://data.dev.carrot.community", dataManager: "https://data-manager.api.dev.carrot.community", }, [Environment.Staging]: { - staticCdn: "https://static.staging.carrot.community", - dataCdn: "https://data.staging.carrot.community", dataManager: "https://data-manager.api.staging.carrot.community", }, + [Environment.Production]: { + dataManager: "https://data-manager.api.carrot.community", + }, }; +export const DATA_CDN_URL = "https://data.carrot.community"; + export interface SupportedChain extends Chain { contracts: { ensRegistry?: ChainContract; @@ -52,8 +50,7 @@ export interface SupportedChain extends Chain { kpiTokensManager: ChainContract; oraclesManager: ChainContract; }; - environment: Environment; - serviceUrls: ServiceUrls & { subgraph: string | null }; + subgraphUrl?: string; } export const SUPPORTED_CHAIN: Record = { @@ -63,12 +60,8 @@ export const SUPPORTED_CHAIN: Record = { ...sepolia.contracts, ...DEPLOYMENT_ADDRESSES[ChainId.Sepolia], }, - environment: Environment.Staging, - serviceUrls: { - ...SERVICE_URLS[Environment.Staging], - subgraph: - "https://api.thegraph.com/subgraphs/name/carrot-kpi/carrot-sepolia", - }, + subgraphUrl: + "https://api.thegraph.com/subgraphs/name/carrot-kpi/carrot-sepolia", }), [ChainId.ArbitrumSepolia]: defineChain({ ...arbitrumSepolia, @@ -76,12 +69,8 @@ export const SUPPORTED_CHAIN: Record = { ...arbitrumSepolia.contracts, ...DEPLOYMENT_ADDRESSES[ChainId.ArbitrumSepolia], }, - environment: Environment.Development, - serviceUrls: { - ...SERVICE_URLS[Environment.Development], - subgraph: - "https://api.thegraph.com/subgraphs/name/carrot-kpi/carrot-arbitrum-sepolia", - }, + subgraphUrl: + "https://api.thegraph.com/subgraphs/name/carrot-kpi/carrot-arbtirum-sepolia", }), }; diff --git a/packages/sdk/src/entities/template.ts b/packages/sdk/src/entities/template.ts index 4365d1050..735b7e194 100644 --- a/packages/sdk/src/entities/template.ts +++ b/packages/sdk/src/entities/template.ts @@ -1,5 +1,5 @@ import { type Address } from "viem"; -import type { Environment } from "../commons"; +import type { Environment } from "@carrot-kpi/shared-state"; export interface OnChainTemplate { addrezz: Address; diff --git a/packages/sdk/src/fetcher/abstraction.ts b/packages/sdk/src/fetcher/abstraction.ts index 496fa0cd9..8bb72f125 100644 --- a/packages/sdk/src/fetcher/abstraction.ts +++ b/packages/sdk/src/fetcher/abstraction.ts @@ -13,7 +13,6 @@ export interface DecentralizationParams { export interface CIDDataFetchingParams extends DecentralizationParams { ipfsGatewayURL: string; - dataCDNURL: string; } export interface FetchERC20TokensParams { diff --git a/packages/sdk/src/fetcher/core.ts b/packages/sdk/src/fetcher/core.ts index a9eb29b7d..0a344781c 100644 --- a/packages/sdk/src/fetcher/core.ts +++ b/packages/sdk/src/fetcher/core.ts @@ -1,4 +1,4 @@ -import { ERC20_ABI } from "../commons"; +import { DATA_CDN_URL, ERC20_ABI } from "../commons"; import { cacheERC20Token, getCachedERC20Token, @@ -163,7 +163,6 @@ export class CoreFetcher implements ICoreFetcher { public async fetchCIDData({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, cids, }: FetchCIDDataParams): Promise<{ [cid: string]: string }> { @@ -171,7 +170,7 @@ export class CoreFetcher implements ICoreFetcher { cids.map(async (cid) => { if (!preferDecentralization) { try { - const response = await fetch(`${dataCDNURL}/${cid}`); + const response = await fetch(`${DATA_CDN_URL}/${cid}`); const responseText = await response.text(); if (!response.ok) throw new Error(`response not ok: ${responseText}`); @@ -210,7 +209,6 @@ export class CoreFetcher implements ICoreFetcher { public async resolveTemplates({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, templates, }: ResolveTemplatesParams): Promise { @@ -221,7 +219,6 @@ export class CoreFetcher implements ICoreFetcher { const rawTemplateSpecification = ( await this.fetchCIDData({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, cids: [resolvedTemplateSpecificationCID], }) @@ -249,19 +246,16 @@ export class CoreFetcher implements ICoreFetcher { protected async resolveEntity({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, entity, }: { ipfsGatewayURL: string; - dataCDNURL: string; preferDecentralization?: boolean; entity: T; }): Promise { const template = ( await this.resolveTemplates({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, templates: [entity.template], }) @@ -280,7 +274,6 @@ export class CoreFetcher implements ICoreFetcher { ( await this.resolveTemplates({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, templates: [oracle.template], }) @@ -292,7 +285,6 @@ export class CoreFetcher implements ICoreFetcher { const resolvedKPITokenSpecification = ( await this.fetchCIDData({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, cids: [entity.specificationCID], }) @@ -312,7 +304,6 @@ export class CoreFetcher implements ICoreFetcher { public async resolveKPITokens({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, kpiTokens, }: ResolveKPITokensParams): Promise { @@ -320,7 +311,6 @@ export class CoreFetcher implements ICoreFetcher { kpiTokens.map(async (kpiToken) => this.resolveEntity({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, entity: kpiToken, }), @@ -338,7 +328,6 @@ export class CoreFetcher implements ICoreFetcher { public async resolveOracles({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, oracles, }: ResolveOraclesParams): Promise { @@ -346,7 +335,6 @@ export class CoreFetcher implements ICoreFetcher { oracles.map(async (oracle) => this.resolveEntity({ ipfsGatewayURL, - dataCDNURL, preferDecentralization, entity: oracle, }), diff --git a/packages/sdk/src/fetcher/subgraph/index.ts b/packages/sdk/src/fetcher/subgraph/index.ts index 1f1c20430..4043e0a36 100644 --- a/packages/sdk/src/fetcher/subgraph/index.ts +++ b/packages/sdk/src/fetcher/subgraph/index.ts @@ -89,7 +89,7 @@ const mapRawKPIToken = (chainId: ChainId, rawKPIToken: KPITokenData) => { class Fetcher implements IPartialCarrotFetcher { public supportedInChain({ chain }: SupportedInChainParams): boolean { - return !!chain.serviceUrls.subgraph; + return !!chain.subgraphUrl; } public async fetchKPITokensAmount({ @@ -515,10 +515,10 @@ class Fetcher implements IPartialCarrotFetcher { ): Promise<{ subgraphUrl: string; chain: SupportedChain }> { const chain = await validateChainId(publicClient); enforce( - chain.serviceUrls.subgraph !== null, + !!chain.subgraphUrl, `no subgraph available in chain ${chain.id}`, ); - return { subgraphUrl: chain.serviceUrls.subgraph, chain }; + return { subgraphUrl: chain.subgraphUrl, chain }; } } diff --git a/packages/shared-state/package.json b/packages/shared-state/package.json index 2fba61189..ac4e6ea12 100644 --- a/packages/shared-state/package.json +++ b/packages/shared-state/package.json @@ -4,7 +4,7 @@ "version": "0.17.1", "license": "GPL-3.0-or-later", "main": "./dist/cjs/index.cjs", - "module": "./dist/mjs/index.mjs", + "module": "./dist/es/index.mjs", "types": "./dist/types/index.d.ts", "exports": { ".": { diff --git a/packages/shared-state/src/reducers/preferences/actions/index.ts b/packages/shared-state/src/reducers/preferences/actions/index.ts index 6b71b3a46..7711db135 100644 --- a/packages/shared-state/src/reducers/preferences/actions/index.ts +++ b/packages/shared-state/src/reducers/preferences/actions/index.ts @@ -13,8 +13,8 @@ export const setIPFSGatewayURL = createAction< PreferencesState["ipfsGatewayURL"] >("preferences/setIPFSGatewayURL"); -export const setDevMode = createAction( - "preferences/setDevMode", +export const setEnvironment = createAction( + "preferences/setEnvironment", ); export const setTemplatePreviewMode = createAction< diff --git a/packages/shared-state/src/reducers/preferences/reducer/index.ts b/packages/shared-state/src/reducers/preferences/reducer/index.ts index 73ad1de3c..a1bde9535 100644 --- a/packages/shared-state/src/reducers/preferences/reducer/index.ts +++ b/packages/shared-state/src/reducers/preferences/reducer/index.ts @@ -1,6 +1,6 @@ import { createReducer } from "@reduxjs/toolkit"; import { - setDevMode, + setEnvironment, setIPFSGatewayURL, setKPITokenTemplateBaseURL, setOracleTemplateBaseURL, @@ -8,14 +8,14 @@ import { setTemplatePreviewMode, setTheme, } from "../actions"; -import type { PreferencesState } from "../types"; +import { Environment, type PreferencesState } from "../types"; const initialState: PreferencesState = { // TODO: use system as a default once the dark theme is available theme: "light", preferDecentralization: false, ipfsGatewayURL: "https://gateway.api.carrot.community", - devMode: false, + environment: Environment.Development, templatePreviewMode: false, kpiTokenTemplateBaseURL: undefined, oracleTemplateBaseURL: undefined, @@ -32,8 +32,8 @@ export const preferencesReducer = createReducer(initialState, (builder) => .addCase(setIPFSGatewayURL, (state, action) => { state.ipfsGatewayURL = action.payload; }) - .addCase(setDevMode, (state, action) => { - state.devMode = action.payload; + .addCase(setEnvironment, (state, action) => { + state.environment = action.payload; }) .addCase(setTemplatePreviewMode, (state, action) => { state.templatePreviewMode = action.payload; diff --git a/packages/shared-state/src/reducers/preferences/types/index.ts b/packages/shared-state/src/reducers/preferences/types/index.ts index 2c9569e39..1e501c64d 100644 --- a/packages/shared-state/src/reducers/preferences/types/index.ts +++ b/packages/shared-state/src/reducers/preferences/types/index.ts @@ -1,3 +1,10 @@ +export enum Environment { + Local = "local", + Development = "development", + Staging = "staging", + Production = "production", +} + export type Theme = "dark" | "light" | "system"; export interface PreferencesState { @@ -5,9 +12,9 @@ export interface PreferencesState { preferDecentralization: boolean; ipfsGatewayURL: string; - // templates/libs will know when the platform is being run locally - // in order to develop templates, and will be able to act accordingly - devMode: boolean; + // templates/libs will know in which environment the platform is being run + // and will be able to act accordingly + environment: Environment; // templates/libs will know when the platform is being run in staging // mode and will be able to load templates from their staging deployments diff --git a/packages/ui/stories/erc20-token-picker.stories.tsx b/packages/ui/stories/erc20-token-picker.stories.tsx index 1b824eb12..2ebe55c51 100644 --- a/packages/ui/stories/erc20-token-picker.stories.tsx +++ b/packages/ui/stories/erc20-token-picker.stories.tsx @@ -20,7 +20,12 @@ import { import { injected } from "wagmi/connectors"; import { http, type Address } from "viem"; import { Typography } from "../src/components/typography"; -import { ChainId, ERC20_ABI, SUPPORTED_CHAIN } from "@carrot-kpi/sdk"; +import { + ChainId, + DATA_CDN_URL, + ERC20_ABI, + SUPPORTED_CHAIN, +} from "@carrot-kpi/sdk"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const injectedConnector = injected(); @@ -109,7 +114,7 @@ const Component = (props: ERC20TokenPickerProps) => { let cancelled = false; const fetchData = async () => { let response = await fetch( - `${SUPPORTED_CHAIN[ChainId.Sepolia].serviceUrls.staticCdn}/token-list.json`, + `${DATA_CDN_URL}/static/token-list.json`, ); if (!response.ok) { console.warn("could not fetch carrot token list"); diff --git a/turbo.json b/turbo.json index 15cb92c38..150bff50b 100644 --- a/turbo.json +++ b/turbo.json @@ -26,6 +26,7 @@ "outputs": ["storybook-static/**"] }, "test": { + "dependsOn": ["^build"], "inputs": [ "src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}",