diff --git a/.env.example b/.env.example
index 32579d8..e970a99 100644
--- a/.env.example
+++ b/.env.example
@@ -4,6 +4,7 @@ NODE_ENV=test
# API URLs
NEXT_PUBLIC_BRIDGE_API_URL="https://turing-bridge-api.fra.avail.so"
+NEXT_PUBLIC_LIQUIDITY_BRIDGE_API_URL=""
NEXT_PUBLIC_BRIDGE_INDEXER_URL="https://turing-bridge-indexer.fra.avail.so"
NEXT_PUBLIC_COINGECKO_API_URL="https://api.coingecko.com/api/v3/simple/price"
@@ -30,6 +31,10 @@ NEXT_PUBLIC_MANAGER_ADDRESS_BASE="0xf4B55457fCD2b6eF6ffd41E5F5b0D65fbE370EA3"
NEXT_PUBLIC_WORMHOLE_TRANSCEIVER_ETH="0x988140794D960fD962329751278Ef0DD2438a64C"
NEXT_PUBLIC_WORMHOLE_TRANSCEIVER_BASE="0xAb9C68eD462f61Fd5fd34e6c21588513d89F603c"
+NEXT_PUBLIC_LP_ADDRESS_ETH=""
+NEXT_PUBLIC_LP_ADDRESS_BASE=""
+NEXT_PUBLIC_LP_ADDRESS_AVAIL=""
+
# Datadog Configuration
NEXT_PUBLIC_DATADOG_RUM_APPLICATION_ID=""
NEXT_PUBLIC_DATADOG_RUM_CLIENT_TOKEN=""
diff --git a/app/providers.tsx b/app/providers.tsx
index f17eef1..6bf93bd 100644
--- a/app/providers.tsx
+++ b/app/providers.tsx
@@ -36,7 +36,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
useEffect(() => {
(async () => {
await fetchDollarAmount();
-
+
const interval = setInterval(async () => {
await fetchDollarAmount();
}, 30000);
diff --git a/components/chainselector/chainselectormodal.tsx b/components/chainselector/chainselectormodal.tsx
index a6d1eda..2ed41e6 100644
--- a/components/chainselector/chainselectormodal.tsx
+++ b/components/chainselector/chainselectormodal.tsx
@@ -10,12 +10,6 @@ import { useCommonStore } from "@/stores/common";
import { ChevronLeft } from "lucide-react";
import useEthWallet from "@/hooks/common/useEthWallet";
import { capitalizeFirstLetter } from "@/hooks/wormhole/helper";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
type ChainSelectorModalProps = {
isOpen: boolean;
@@ -24,38 +18,20 @@ type ChainSelectorModalProps = {
};
const ChainSelectorModal = ({ isOpen, onClose, type }: ChainSelectorModalProps) => {
- const { setFromChain, setToChain, fromChain, toChain } = useCommonStore();
- const { validateandSwitchChain } = useEthWallet();
-
- const isInvalidCombination = (chain: Chain) => {
- if (type === "from") {
- return (toChain === Chain.BASE && chain === Chain.AVAIL) ||
- (toChain === Chain.AVAIL && chain === Chain.BASE);
- }
- if (type === "to") {
- return (fromChain === Chain.BASE && chain === Chain.AVAIL) ||
- (fromChain === Chain.AVAIL && chain === Chain.BASE);
- }
- return false;
- };
+ const { setFromChain, setToChain, fromChain, toChain } = useCommonStore();
+ const { validateandSwitchChain } = useEthWallet();
const handleChainSelect = async (selectedChain: Chain) => {
- if (isInvalidCombination(selectedChain)) return;
-
if (type === "from") {
- const needToAdjustToChain = selectedChain === toChain ||
- // Add check for BASE <-> AVAIL combination
- (selectedChain === Chain.AVAIL && toChain === Chain.BASE) ||
- (selectedChain === Chain.BASE && toChain === Chain.AVAIL);
-
- if (needToAdjustToChain) {
- setToChain(selectedChain === Chain.ETH ? Chain.BASE : Chain.ETH);
+ if (selectedChain === toChain) {
+ // If selected source chain is same as destination, swap them
+ setToChain(fromChain);
}
-
setFromChain(selectedChain);
await validateandSwitchChain(selectedChain);
} else {
if (selectedChain === fromChain) {
+ // If selected destination chain is same as source, swap them
await validateandSwitchChain(toChain);
setFromChain(toChain);
setToChain(fromChain);
@@ -66,66 +42,50 @@ const ChainSelectorModal = ({ isOpen, onClose, type }: ChainSelectorModalProps)
onClose();
};
- return (
-
+ );
};
-export default ChainSelectorModal;
\ No newline at end of file
+export default ChainSelectorModal;
diff --git a/components/common/container.tsx b/components/common/container.tsx
index d985d15..89dfb40 100644
--- a/components/common/container.tsx
+++ b/components/common/container.tsx
@@ -8,6 +8,9 @@ import BridgeSection from "../sections/bridge";
import { useAvailAccount } from "@/stores/availwallet";
import { useAccount } from "wagmi";
import { useTransactionsStore } from "@/stores/transactions";
+import TransactionModal from "../sections/bridge/review-section";
+import { useCommonStore } from "@/stores/common";
+import AdvancedSettings from "./settings";
export default function Container() {
const [activeTab, setActiveTab] = useState("bridge");
@@ -15,6 +18,7 @@ export default function Container() {
const { selected } = useAvailAccount();
const { address } = useAccount();
const { fetchAllTransactions, setTransactionLoader} = useTransactionsStore();
+ const { reviewDialog: { isOpen: isModalOpen, onOpenChange: setIsModalOpen } } = useCommonStore();
useEffect(()=>{
(async () => {
@@ -47,8 +51,8 @@ export default function Container() {
id="container"
className="section_bg p-2 w-screen max-sm:rounded-none max-sm:!border-x-0 !max-w-xl "
>
-
-
+
+
+
@@ -92,6 +97,7 @@ export default function Container() {
+
setIsModalOpen(false)} />
);
}
diff --git a/components/common/settings.tsx b/components/common/settings.tsx
new file mode 100644
index 0000000..4d886ab
--- /dev/null
+++ b/components/common/settings.tsx
@@ -0,0 +1,63 @@
+import React, { useState } from 'react';
+import { ArrowLeft, Info, Settings } from 'lucide-react';
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { Switch } from "@/components/ui/switch";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip';
+
+const AdvancedSettings = () => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [autoClaim, setAutoClaim] = useState(false);
+
+ return (
+ <>
+ setIsOpen(true)}
+ />
+
+
+
+
+
+
setIsOpen(false)}
+ />
+ Advanced Settings
+
+
+
+
+
+
+
+
+ Automatically claim on destination
+ chain wherever possible
+
+
+
+
+
+ Coming soon 👀
+
+
+
+
+
+
+ >
+ );
+};
+
+export default AdvancedSettings;
\ No newline at end of file
diff --git a/components/common/utils.ts b/components/common/utils.ts
index 3a3cab3..0066784 100644
--- a/components/common/utils.ts
+++ b/components/common/utils.ts
@@ -1,4 +1,6 @@
+import { appConfig } from "@/config/default";
import { TxnLifecyle } from "@/hooks/common/useTrackTxnStatus";
+import { Chain } from "@/types/common";
export const getStepStatus = (step: number, status: TxnLifecyle) => {
if (step === 1) {
@@ -12,4 +14,37 @@ export const getStepStatus = (step: number, status: TxnLifecyle) => {
: "waiting";
}
return "waiting";
- };
\ No newline at end of file
+ };
+
+ export const chainToChainId = (chain: Chain) => {
+ switch (chain) {
+ case Chain.ETH:
+ return appConfig.networks.ethereum.id;
+ case Chain.BASE:
+ return appConfig.networks.base.id;
+ default:
+ throw new Error(`Unsupported chain: ${chain}`);
+ }
+ }
+
+ export const chainToAddresses = (chain: Chain) => {
+ switch (chain) {
+ case Chain.ETH:
+ return {
+ tokenAddress: appConfig.contracts.ethereum.availToken,
+ bridgeAddress: appConfig.contracts.ethereum.bridge,
+ liquidityBridgeAddress: appConfig.contracts.ethereum.liquidityBridgeAddress,
+ }
+ case Chain.BASE:
+ return {
+ tokenAddress: appConfig.contracts.base.availToken,
+ liquidityBridgeAddress: appConfig.contracts.base.liquidityBridgeAddress,
+ }
+ case Chain.AVAIL:
+ return {
+ liquidityBridgeAddress: appConfig.contracts.avail.liquidityBridgeAddress,
+ }
+ default:
+ throw new Error(`Unsupported chain: ${chain}`);
+ }
+ }
\ No newline at end of file
diff --git a/components/sections/bridge/index.tsx b/components/sections/bridge/index.tsx
index e1d3a28..74cf9cd 100644
--- a/components/sections/bridge/index.tsx
+++ b/components/sections/bridge/index.tsx
@@ -1,5 +1,5 @@
-import SubmitTransaction from "./submittransaction";
+import SubmitTransaction from "./submit-transaction";
import FromField from "./fromfield";
import ToField from "./tofield";
import ChainSwapBtn from "../../chainselector/chainswapbtn";
@@ -9,10 +9,10 @@ export default function BridgeSection() {
return (
);
diff --git a/components/sections/bridge/review-section.tsx b/components/sections/bridge/review-section.tsx
new file mode 100644
index 0000000..3734756
--- /dev/null
+++ b/components/sections/bridge/review-section.tsx
@@ -0,0 +1,270 @@
+import { Badge } from "@/components/ui/badge";
+import React from "react";
+import { LoadingButton } from "@/components/ui/loadingbutton";
+import useZkBridge from "@/hooks/useZkBridge";
+import useSubmitTxnState from "@/hooks/common/useSubmitTxnState";
+import { SuccessDialog, useCommonStore } from "@/stores/common";
+import { Chain } from "@/types/common";
+import { validAddress } from "@/utils/common";
+import BigNumber from "bignumber.js";
+import { useState } from "react";
+import useWormHoleBridge from "@/hooks/wormhole/useWormHoleBridge";
+import { ChainPairs } from "./types";
+import { appConfig } from "@/config/default";
+import { useBalanceStore } from "@/stores/balances";
+import { useApi } from "@/stores/api";
+import { useAvailAccount } from "@/stores/availwallet";
+import { useAccount } from "wagmi";
+import useLiquidityBridge from "@/hooks/useLiquidityBridge";
+import { motion, AnimatePresence } from "framer-motion";
+
+interface TransactionModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+const TransactionModal: React.FC = ({
+ isOpen,
+ onClose,
+}) => {
+ const [transactionInProgress, setTransactionInProgress] =
+ useState(false);
+
+const {
+ fromChain,
+ toChain,
+ fromAmount,
+ toAddress,
+ successDialog,
+ errorDialog: { onOpenChange: setErrorOpenDialog, setError },
+} = useCommonStore();
+
+const { fetchBalance } = useBalanceStore();
+const { selected } = useAvailAccount();
+const { address: ethAddress } = useAccount();
+const { api } = useApi();
+const { initEthToAvailBridging, initAvailToEthBridging } = useZkBridge();
+const {
+ initAvailToERC20AutomaticBridging,
+ initERC20toAvailAutomaticBridging,
+} = useLiquidityBridge();
+const { initWormholeBridge } = useWormHoleBridge();
+const { buttonStatus, isDisabled } = useSubmitTxnState(transactionInProgress);
+
+const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ successDialog.setClaimDialog(false);
+
+ try {
+ let bridgeResult: SuccessDialog["details"] | null = null;
+ const chainPair = `${fromChain}-${toChain}` as const;
+
+ const fromAmountAtomic = new BigNumber(fromAmount)
+ .multipliedBy(new BigNumber(10).pow(18))
+ .toString(10);
+
+ if (toAddress === undefined || !validAddress(toAddress, toChain)) {
+ throw new Error("Please enter a valid address");
+ }
+
+ setTransactionInProgress(true);
+
+ switch (chainPair) {
+ case ChainPairs.ETH_TO_AVAIL: {
+ const blockhash = await initEthToAvailBridging({
+ atomicAmount: fromAmountAtomic,
+ destinationAddress: toAddress!,
+ });
+ bridgeResult = { chain: Chain.ETH, hash: blockhash };
+ break;
+ }
+ case ChainPairs.AVAIL_TO_ETH: {
+ const init = await initAvailToEthBridging({
+ atomicAmount: fromAmountAtomic,
+ destinationAddress: toAddress!,
+ });
+ if (init.txHash) {
+ bridgeResult = { chain: Chain.AVAIL, hash: init.txHash };
+ }
+ break;
+ }
+ case ChainPairs.BASE_TO_ETH: {
+ const init = await initWormholeBridge({
+ whfrom: appConfig.config === "mainnet" ? "Base" : "BaseSepolia",
+ whto: appConfig.config === "mainnet" ? "Ethereum" : "Sepolia",
+ sendAmount: fromAmount,
+ destinationAddress: toAddress!,
+ switcher: Chain.BASE,
+ });
+ if (init) {
+ bridgeResult = {
+ chain: Chain.BASE,
+ hash: init[1] ? init[1].txid : init[0].txid,
+ isWormhole: true,
+ };
+ }
+ break;
+ }
+ case ChainPairs.ETH_TO_BASE: {
+ const init = await initWormholeBridge({
+ whfrom: appConfig.config === "mainnet" ? "Ethereum" : "Sepolia",
+ whto: appConfig.config === "mainnet" ? "Base" : "BaseSepolia",
+ sendAmount: fromAmount,
+ destinationAddress: toAddress!,
+ switcher: Chain.ETH,
+ });
+ if (init) {
+ bridgeResult = {
+ chain: Chain.ETH,
+ hash: init[1] ? init[1].txid : init[0].txid,
+ isWormhole: true,
+ };
+ }
+ break;
+ }
+ case ChainPairs.AVAIL_TO_BASE: {
+ console.log("AVAIL TO BASE");
+ const init = await initAvailToERC20AutomaticBridging({
+ ERC20Chain: Chain.BASE,
+ atomicAmount: fromAmountAtomic,
+ destinationAddress: toAddress!,
+ });
+
+ if (init.hash) {
+ bridgeResult = {
+ chain: Chain.AVAIL,
+ hash: init.hash,
+ };
+ }
+ }
+ case ChainPairs.BASE_TO_AVAIL: {
+ console.log("BASE TO AVAIL", fromAmountAtomic, toAddress);
+
+ const init = await initERC20toAvailAutomaticBridging({
+ ERC20Chain: Chain.BASE,
+ atomicAmount: fromAmountAtomic,
+ destinationAddress: toAddress!,
+ });
+
+ if (init.hash) {
+ bridgeResult = {
+ chain: Chain.BASE,
+ hash: init.hash,
+ };
+ }
+ }
+ }
+ if (bridgeResult) {
+ successDialog.setDetails(bridgeResult);
+ successDialog.onOpenChange(true);
+ }
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ setErrorOpenDialog(true);
+ } finally {
+ setTransactionInProgress(false);
+ onClose();
+ await fetchBalance(
+ fromChain === Chain.AVAIL ? selected?.address! : ethAddress!,
+ fromChain,
+ api
+ );
+ }
+};
+
+ if (!isOpen) return null;
+
+ return (
+
+ {isOpen && (
+
+
+
+
+
+
+ Review Transaction Details
+
+
+
+
+
+ {buttonStatus}
+
+
+
+
+ )}
+
+ );
+};
+
+export default TransactionModal;
+
+
+const Details = () => {
+ return (
+
+
+ Destination Gas Fee ($)
+ 1.66 Avail ($1.2)
+
+
+
+ Estimated Time
+ ~5 minutes
+
+
+
+ Claim Type
+
+ Auto
+
+
+
+
+
User will recieve
+
+ 11.34
+ Avail
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/sections/bridge/submit-transaction.tsx b/components/sections/bridge/submit-transaction.tsx
new file mode 100644
index 0000000..1f55e1a
--- /dev/null
+++ b/components/sections/bridge/submit-transaction.tsx
@@ -0,0 +1,47 @@
+import { LoadingButton } from "@/components/ui/loadingbutton";
+import useSubmitTxnState from "@/hooks/common/useSubmitTxnState";
+import { useCommonStore } from "@/stores/common";
+import { useState } from "react";
+import { RxArrowTopRight } from "react-icons/rx";
+import { isWormholeBridge } from "./utils";
+
+export default function SubmitTransaction() {
+ const [transactionInProgress, setTransactionInProgress] =
+ useState(false);
+
+ const {
+ fromChain,
+ toChain,
+ reviewDialog: { onOpenChange: setShowReviewModal },
+ } = useCommonStore();
+ const { buttonStatus, isDisabled } = useSubmitTxnState(transactionInProgress);
+
+
+
+ const handleReview = (e: React.FormEvent) => {
+ e.preventDefault();
+ setShowReviewModal(true);
+ };
+
+
+ return (
+ <>
+
+ {buttonStatus}
+
+
+ {isWormholeBridge(`${fromChain}-${toChain}`) && (
+
+ Using Third Party Wormhole Bridge{" "}
+
+
+ )}
+ >
+ );
+ };
+
diff --git a/components/sections/bridge/submittransaction.tsx b/components/sections/bridge/submittransaction.tsx
deleted file mode 100644
index be91a1a..0000000
--- a/components/sections/bridge/submittransaction.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import { LoadingButton } from "@/components/ui/loadingbutton";
-import useBridge from "@/hooks/useBridge";
-import useSubmitTxnState from "@/hooks/common/useSubmitTxnState";
-import { SuccessDialog, useCommonStore } from "@/stores/common";
-import { Chain } from "@/types/common";
-import { validAddress } from "@/utils/common";
-import BigNumber from "bignumber.js";
-import { use, useState } from "react";
-import useWormHoleBridge from "@/hooks/wormhole/useWormHoleBridge";
-import { ChainPairs } from "./types";
-import { appConfig } from "@/config/default";
-import { RxArrowTopRight } from "react-icons/rx";
-import { useBalanceStore } from "@/stores/balances";
-import { useApi } from "@/stores/api";
-import { useAvailAccount } from "@/stores/availwallet";
-import { useAccount } from "wagmi";
-import { isWormholeBridge } from "./utils";
-
-export default function SubmitTransaction() {
- const [transactionInProgress, setTransactionInProgress] =
- useState(false);
-
- const {
- fromChain,
- toChain,
- fromAmount,
- toAddress,
- successDialog: { onOpenChange: setOpenDialog, setDetails, setClaimDialog },
- errorDialog: { onOpenChange: setErrorOpenDialog, setError },
- } = useCommonStore();
-
- const { fetchBalance } = useBalanceStore();
- const { selected } = useAvailAccount();
- const { address: ethAddress } = useAccount();
- const { api } = useApi();
- const { initEthToAvailBridging, initAvailToEthBridging } = useBridge();
- const { initWormholeBridge } = useWormHoleBridge();
- const { buttonStatus, isDisabled } = useSubmitTxnState(transactionInProgress);
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
- setClaimDialog(false)
-
- try {
- let bridgeResult: SuccessDialog["details"] | null = null;
- const chainPair = `${fromChain}-${toChain}` as const;
-
- const fromAmountAtomic = new BigNumber(fromAmount)
- .multipliedBy(new BigNumber(10).pow(18))
- .toString(10);
-
- if (toAddress === undefined || !validAddress(toAddress, toChain)) {
- throw new Error("Please enter a valid address");
- }
-
- setTransactionInProgress(true);
-
- switch (chainPair) {
- case ChainPairs.ETH_TO_AVAIL: {
- const blockhash = await initEthToAvailBridging({
- atomicAmount: fromAmountAtomic,
- destinationAddress: toAddress!,
- });
- bridgeResult = { chain: Chain.ETH, hash: blockhash };
- break;
- }
- case ChainPairs.AVAIL_TO_ETH: {
- const init = await initAvailToEthBridging({
- atomicAmount: fromAmountAtomic,
- destinationAddress: toAddress!,
- });
- if (init.txHash) {
- bridgeResult = { chain: Chain.AVAIL, hash: init.txHash };
- }
- break;
- }
- case ChainPairs.BASE_TO_ETH: {
- const init = await initWormholeBridge({
- whfrom: appConfig.config === "mainnet" ? "Base" : "BaseSepolia",
- whto: appConfig.config === "mainnet" ? "Ethereum" : "Sepolia",
- sendAmount: fromAmount,
- destinationAddress: toAddress!,
- switcher: Chain.BASE,
- });
- if (init) {
- bridgeResult = {
- chain: Chain.BASE,
- hash: init[1] ? init[1].txid : init[0].txid,
- isWormhole: true,
- };
- }
- break;
- }
- case ChainPairs.ETH_TO_BASE: {
- const init = await initWormholeBridge({
- whfrom: appConfig.config === "mainnet" ? "Ethereum" : "Sepolia",
- whto: appConfig.config === "mainnet" ? "Base" : "BaseSepolia",
- sendAmount: fromAmount,
- destinationAddress: toAddress!,
- switcher: Chain.ETH,
- });
- if (init) {
- bridgeResult = {
- chain: Chain.ETH,
- hash: init[1] ? init[1].txid : init[0].txid,
- isWormhole: true,
- };
- }
- break;
- }
- default:
- throw new Error("Unsupported chain combination");
- }
-
- if (bridgeResult) {
- setDetails(bridgeResult);
- setOpenDialog(true);
- }
- } catch (error: any) {
- console.error(error);
- setError(error);
- setErrorOpenDialog(true);
- } finally {
- setTransactionInProgress(false);
- await fetchBalance(
- fromChain === Chain.AVAIL ? selected?.address! : ethAddress!,
- fromChain,
- api
- );
- }
- }
-
- return (
- <>
-
- {buttonStatus}
-
- {isWormholeBridge(`${fromChain}-${toChain}`) && (
-
- Using Third Party Wormhole Bridge{" "}
-
-
- )}
- >
- );
- };
-
diff --git a/components/sections/bridge/types.ts b/components/sections/bridge/types.ts
index b845dec..ddb336c 100644
--- a/components/sections/bridge/types.ts
+++ b/components/sections/bridge/types.ts
@@ -73,7 +73,9 @@ export interface Account {
ETH_TO_AVAIL: `${Chain.ETH}-${Chain.AVAIL}`,
AVAIL_TO_ETH: `${Chain.AVAIL}-${Chain.ETH}`,
BASE_TO_ETH: `${Chain.BASE}-${Chain.ETH}`,
- ETH_TO_BASE: `${Chain.ETH}-${Chain.BASE}`
+ ETH_TO_BASE: `${Chain.ETH}-${Chain.BASE}`,
+ AVAIL_TO_BASE: `${Chain.AVAIL}-${Chain.BASE}`,
+ BASE_TO_AVAIL: `${Chain.BASE}-${Chain.AVAIL}`
} as const;
diff --git a/components/sections/transactions/index.tsx b/components/sections/transactions/index.tsx
index 74f38a3..9db6cf5 100644
--- a/components/sections/transactions/index.tsx
+++ b/components/sections/transactions/index.tsx
@@ -117,7 +117,7 @@ export default function TransactionSection() {
{/* Pagination */}
-
+
Can't find your transaction?
@@ -152,7 +152,7 @@ export default function TransactionSection() {
-
+