diff --git a/apps/maestro/src/features/CanonicalTokenDeployment/CanonicalTokenDeployment.tsx b/apps/maestro/src/features/CanonicalTokenDeployment/CanonicalTokenDeployment.tsx index 7165b7c38..401571148 100644 --- a/apps/maestro/src/features/CanonicalTokenDeployment/CanonicalTokenDeployment.tsx +++ b/apps/maestro/src/features/CanonicalTokenDeployment/CanonicalTokenDeployment.tsx @@ -1,6 +1,9 @@ +import { Alert, InfoIcon } from "@axelarjs/ui"; import { useMemo, type FC } from "react"; import dynamic from "next/dynamic"; +import { useChainFromRoute } from "~/lib/hooks"; +import { useGetChainsConfig } from "~/services/axelarConfigs/hooks"; import { MultiStepDialog, StepLoading } from "~/ui/compounds/MultiStepForm"; import { CanonicalTokenDeploymentStateProvider, @@ -24,6 +27,20 @@ const STEPS = [Step1, Step2, Step3]; const CanonicalTokenDeployment: FC = () => { const { state, actions } = useCanonicalTokenDeploymentStateContainer(); + const routeChain = useChainFromRoute(); + const { data: tokenInfo } = useGetChainsConfig({ + axelarChainId: routeChain?.axelarChainId, + }); + + const isGatewayToken = useMemo( + () => + ( + tokenInfo?.assets.map((asset: any) => + asset.tokenAddress.toLowerCase() + ) || [] + ).includes(state.tokenDetails.tokenAddress.toLowerCase()), + [tokenInfo, state.tokenDetails.tokenAddress] + ); const CurrentStep = useMemo(() => STEPS[state.step], [state.step]); @@ -32,6 +49,14 @@ const CanonicalTokenDeployment: FC = () => { [state.step] ); + if (isGatewayToken) + return ( + }> + This token is supported directly on Axelar and cannot be registered in + ITS. + + ); + return ( { const axelarQueryClient = createAxelarQueryClient(NEXT_PUBLIC_NETWORK_ENV); + const axelarConfigClient = createAxelarConfigClient(NEXT_PUBLIC_NETWORK_ENV); + return { req, res, @@ -74,6 +78,12 @@ const createContextInner = async ({ req, res }: ContextConfig) => { axelarscanClient, "evmChains" as const ), + axelarConfigs: axelarConfigs.bind( + null, + maestroKVClient, + axelarConfigClient, + "axelarConfigs" as const + ), wagmiChainConfigs: WAGMI_CHAIN_CONFIGS, }, persistence: { @@ -186,3 +196,24 @@ async function evmChains( return evmChainsMap; } + +async function axelarConfigs( + kvClient: MaestroKVClient, + axelarConfigClient: AxelarConfigClient, + cacheKey: TCacheKey +): Promise { + const chainConfigs = await axelarConfigClient.getChainConfigs( + NEXT_PUBLIC_NETWORK_ENV + ); + + const cached = await kvClient.getCached(cacheKey); + + if (cached) { + return cached; + } + + // cache for 1 hour + await kvClient.setCached(cacheKey, chainConfigs, 3600); + + return chainConfigs; +} diff --git a/apps/maestro/src/server/routers/_app.ts b/apps/maestro/src/server/routers/_app.ts index c8aea5728..904c5fed8 100644 --- a/apps/maestro/src/server/routers/_app.ts +++ b/apps/maestro/src/server/routers/_app.ts @@ -1,5 +1,6 @@ import { publicProcedure, router } from "~/server/trpc"; import { authRouter } from "./auth"; +import { axelarConfigsRouter } from "./axelarConfigs"; import { axelarjsSDKRouter } from "./axelarjsSDK"; import { axelarscanRouter } from "./axelarscan"; import { erc20Router } from "./erc20"; @@ -16,6 +17,7 @@ export const appRouter = router({ axelarscan: axelarscanRouter, erc20: erc20Router, axelarjsSDK: axelarjsSDKRouter, + axelarConfigs: axelarConfigsRouter, interchainToken: interchainTokenRouter, auth: authRouter, openai: openaiRouter, diff --git a/apps/maestro/src/server/routers/axelarConfigs/getChainConfigs.ts b/apps/maestro/src/server/routers/axelarConfigs/getChainConfigs.ts new file mode 100644 index 000000000..648ed0c98 --- /dev/null +++ b/apps/maestro/src/server/routers/axelarConfigs/getChainConfigs.ts @@ -0,0 +1,37 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; + +import { publicProcedure } from "~/server/trpc"; + +export const getConfigForChain = publicProcedure + .meta({ + openapi: { + summary: "Get the full configs for a chain on the Axelar network", + description: + "Get the full configs for a chain on the Axelar network, including the assets registered directly on the network", + method: "GET", + path: "/axelar-chain-configs", + tags: ["axelar-chain-configs"], + }, + }) + .input( + z.object({ + axelarChainId: z.string().max(64), + }) + ) + .output(z.any()) + .query(async ({ ctx, input }) => { + try { + return (await ctx.configs.axelarConfigs()).chains[input.axelarChainId]; + } catch (error) { + // If we get a TRPC error, we throw it + if (error instanceof TRPCError) { + throw error; + } + // otherwise, we throw an internal server error + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to get chain configs", + }); + } + }); diff --git a/apps/maestro/src/server/routers/axelarConfigs/index.ts b/apps/maestro/src/server/routers/axelarConfigs/index.ts new file mode 100644 index 000000000..b22853021 --- /dev/null +++ b/apps/maestro/src/server/routers/axelarConfigs/index.ts @@ -0,0 +1,8 @@ +import { router } from "~/server/trpc"; +import { getConfigForChain } from "./getChainConfigs"; + +export const axelarConfigsRouter = router({ + getChainConfigs: getConfigForChain, +}); + +export type AxelarConfigsRouter = typeof axelarConfigsRouter; diff --git a/apps/maestro/src/services/axelarConfigs/hooks.ts b/apps/maestro/src/services/axelarConfigs/hooks.ts new file mode 100644 index 000000000..9f657f174 --- /dev/null +++ b/apps/maestro/src/services/axelarConfigs/hooks.ts @@ -0,0 +1,16 @@ +import { Maybe } from "@axelarjs/utils"; + +import { trpc } from "~/lib/trpc"; + +export function useGetChainsConfig(input: { axelarChainId?: string }) { + return trpc.axelarConfigs.getChainConfigs.useQuery( + { + axelarChainId: Maybe.of(input.axelarChainId).mapOr("", String), + }, + { + enabled: Boolean(input.axelarChainId), + staleTime: 1000 * 60 * 60 * 24, // 24 hours + refetchOnWindowFocus: false, + } + ); +}