Skip to content

Commit

Permalink
feat: add flow blockchain (#484)
Browse files Browse the repository at this point in the history
Co-authored-by: npty <[email protected]>
  • Loading branch information
SGiaccobasso and npty authored Jan 15, 2025
1 parent 41ea803 commit 1d05d56
Show file tree
Hide file tree
Showing 62 changed files with 1,317 additions and 499 deletions.
6 changes: 6 additions & 0 deletions .changeset/clever-bulldogs-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@axelarjs/maestro": patch
"@axelarjs/evm": patch
---

add flow support to portal
2 changes: 1 addition & 1 deletion apps/maestro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"release": "tsx scripts/release.ts"
},
"dependencies": {
"@axelar-network/axelarjs-sdk": "0.15.0",
"@axelar-network/axelarjs-sdk": "0.17.1-alpha.12",
"@axelarjs/api": "workspace:*",
"@axelarjs/core": "workspace:*",
"@axelarjs/evm": "workspace:*",
Expand Down
Binary file added apps/maestro/public/logos/chains/flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions apps/maestro/public/logos/chains/flow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions apps/maestro/src/config/evm-chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
fantomTestnet,
filecoin,
filecoinCalibration,
flowMainnet,
flowTestnet,
fraxtal,
fraxtalTestnet,
immutableZkEvm,
Expand Down Expand Up @@ -78,10 +80,26 @@ export const ALL_CHAINS: ExtendedWagmiChainConfig[] = [
},
{
...sepolia,
rpcUrls: {
default: { http: ["https://endpoints.omniatech.io/v1/eth/sepolia/public", "https://1rpc.io/sepolia"] }, // Temporarily using this url
public: { http: ["https://endpoints.omniatech.io/v1/eth/sepolia/public", "https://1rpc.io/sepolia"] },
},
axelarChainId: "ethereum-sepolia",
axelarChainName: "ethereum-sepolia",
environment: ENVIRONMENTS.testnet,
},
{
...flowMainnet,
axelarChainId: "flow",
axelarChainName: "Flow",
environment: ENVIRONMENTS.mainnet,
},
{
...flowTestnet,
axelarChainId: "flow",
axelarChainName: "Flow",
environment: ENVIRONMENTS.testnet,
},
{
...moonbeam,
rpcUrls: createRpcUrlConfig(moonbeam, ["https://moonbeam.drpc.org"]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { trpc } from "~/lib/trpc";
import { isValidEVMAddress } from "~/lib/utils/validation";
import { RecordInterchainTokenDeploymentInput } from "~/server/routers/interchainToken/recordInterchainTokenDeployment";
import { useEVMChainConfigsQuery } from "~/services/axelarscan/hooks";
import { useAllChainConfigsQuery } from "~/services/axelarscan/hooks";
import type { DeployAndRegisterTransactionState } from "../CanonicalTokenDeployment.state";

export interface UseDeployAndRegisterCanonicalTokenInput {
Expand All @@ -43,7 +43,7 @@ export function useDeployAndRegisterRemoteCanonicalTokenMutation(
const { address: deployerAddress } = useAccount();
const chainId = useChainId();

const { computed } = useEVMChainConfigsQuery();
const { combinedComputed } = useAllChainConfigsQuery();

const { mutateAsync: recordDeploymentAsync } =
trpc.interchainToken.recordInterchainTokenDeployment.useMutation();
Expand All @@ -64,7 +64,7 @@ export function useDeployAndRegisterRemoteCanonicalTokenMutation(
});

const { originalChainName, destinationChainNames } = useMemo(() => {
const index = computed.indexedById;
const index = combinedComputed.indexedById;
const originalChainName =
index[input?.sourceChainId ?? chainId]?.chain_name ?? "Unknown";

Expand All @@ -80,7 +80,7 @@ export function useDeployAndRegisterRemoteCanonicalTokenMutation(
};
}, [
chainId,
computed.indexedById,
combinedComputed.indexedById,
input?.destinationChainIds,
input?.sourceChainId,
]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EVMChainConfig } from "@axelarjs/api/axelarscan";
import type { EVMChainConfig, VMChainConfig } from "@axelarjs/api/axelarscan";
import { useEffect, useState } from "react";

import { formatEther } from "viem";
Expand All @@ -9,21 +9,24 @@ import {
NEXT_PUBLIC_INTERCHAIN_DEPLOYMENT_GAS_LIMIT,
} from "~/config/env";
import { useEstimateGasFeeMultipleChainsQuery } from "~/services/axelarjsSDK/hooks";
import { useEVMChainConfigsQuery } from "~/services/axelarscan/hooks";
import { useAllChainConfigsQuery } from "~/services/axelarscan/hooks";
import { useCanonicalTokenDeploymentStateContainer } from "../../CanonicalTokenDeployment.state";

export type UseStep3ChainSelectionStateProps = {
selectedChains: Set<string>;
};

export function useStep3ChainSelectionState() {
const { data: evmChains } = useEVMChainConfigsQuery();
const { allChains } = useAllChainConfigsQuery();
const chainId = useChainId();
const [isDeploying, setIsDeploying] = useState(false);
const [totalGasFee, $setTotalGasFee] = useState(formatEther(0n));
const [sourceChainId, setSourceChainId] = useState(
evmChains?.find((evmChain: EVMChainConfig) => evmChain.chain_id === chainId)
?.id as string

// Find source chain from both EVM and VM chains
const currentChain = allChains?.find((chain: EVMChainConfig | VMChainConfig) => chain.chain_id === chainId);

const [sourceChainId, setSourceChainId] = useState<string>(
currentChain?.id || ""
);

const { state: rootState } = useCanonicalTokenDeploymentStateContainer();
Expand Down Expand Up @@ -58,20 +61,17 @@ export function useStep3ChainSelectionState() {
};

useEffect(() => {
const candidateChain = evmChains?.find(
(evmChain) => evmChain.chain_id === chainId
);
if (!candidateChain || candidateChain.chain_name === sourceChainId) return;
if (!currentChain || currentChain.chain_name === sourceChainId) return;

setSourceChainId(candidateChain.chain_name);
}, [evmChains, chainId, sourceChainId]);
setSourceChainId(currentChain.chain_name);
}, [currentChain, chainId, sourceChainId]);

return {
state: {
isDeploying,
totalGasFee,
sourceChainId,
evmChains,
allChains,
isEstimatingGasFees: isRemoteDeploymentGasFeeLoading,
hasGasFeesEstimationError: isRemoteDeploymentGasFeeError,
remoteDeploymentGasFees,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ export const Step3: FC = () => {

const chainId = useChainId();

const sourceChain = state.evmChains.find((x) => x.chain_id === chainId);
// Support both EVM and VM chains
const sourceChain = state.allChains?.find((chain) => chain.chain_id === chainId);

const [validDestinationChainIds, erroredDestinationChainIds] = useMemo(
const [validDestinationChainIds, erroredDestinationChainIds] = useMemo(
() =>
(state.remoteDeploymentGasFees?.gasFees ?? []).reduce(
([succeeded, errored], x): [string[], string[]] =>
Expand Down Expand Up @@ -135,19 +136,19 @@ export const Step3: FC = () => {
addTransaction,
]
);

const eligibleChains = useMemo(
() => state.evmChains?.filter((chain) => chain.chain_id !== chainId),
[state.evmChains, chainId]
);

const eligibleChains = state.allChains.filter(chain => chain.chain_id !== chainId);
const formSubmitRef = useRef<ComponentRef<"button">>(null);

const { address } = useAccount();

const { data: balance } = useBalance({ address });

const nativeTokenSymbol = getNativeToken(state.sourceChainId);
const nativeTokenSymbol = useMemo(() => {
if (sourceChain?.chain_type === 'vm') {
return sourceChain.native_token.symbol;
}
return getNativeToken(state.sourceChainId);
}, [sourceChain, state.sourceChainId]);

const hasInsufficientGasBalance = useMemo(() => {
if (!balance || !state.remoteDeploymentGasFees) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import {
ExternalLinkIcon,
LinkButton,
} from "@axelarjs/ui";
import { maskAddress, Maybe } from "@axelarjs/utils";
import { useCallback, useEffect, useState, type FC } from "react";
import { maskAddress } from "@axelarjs/utils";
import { useCallback, useEffect, useState, useMemo, type FC } from "react";
import { useRouter } from "next/router";

import { useAccount } from "wagmi";

import { useChainFromRoute } from "~/lib/hooks";
import { useEVMChainConfigsQuery } from "~/services/axelarscan/hooks";
import { useAllChainConfigsQuery } from "~/services/axelarscan/hooks";
import { useInterchainTokensQuery } from "~/services/gmp/hooks";
import GMPTxStatusMonitor from "~/ui/compounds/GMPTxStatusMonitor";
import { ShareHaikuButton } from "~/ui/compounds/MultiStepForm";
Expand All @@ -26,7 +25,7 @@ const Review: FC = () => {
const { chain } = useAccount();
const routeChain = useChainFromRoute();

const { computed } = useEVMChainConfigsQuery();
const { combinedComputed } = useAllChainConfigsQuery();

const [shouldFetch, setShouldFetch] = useState(false);

Expand All @@ -47,26 +46,28 @@ const Review: FC = () => {
chain.id,
state.txState.txHash,
state.selectedChains.map(
(axelarChainId) => computed.indexedById[axelarChainId].chain_id
(axelarChainId) => combinedComputed.indexedById[axelarChainId].chain_id
)
);
}
}, [chain, computed.indexedById, state.selectedChains, state.txState]);
}, [chain, combinedComputed.indexedById, state.selectedChains, state.txState]);

const chainConfig = Maybe.of(chain).mapOrUndefined(
(chain) => computed.indexedByChainId[chain.id]
);
const chainConfig = useMemo(() => {
if (!chain) return undefined;
return combinedComputed.indexedByChainId[chain.id];
}, [chain, combinedComputed.indexedByChainId]);

const handleGoToTokenPage = useCallback(async () => {
if (chainConfig && state.txState.type === "deployed") {
actions.reset();

await router.push(
`/${chainConfig.chain_name.toLowerCase()}/${state.txState.tokenAddress}`
);
}
}, [actions, chainConfig, router, state.txState]);

const isVMChain = chainConfig?.chain_type === 'vm';

return (
<>
<div className="grid gap-4">
Expand Down Expand Up @@ -105,32 +106,32 @@ const Review: FC = () => {
{state.selectedChains.length > 0 ? (
<GMPTxStatusMonitor txHash={state.txState.txHash} />
) : (
<LinkButton
$size="sm"
href={`${chain?.blockExplorers?.default.url}/tx/${state.txState.txHash}`}
className="flex items-center gap-2"
target="_blank"
>
View transaction{" "}
<span className="hidden md:inline">
{maskAddress(state.txState.txHash ?? "")}
</span>{" "}
on {chain?.blockExplorers?.default.name}{" "}
<ExternalLinkIcon className="h-4 w-4" />
</LinkButton>
!isVMChain && chain?.blockExplorers?.default && (
<LinkButton
$size="sm"
href={`${chain.blockExplorers.default.url}/tx/${state.txState.txHash}`}
className="flex items-center gap-2"
target="_blank"
>
View transaction{" "}
<span className="hidden md:inline">
{maskAddress(state.txState.txHash ?? "")}
</span>{" "}
on {chain.blockExplorers.default.name}{" "}
<ExternalLinkIcon className="h-4 w-4" />
</LinkButton>
)
)}
</>
)}
</div>
<Dialog.Actions>
{routeChain ? (
// if the chain is not the same as the route, we need to refresh the page
<Dialog.CloseAction
$length="block"
$variant="primary"
onClick={async () => {
setShouldFetch(true);
// refresh the page to show the new token
await router.replace(router.asPath);
}}
>
Expand All @@ -140,7 +141,7 @@ const Review: FC = () => {
<Button
$length="block"
$variant="primary"
disabled={!chain?.name || state.txState.type !== "deployed"}
disabled={!chainConfig || state.txState.type !== "deployed"}
onClick={handleGoToTokenPage}
>
Go to token page!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { trpc } from "~/lib/trpc";
import { isValidEVMAddress } from "~/lib/utils/validation";
import type { EstimateGasFeeMultipleChainsOutput } from "~/server/routers/axelarjsSDK";
import { RecordInterchainTokenDeploymentInput } from "~/server/routers/interchainToken/recordInterchainTokenDeployment";
import { useEVMChainConfigsQuery } from "~/services/axelarscan/hooks";
import { useAllChainConfigsQuery } from "~/services/axelarscan/hooks";
import type { DeployAndRegisterTransactionState } from "../InterchainTokenDeployment.state";

export interface UseDeployAndRegisterInterchainTokenInput {
Expand All @@ -49,7 +49,7 @@ export function useDeployAndRegisterRemoteInterchainTokenMutation(
const { address: deployerAddress } = useAccount();
const chainId = useChainId();

const { computed } = useEVMChainConfigsQuery();
const { combinedComputed } = useAllChainConfigsQuery();

const { mutateAsync: recordDeploymentAsync } =
trpc.interchainToken.recordInterchainTokenDeployment.useMutation();
Expand Down Expand Up @@ -81,7 +81,7 @@ export function useDeployAndRegisterRemoteInterchainTokenMutation(
});

const { destinationChainNames } = useMemo(() => {
const index = computed.indexedById;
const index = combinedComputed.indexedById;

return {
destinationChainNames:
Expand All @@ -90,7 +90,7 @@ export function useDeployAndRegisterRemoteInterchainTokenMutation(
index[destinationChainId]?.chain_name ?? "Unknown"
) ?? [],
};
}, [computed.indexedById, input?.destinationChainIds]);
}, [combinedComputed.indexedById, input?.destinationChainIds]);

const multicallArgs = useMemo(() => {
if (!input || !tokenId) {
Expand Down
Loading

0 comments on commit 1d05d56

Please sign in to comment.