diff --git a/CHANGELOG.md b/CHANGELOG.md index 321ce286c..028d8efe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,6 +128,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements +- [#341](https://github.com/alleslabs/celatone-frontend/pull/341) Apply faucet info from chain config - [#234](https://github.com/alleslabs/celatone-frontend/pull/234) Fix faucet wording - [#216](https://github.com/alleslabs/celatone-frontend/pull/216) Change icon to Alles Labs icon set - [#227](https://github.com/alleslabs/celatone-frontend/pull/227) Refactor directory structure and components e.g. various tables diff --git a/src/config/index.ts b/src/config/index.ts index 56e781f1f..01a126785 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -89,6 +89,8 @@ export const CHAIN_CONFIGS: ChainConfigs = { faucet: { enabled: true, url: "https://faucet.alleslabs.dev", + denom: "osmo", + amount: 10, }, wasm: { enabled: true, diff --git a/src/config/types.ts b/src/config/types.ts index 05fb3ff18..d57e9d8a8 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -4,6 +4,8 @@ type FaucetConfig = | { enabled: true; url: string; + denom: string; + amount: number; } | { enabled: false }; diff --git a/src/lib/app-provider/contexts/app.tsx b/src/lib/app-provider/contexts/app.tsx index 6c3b2d521..5c4a68cc4 100644 --- a/src/lib/app-provider/contexts/app.tsx +++ b/src/lib/app-provider/contexts/app.tsx @@ -1,7 +1,6 @@ import { useWallet } from "@cosmos-kit/react"; import { GraphQLClient } from "graphql-request"; import { observer } from "mobx-react-lite"; -import { useRouter } from "next/router"; import type { ReactNode } from "react"; import { useCallback, @@ -13,11 +12,10 @@ import { } from "react"; import { useAmplitude } from "../hooks/useAmplitude"; -import { useInternalNavigate } from "../hooks/useInternalNavigate"; import { useNetworkChange } from "../hooks/useNetworkChange"; import { CHAIN_CONFIGS, DEFAULT_CHAIN_CONFIG, PROJECT_CONSTANTS } from "config"; import type { ChainConfig, ProjectConstants } from "config/types"; -import { DEFAULT_SUPPORTED_CHAIN_ID, SUPPORTED_CHAIN_IDS } from "env"; +import { SUPPORTED_CHAIN_IDS } from "env"; import { LoadingOverlay } from "lib/components/LoadingOverlay"; import { DEFAULT_ADDRESS } from "lib/data"; import { @@ -50,53 +48,38 @@ const AppContext = createContext({ }); export const AppProvider = observer(({ children }: AppProviderProps) => { - const router = useRouter(); - const navigate = useInternalNavigate(); const { currentChainName, setCurrentChain } = useWallet(); const { setCodeUserKey, isCodeUserKeyExist } = useCodeStore(); const { setContractUserKey, isContractUserKeyExist } = useContractStore(); const { setProjectUserKey, isProjectUserKeyExist } = usePublicProjectStore(); - const [currentChainId, setCurrentChainId] = useState( - DEFAULT_SUPPORTED_CHAIN_ID - ); + const [currentChainId, setCurrentChainId] = useState(""); + // Remark: this function is only used in useSelectChain. Do not use in other places. const handleOnChainIdChange = useCallback( (newChainId: string) => { const config = CHAIN_CONFIGS[newChainId]; setCurrentChainId(newChainId); setCurrentChain(config?.registryChainName); - - navigate({ - pathname: router.pathname.replace("/[network]", ""), - query: { - ...router.query, - network: newChainId, - }, - }); }, - [navigate, router, setCurrentChain] + [setCurrentChain, setCurrentChainId] ); - const chainConfig = CHAIN_CONFIGS[currentChainId]; + const states = useMemo(() => { + const chainConfig = currentChainId + ? CHAIN_CONFIGS[currentChainId] + : DEFAULT_CHAIN_CONFIG; - const indexerGraphClient = useMemo( - () => new GraphQLClient(chainConfig.indexer), - [chainConfig.indexer] - ); - - const states = useMemo( - () => ({ + return { availableChainIds: SUPPORTED_CHAIN_IDS, currentChainId, chainConfig, - indexerGraphClient, + indexerGraphClient: new GraphQLClient(chainConfig.indexer), constants: PROJECT_CONSTANTS, handleOnChainIdChange, - }), - [chainConfig, currentChainId, handleOnChainIdChange, indexerGraphClient] - ); + }; + }, [currentChainId, handleOnChainIdChange]); useEffect(() => { if (currentChainName) { @@ -114,7 +97,8 @@ export const AppProvider = observer(({ children }: AppProviderProps) => { if ( !isCodeUserKeyExist() || !isContractUserKeyExist() || - !isProjectUserKeyExist() + !isProjectUserKeyExist() || + !currentChainId ) return ; diff --git a/src/lib/app-provider/hooks/index.ts b/src/lib/app-provider/hooks/index.ts index 1cd1d7a76..ccb93f811 100644 --- a/src/lib/app-provider/hooks/index.ts +++ b/src/lib/app-provider/hooks/index.ts @@ -13,4 +13,5 @@ export * from "./useTokensInfo"; export * from "./useCurrentNetwork"; export * from "./useBaseApiRoute"; export * from "./useRPCEndpoint"; +export * from "./useFaucetConfig"; export * from "./useWasmConfig"; diff --git a/src/lib/app-provider/hooks/useFaucetConfig.ts b/src/lib/app-provider/hooks/useFaucetConfig.ts new file mode 100644 index 000000000..a8310b21c --- /dev/null +++ b/src/lib/app-provider/hooks/useFaucetConfig.ts @@ -0,0 +1,11 @@ +import { useCelatoneApp } from "../contexts"; + +export const useFaucetConfig = () => { + const { + chainConfig: { + features: { faucet }, + }, + } = useCelatoneApp(); + + return faucet; +}; diff --git a/src/lib/app-provider/hooks/useNetworkChange.ts b/src/lib/app-provider/hooks/useNetworkChange.ts index 672f1d194..76c512961 100644 --- a/src/lib/app-provider/hooks/useNetworkChange.ts +++ b/src/lib/app-provider/hooks/useNetworkChange.ts @@ -11,12 +11,11 @@ export const useNetworkChange = ( const networkRef = useRef(); useEffect(() => { - if (router.isReady) { - let networkRoute = getFirstQueryParam( - router.query.network, - DEFAULT_SUPPORTED_CHAIN_ID - ); + let networkRoute = router.query.network + ? getFirstQueryParam(router.query.network, DEFAULT_SUPPORTED_CHAIN_ID) + : router.asPath.split("/")[1]; + if (router.isReady || router.pathname === "/404") { // Redirect to default chain if the chain is not supported by the app if (!SUPPORTED_CHAIN_IDS.includes(networkRoute)) networkRoute = DEFAULT_SUPPORTED_CHAIN_ID; @@ -26,5 +25,11 @@ export const useNetworkChange = ( handleOnChainIdChange(networkRoute); } } - }, [handleOnChainIdChange, router.isReady, router.query.network]); + }, [ + handleOnChainIdChange, + router.asPath, + router.isReady, + router.pathname, + router.query.network, + ]); }; diff --git a/src/lib/app-provider/hooks/useSelectChain.ts b/src/lib/app-provider/hooks/useSelectChain.ts index f455b293e..d6b70be41 100644 --- a/src/lib/app-provider/hooks/useSelectChain.ts +++ b/src/lib/app-provider/hooks/useSelectChain.ts @@ -3,16 +3,28 @@ import { useCallback } from "react"; import { useCelatoneApp } from "../contexts/app"; +import { useInternalNavigate } from "./useInternalNavigate"; + export const useSelectChain = () => { const router = useRouter(); + const navigator = useInternalNavigate(); const { handleOnChainIdChange } = useCelatoneApp(); return useCallback( (chainId: string) => { if (router.query.network === chainId) return; + // Remark: This is workaround solution to set new chainId and replace the pathname handleOnChainIdChange(chainId); + + navigator({ + pathname: router.pathname.replace("/[network]", ""), + query: { + ...router.query, + network: chainId, + }, + }); }, - [handleOnChainIdChange, router.query.network] + [handleOnChainIdChange, navigator, router.pathname, router.query] ); }; diff --git a/src/lib/app-provider/hooks/useWasmConfig.ts b/src/lib/app-provider/hooks/useWasmConfig.ts index aa7cff64d..60c19e6f3 100644 --- a/src/lib/app-provider/hooks/useWasmConfig.ts +++ b/src/lib/app-provider/hooks/useWasmConfig.ts @@ -7,11 +7,5 @@ export const useWasmConfig = () => { }, } = useCelatoneApp(); - if (!wasm.enabled) { - throw new Error( - "Cannot access Wasm configs when Wasm feature is disabled." - ); - } - return wasm; }; diff --git a/src/lib/app-provider/tx/clearAdmin.ts b/src/lib/app-provider/tx/clearAdmin.ts index 5713b907e..ab749f505 100644 --- a/src/lib/app-provider/tx/clearAdmin.ts +++ b/src/lib/app-provider/tx/clearAdmin.ts @@ -16,14 +16,19 @@ export const useClearAdminTx = (contractAddress: ContractAddr) => { const fabricateFee = useFabricateFee(); const wasm = useWasmConfig(); - const clearAdminFee = fabricateFee(wasm.clearAdminGas); - return useCallback( async ({ onTxSucceed }: ClearAdminStreamParams) => { const client = await getCosmWasmClient(); if (!address || !client) throw new Error("Please check your wallet connection."); + if (!wasm.enabled) + throw new Error( + "Wasm config isn't loaded or Wasm feature is disabled." + ); + + const clearAdminFee = fabricateFee(wasm.clearAdminGas); + return clearAdminTx({ address: address as HumanAddr, contractAddress, @@ -42,6 +47,13 @@ export const useClearAdminTx = (contractAddress: ContractAddr) => { }, }); }, - [address, clearAdminFee, queryClient, contractAddress, getCosmWasmClient] + [ + getCosmWasmClient, + address, + wasm, + fabricateFee, + contractAddress, + queryClient, + ] ); }; diff --git a/src/lib/components/button/FaucetButton.tsx b/src/lib/components/button/FaucetButton.tsx index b5c4ec7ab..ae71e9f6d 100644 --- a/src/lib/components/button/FaucetButton.tsx +++ b/src/lib/components/button/FaucetButton.tsx @@ -2,11 +2,17 @@ import { Button, Text } from "@chakra-ui/react"; import type { MouseEventHandler } from "react"; import { CustomIcon } from "../icon"; -import { useCurrentNetwork, useInternalNavigate } from "lib/app-provider"; +import { useCelatoneApp, useInternalNavigate } from "lib/app-provider"; export const FaucetBtn = () => { const navigate = useInternalNavigate(); - const { isTestnet } = useCurrentNetwork(); + const { + chainConfig: { + features: { + faucet: { enabled }, + }, + }, + } = useCelatoneApp(); const onClick: MouseEventHandler = async (e) => { e.preventDefault(); @@ -15,7 +21,7 @@ export const FaucetBtn = () => { }); }; - return isTestnet ? ( + return enabled ? ( {result.status && ( diff --git a/src/lib/utils/formatter/text.ts b/src/lib/utils/formatter/text.ts index 87d32ff52..d4da03649 100644 --- a/src/lib/utils/formatter/text.ts +++ b/src/lib/utils/formatter/text.ts @@ -1,2 +1,5 @@ +export const capitalize = (text: string) => + text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(); + export const removeSpecialChars = (text: string) => text.replace(/[^a-zA-Z0-9]/g, "");