bridge compiles

Fix issues with abi-wan-kenobi

bridge compiles, builds, and runs (routes still removed)

auth route back in

transactions route

all routes restored

Bump @hanzo/auth to fix firebase credential handling

pnpm version

linting re-enabled and issues fixed

working version with some UI tweeks

@luxfi/ui version update

fixed page routers
This commit is contained in:
artem ash
2024-11-07 06:30:34 -08:00
parent 4cb4d0aba6
commit 2870e85498
575 changed files with 42533 additions and 8965 deletions
-14
View File
@@ -1,14 +0,0 @@
import { CryptoNetwork } from "./CryptoNetwork";
import { Exchange } from "./Exchange";
export class BridgeSettings {
exchanges: Exchange[];
networks: CryptoNetwork[];
sources?: Route[];
destinations?: Route[];
};
export class Route {
network: string;
asset: string;
}
@@ -1,130 +0,0 @@
'use client'
import { FC } from "react"
import { useRouter } from "next/router"
import { useAccount } from "wagmi"
import { LinkElement } from "@luxdefi/ui/primitives"
import { useSettingsState } from "../../../context/settings"
import Image from 'next/image'
import { Gift } from "lucide-react"
import BridgeApiClient, { Campaign } from "../../../lib/BridgeApiClient"
import useSWR from "swr"
import { ApiResponse } from "../../../Models/ApiResponse"
import RainbowKit from "../../Swap/Withdraw/Wallet/RainbowKit"
import SubmitButton from "../../buttons/submitButton";
import WalletIcon from "../../icons/WalletIcon";
import LinkWrapper from "../../LinkWrapper";
import { Widget } from "../../Widget/Index";
import Leaderboard from "./Leaderboard"
import Rewards from "./Rewards";
import SpinIcon from "../../icons/spinIcon"
import { Layer } from "../../../Models/Layer"
function CampaignDetails() {
const settings = useSettingsState()
const router = useRouter();
const { resolveImgSrc, layers } = settings
const camapaignName = router.query.campaign?.toString()
const { isConnected } = useAccount();
const apiClient = new BridgeApiClient()
const { data: campaignsData, isLoading } = useSWR<ApiResponse<Campaign[]>>('/campaigns', apiClient.fetcher)
const campaign = campaignsData?.data?.find(c => c.name === camapaignName)
const network = layers.find(n => n.internal_name === campaign?.network)
if (isLoading) {
return <Loading />
}
if (!campaign) {
return <NotFound />
}
return (
<Widget>
<Widget.Content>
<div className="space-y-5">
<div className="flex items-center gap-1">
{network && (
<div className="h-7 w-7 relative">
<Image
src={resolveImgSrc(network)}
alt="Project Logo"
height="40"
width="40"
loading="eager"
className="rounded-md object-contain"
/>
</div>
)}
<h2 className="font-bold text-xl text-left flex items-center">
{network?.display_name && (<span>network.display_name&nbsp;</span>)}
Rewards
</h2>
</div>
{isConnected ? (
<Rewards campaign={campaign} />
) : (
<BriefInformation network={network} campaign={campaign} />
)}
<Leaderboard campaign={campaign} />
</div>
</Widget.Content>
{!isConnected ? (
<Widget.Footer>
<RainbowKit>
<SubmitButton isDisabled={false} isSubmitting={false} icon={<WalletIcon className="stroke-2 w-6 h-6" />}>
Connect a wallet
</SubmitButton>
</RainbowKit>
</Widget.Footer>
) : (<></>)}
</Widget >
)
}
type BriefInformationProps = {
campaign: Campaign,
network?: Layer
}
const BriefInformation: FC<BriefInformationProps> = ({ campaign, network }) =>
<p className="text-muted text-base">
You can earn $<span>{campaign?.asset}</span>&nbsp;tokens by transferring assets to&nbsp;
<span>{network?.display_name || campaign.network}</span>. For each transaction, you'll receive&nbsp;
<span>{campaign?.percentage}</span>% of Bridge fee back.&nbsp;
<LinkElement
def={{
title: 'Learn more',
newTab: true,
href: "https://docs.bridge.lux.network/user-docs/layerswap-campaigns/usdop-rewards"
}}
className="inline-flex text-primary underline hover:no-underline"
/>
</p>
const Loading = () => <Widget className="min-h-[500px]">
<Widget.Content>
<div className="absolute top-[calc(50%-5px)] left-[calc(50%-5px)]">
<SpinIcon className="animate-spin h-5 w-5" />
</div>
</Widget.Content>
</Widget>
const NotFound = () => <Widget className="min-h-[500px]">
<Widget.Content>
<div className="h-[364px] flex flex-col items-center justify-center space-y-4">
<Gift className="h-20 w-20 " />
<p className="font-bold text-center">Campaign not found</p>
<LinkWrapper className="text-xs underline hover:no-underline" href='/campaigns'>See all campaigns</LinkWrapper>
</div>
</Widget.Content>
</Widget>
export default CampaignDetails;
@@ -1,71 +0,0 @@
'use client'
import { SwapFormValues } from '../../DTOs/SwapFormValues';
import { ReceiveAmounts } from './ReceiveAmounts';
import DetailedEstimates from './DetailedEstimates';
import { useFee } from '../../../context/feeContext';
import { useSettingsState } from '../../../context/settings';
import RefuelToggle from './Refuel';
import CEXNetworkFormField from '../../Input/CEXNetworkFormField';
import FeeDetails from './FeeDetailsComponent';
import { useQueryState } from '../../../context/query';
import Campaign from './Campaign';
export default function FeeDetailsComponent({ values }: { values: SwapFormValues }) {
const { toCurrency, from, to, refuel, fromExchange, toExchange } = values || {};
const { fee } = useFee()
const currency = toCurrency
const { layers } = useSettingsState()
const query = useQueryState();
return (
<>
<FeeDetails>
{/* {
((fromExchange || toExchange) && (from || to)) &&
<FeeDetails.Item>
<CEXNetworkFormField direction={fromExchange ? 'from' : 'to'} />
</FeeDetails.Item>
} */}
{to && toCurrency && to.assets.find(a => a.asset === toCurrency.asset)?.is_refuel_enabled && !query?.hideRefuel &&
<FeeDetails.Item>
<RefuelToggle />
</FeeDetails.Item>
}
{from && to &&
<FeeDetails.Item>
<DetailedEstimates
networks={layers}
selected_currency={currency}
source={from}
destination={to}
/>
</FeeDetails.Item>
}
<FeeDetails.Item>
<ReceiveAmounts
currency={currency}
to={to}
receive_amount={fee.walletReceiveAmount}
refuel={!!refuel}
/>
</FeeDetails.Item>
</FeeDetails>
{
values.to &&
values.toCurrency &&
<Campaign
destination={values.to}
selected_currency={values.toCurrency}
fee={fee.walletFee}
/>
}
</>
)
}
-33
View File
@@ -1,33 +0,0 @@
'use client'
import Link, { LinkProps } from "next/link";
import { useRouter } from "next/router";
import { FC } from "react";
import { resolvePersistantQueryParams } from "../helpers/querryHelper";
const LinkWrapper: FC<Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> & LinkProps & {
children?: React.ReactNode;
} & React.RefAttributes<HTMLAnchorElement>> = (props) => {
const router = useRouter();
const { children } = props
const pathname = typeof props.href === 'object' ? props.href.pathname : props.href
const query = (typeof props.href === 'object' && typeof props.href.query === 'object' && props.href.query) || {}
return (
<Link
{...props}
href={{
pathname: pathname,
query: {
...resolvePersistantQueryParams(router.query),
...query
}
}}
>
{children}
</Link>
)
}
export default LinkWrapper
@@ -1,84 +0,0 @@
import CancelIcon from "./icons/CancelIcon";
import DelayIcon from "./icons/DelayIcon";
import FailIcon from "./icons/FailIcon";
import SuccessIcon from "./icons/SuccessIcon";
type iconStyle = 'red' | 'green' | 'yellow' | 'gray'
class MessageComponentProps {
children: JSX.Element | JSX.Element[];
center?: boolean
icon: iconStyle
}
function constructIcons(icon: iconStyle) {
let iconStyle: JSX.Element
switch (icon) {
case 'red':
iconStyle = <FailIcon />;
break;
case 'green':
iconStyle = <SuccessIcon />;
break;
case 'yellow':
iconStyle = <DelayIcon />
break
case 'gray':
iconStyle = CancelIcon
break
}
return iconStyle
}
const MessageComponent = ({ children }) => {
return <div className="w-full flex flex-col h-full justify-between pt-6 min-h-full">
{children}
</div>
}
const Content = ({ children, icon, center }: MessageComponentProps) => {
return (
center ?
<div className='flex flex-col self-center grow w-full'>
<div className='flex self-center grow w-full'>
<div className='flex flex-col space-y-8 self-center w-full'>
<div className='flex place-content-center'>{constructIcons(icon)}</div>
{children}
</div>
</div>
</div>
:
<div className='space-y-3'>
<div className='flex place-content-center'>{constructIcons(icon)}</div>
{children}
</div>
)
}
const Header = ({ children }) => {
return <div className='md:text-3xl text-lg font-bold leading-6 text-center'>
{children}
</div>
}
const Description = ({ children }) => {
return <div className="text-base font-medium space-y-6 text-center mb-6">
{children}
</div>
}
const Buttons = ({ children }) => {
return <div className="flex flex-row justify-center">
{children}
</div>
}
MessageComponent.Content = Content
MessageComponent.Header = Header
MessageComponent.Description = Description
MessageComponent.Buttons = Buttons
export default MessageComponent
-112
View File
@@ -1,112 +0,0 @@
import React, { type PropsWithChildren } from "react"
import {
Chain,
WagmiConfig,
configureChains,
createConfig,
mainnet,
sepolia
} from "wagmi"
import { publicProvider } from 'wagmi/providers/public'
import {
connectorsForWallets,
RainbowKitProvider,
DisclaimerComponent,
AvatarComponent
} from '@rainbow-me/rainbowkit'
import {
walletConnectWallet,
rainbowWallet,
metaMaskWallet,
coinbaseWallet,
bitgetWallet,
argentWallet,
phantomWallet
} from '@rainbow-me/rainbowkit/wallets'
import { useSettingsState } from "../context/settings"
import { NetworkType } from "../Models/CryptoNetwork"
import resolveChain from "../lib/resolveChain"
import AddressIcon from "./AddressIcon"
import configureRKTheme from "../styles/configureRKTheme"
const rkTheme = configureRKTheme()
const projectId = process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID || ''
const RainbowKitComponent: React.FC<PropsWithChildren> = ({ children }) => {
const settings = useSettingsState();
const isChain = (c: Chain | undefined): c is Chain => c != undefined
const settingsChains = settings?.layers
.sort((a, b) => Number(a.chain_id) - Number(b.chain_id))
.filter(net => net.type === NetworkType.EVM
&& net.nodes?.some(n => n.url?.length > 0))
.map(resolveChain).filter(isChain)
const { chains, publicClient } = configureChains(
// [mainnet],
settingsChains?.length > 0 ? [...settingsChains, sepolia] : [mainnet],
// [mainnet, sepolia, lux],
[publicProvider()]
)
const connectors = connectorsForWallets([
{
groupName: 'Popular',
wallets: [
metaMaskWallet({ projectId, chains }),
walletConnectWallet({ projectId, chains }),
],
},
{
groupName: 'Wallets',
wallets: [
coinbaseWallet({ chains, appName: 'Bridge' }),
argentWallet({ projectId, chains }),
bitgetWallet({ projectId, chains }),
rainbowWallet({ projectId, chains }),
phantomWallet({ chains })
],
}
])
const wagmiConfig = createConfig({
autoConnect: true,
connectors,
publicClient,
})
const disclaimer: DisclaimerComponent = ({ Text }) => (
<Text>
Thanks for choosing the Lux Bridge!
</Text>
)
const CustomAvatar: AvatarComponent = ({ address, size }) => {
return <AddressIcon address={address} size={size} />
}
return (
<WagmiConfig config={wagmiConfig}>
<RainbowKitProvider
avatar={CustomAvatar}
modalSize="compact"
chains={chains}
theme={rkTheme}
appInfo={{
appName: 'LuxBridge',
learnMoreUrl: 'https://docs.bridge.lux.network/',
disclaimer: disclaimer
}}
>
{children}
</RainbowKitProvider>
</WagmiConfig>
)
}
export default RainbowKitComponent
-49
View File
@@ -1,49 +0,0 @@
'use client'
import { useMeasure } from "@uidotdev/usehooks";
import { AnimatePresence, motion } from "framer-motion";
import { ReactNode } from "react";
export default function ResizablePanel({ children, className }: { children: ReactNode, className?: string }) {
let [ref, { height }] = useMeasure();
return (
<motion.div
animate={{ height: height || "auto" }}
className="relative overflow-hidden"
>
<AnimatePresence initial={false}>
<motion.div
key={JSON.stringify(children, ignoreCircularReferences())}
initial={{
x: 382,
}}
animate={{
x: 0,
}}
exit={{
x: -382,
}}
>
<div
ref={ref}
className={`${className}`}
>
{children}
</div>
</motion.div>
</AnimatePresence>
</motion.div>
);
}
const ignoreCircularReferences = () => {
const seen = new WeakSet();
return (key, value) => {
if (key.startsWith("_")) return; // Don't compare React's internal props.
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return;
seen.add(value);
}
return value;
};
};
@@ -1,31 +0,0 @@
import { FC } from 'react'
import { Widget } from '../../Widget/Index';
const External: FC = () => {
return (
<Widget.Content>
<div className="w-full py-12 grid grid-flow-row">
<div className='md:text-3xl text-lg font-bold leading-6 text-center'>
Withdrawal pending
</div>
<div className='flex place-content-center mt-20 mb-16 md:mb-8'>
<div className='relative'>
<div className='absolute top-1 left-1 w-10 h-10 opacity-40 bg bg-primary rounded-full animate-ping'></div>
<div className='absolute top-2 left-2 w-8 h-8 opacity-40 bg bg-primary rounded-full animate-ping'></div>
<div className='relative top-0 left-0 w-12 h-12 scale-75 bg bg-primary-800 rounded-full'></div>
</div>
</div>
<div className="flex flex-col text-center place-content-center mt-1 text-lg font-lighter ">
<p className="text-base font-medium space-y-6 text-center">
The withdrawal has been initiated, please don&apos;t close this screen.
</p>
</div>
</div>
</Widget.Content>
)
}
export default External;
@@ -1,20 +0,0 @@
'use client'
import { FC } from 'react'
import { useSwapDataState } from '../../../../context/swap';
import { useSettingsState } from '../../../../context/settings';
import Processing from './Processing';
const Component: FC = () => {
const { swap } = useSwapDataState()
const settings = useSettingsState()
return (
<>
{swap && <Processing settings={settings} swap={swap} />}
</>
)
}
export default Component;
@@ -1,74 +0,0 @@
'use client'
import { ExternalLink } from 'lucide-react';
import { Home } from 'lucide-react';
import { FC, useCallback } from 'react'
import { useSettingsState } from '../../../context/settings';
import { useSwapDataState } from '../../../context/swap';
import MessageComponent from '../../MessageComponent';
import { Widget } from '../../Widget/Index';
import SubmitButton, { DoubleLineText } from '../../buttons/submitButton';
import GoHomeButton from '../../utils/GoHome';
import { TransactionType } from '../../../lib/BridgeApiClient';
import AppSettings from '../../../lib/AppSettings';
import { useRouter } from 'next/router';
import { useQueryState } from '../../../context/query';
const Success: FC = () => {
const { layers } = useSettingsState()
const { swap } = useSwapDataState()
const router = useRouter()
const { externalId } = useQueryState()
const { destination_network: destination_network_internal_name } = swap || {}
const destination_network = layers.find(n => n.internal_name === destination_network_internal_name)
const transaction_explorer_template = destination_network?.transaction_explorer_template
const swapOutputTransaction = swap?.transactions?.find(t => t.type === TransactionType.Output)
const handleViewInExplorer = useCallback(() => {
if (!transaction_explorer_template)
return
window.open(`${AppSettings.ExplorerURl}/${swapOutputTransaction?.transaction_id}`, '_blank')
}, [transaction_explorer_template])
return (
<>
<Widget.Footer>
<MessageComponent.Buttons>
<div className="flex flex-row text-base space-x-2">
{!externalId &&
((transaction_explorer_template && swapOutputTransaction?.transaction_id) ?
<>
<div className='grow'>
<SubmitButton text_align='left' buttonStyle='filled' isDisabled={false} isSubmitting={false} onClick={handleViewInExplorer} icon={<ExternalLink className='h-5 w-5' />}>
<DoubleLineText
colorStyle='mltln-text-light'
primaryText='View in Explorer'
secondarytext=''
/>
</SubmitButton>
</div>
</>
:
<div className='grow'>
<GoHomeButton>
<SubmitButton className='plausible-event-name=Swap+more' text_align='center' buttonStyle='outline' isDisabled={false} isSubmitting={false} icon={<Home className="h-5 w-5" aria-hidden="true" />}>
Swap more
</SubmitButton>
</GoHomeButton>
</div>)
}
{
externalId && transaction_explorer_template && swapOutputTransaction?.transaction_id &&
<div className='grow'>
<SubmitButton text_align='center' buttonStyle='outline' isDisabled={false} isSubmitting={false} onClick={handleViewInExplorer} icon={<ExternalLink className='h-5 w-5' />}>
View in explorer
</SubmitButton>
</div>
}
</div>
</MessageComponent.Buttons>
</Widget.Footer>
</>
)
}
export default Success;
@@ -1,43 +0,0 @@
import { FC, type PropsWithChildren } from 'react'
import { ConnectButton } from '@rainbow-me/rainbowkit';
const RainbowKit: FC<PropsWithChildren> = ({ children }) => (
<ConnectButton.Custom>
{({
account,
chain,
openChainModal,
openConnectModal,
mounted,
}) => {
const ready = mounted;
const connected = ready && account && chain;
return (
<div
{...(!ready && {
'aria-hidden': true,
'style': {
opacity: 0,
pointerEvents: 'none',
userSelect: 'none',
},
})}
>
{(!connected) && (
<span className='w-full cursor-pointer' onClick={openConnectModal} >
{children}
</span>
)}
{ chain?.unsupported && (
<button onClick={openChainModal} type="button">
Change network
</button>
)}
</div>
)
}}
</ConnectButton.Custom>
)
export default RainbowKit;
@@ -1,155 +0,0 @@
'use client'
import { FC, useCallback, useEffect, useState } from "react";
import {
useAccount,
useContractWrite,
usePrepareContractWrite,
useWaitForTransaction,
useNetwork,
erc20ABI,
} from "wagmi";
import { PublishedSwapTransactionStatus } from "../../../../../lib/BridgeApiClient";
import WalletIcon from "../../../../icons/WalletIcon";
import { encodeFunctionData, http, parseUnits, createWalletClient, publicActions } from 'viem'
import TransactionMessage from "./transactionMessage";
import { BaseTransferButtonProps } from "./sharedTypes";
import { ButtonWrapper } from "./buttons";
import { useSwapTransactionStore } from "../../../../../stores/swapTransactionStore";
import useWalletTransferOptions from "../../../../../hooks/useWalletTransferOptions";
import { SendTransactionData } from "../../../../../lib/telegram";
type TransferERC20ButtonProps = BaseTransferButtonProps & {
tokenContractAddress: `0x${string}`,
tokenDecimals: number,
}
const TransferErc20Button: FC<TransferERC20ButtonProps> = ({
depositAddress,
amount,
tokenContractAddress,
tokenDecimals,
savedTransactionHash,
swapId,
sequenceNumber,
userDestinationAddress,
}) => {
const [applyingTransaction, setApplyingTransaction] = useState<boolean>(!!savedTransactionHash)
const { address } = useAccount();
const [buttonClicked, setButtonClicked] = useState(false)
const [estimatedGas, setEstimatedGas] = useState<bigint>()
const { setSwapTransaction } = useSwapTransactionStore();
const { canDoSweepless, isContractWallet } = useWalletTransferOptions()
const contractWritePrepare = usePrepareContractWrite({
enabled: !!depositAddress && isContractWallet?.ready,
address: tokenContractAddress,
abi: erc20ABI,
functionName: 'transfer',
gas: estimatedGas,
args: depositAddress ? [depositAddress, parseUnits(amount.toString(), tokenDecimals)] : undefined,
});
let encodedData = depositAddress && contractWritePrepare?.config?.request
&& encodeFunctionData({
...contractWritePrepare?.config?.request,
});
if (encodedData && canDoSweepless && address !== userDestinationAddress) {
encodedData = encodedData ? `${encodedData}${sequenceNumber}` as `0x${string}` : encodedData;
}
const tx = {
...contractWritePrepare?.config,
request: {
...contractWritePrepare?.config?.request,
data: encodedData
}
}
const { chain } = useNetwork();
const publicClient = createWalletClient({
account: address,
chain: chain,
transport: http(),
}).extend(publicActions);
useEffect(() => {
(async () => {
if (encodedData && address) {
const estimate = await publicClient.estimateGas({
data: encodedData,
to: tokenContractAddress,
account: address,
})
setEstimatedGas(estimate)
}
})()
}, [address, encodedData, depositAddress, amount, tokenDecimals, tx])
const contractWrite = useContractWrite(tx)
useEffect(() => {
try {
if (contractWrite?.data?.hash) {
setSwapTransaction(swapId, PublishedSwapTransactionStatus.Pending, contractWrite?.data?.hash);
if (!!isContractWallet?.isContract)
SendTransactionData(swapId, contractWrite?.data?.hash)
}
}
catch (e) {
//TODO log to logger
console.error(e.message)
}
}, [contractWrite?.data?.hash, swapId, isContractWallet?.isContract])
const clickHandler = useCallback(() => {
setButtonClicked(true)
contractWrite?.write && contractWrite?.write()
}, [contractWrite])
const waitForTransaction = useWaitForTransaction({
hash: contractWrite?.data?.hash || savedTransactionHash,
onSuccess: async (trxRcpt) => {
setApplyingTransaction(true)
setSwapTransaction(swapId, PublishedSwapTransactionStatus.Completed, trxRcpt.transactionHash);
setApplyingTransaction(false)
},
onError: async (err) => {
if (contractWrite?.data?.hash)
setSwapTransaction(swapId, PublishedSwapTransactionStatus.Error, contractWrite.data.hash, err.message);
}
})
const isError = [
contractWritePrepare,
waitForTransaction,
contractWrite
].find(d => d.isError)
const isLoading = [
waitForTransaction,
contractWrite
].find(d => d.isLoading)
return <>
{
buttonClicked &&
<TransactionMessage
prepare={contractWritePrepare}
transaction={contractWrite}
wait={waitForTransaction}
applyingTransaction={applyingTransaction}
/>
}
{
!isLoading &&
<ButtonWrapper
clcikHandler={clickHandler}
disabled={contractWritePrepare?.isLoading || contractWritePrepare.status === "idle"}
icon={<WalletIcon className="stroke-2 w-6 h-6" />}
>
{(isError && buttonClicked) ? <span>Try again</span>
: <span>Send from wallet</span>}
</ButtonWrapper>
}
</>
}
export default TransferErc20Button
@@ -1,181 +0,0 @@
'use client'
import { FC, useCallback, useEffect, useState } from "react";
import {
useAccount,
usePrepareSendTransaction,
useSendTransaction,
useWaitForTransaction,
useNetwork,
} from "wagmi";
import { parseEther, createPublicClient, http } from 'viem'
import SubmitButton from "../../../../buttons/submitButton";
import { PublishedSwapTransactionStatus } from "../../../../../lib/BridgeApiClient";
import WalletIcon from "../../../../icons/WalletIcon";
import Modal from '../../../../modal/modal';
import MessageComponent from "../../../../MessageComponent";
import { BaseTransferButtonProps } from "./sharedTypes";
import TransactionMessage from "./transactionMessage";
import { ButtonWrapper } from "./buttons";
import { useSwapTransactionStore } from "../../../../../stores/swapTransactionStore";
import useWalletTransferOptions from "../../../../../hooks/useWalletTransferOptions";
import { SendTransactionData } from "../../../../../lib/telegram";
type TransferNativeTokenButtonProps = BaseTransferButtonProps & {
chainId: number,
}
const TransferNativeTokenButton: FC<TransferNativeTokenButtonProps> = ({
depositAddress,
chainId,
amount,
savedTransactionHash,
swapId,
userDestinationAddress,
sequenceNumber,
}) => {
const [applyingTransaction, setApplyingTransaction] = useState<boolean>(!!savedTransactionHash)
const [buttonClicked, setButtonClicked] = useState(false)
const [openChangeAmount, setOpenChangeAmount] = useState(false)
const [estimatedGas, setEstimatedGas] = useState<bigint>()
const { address } = useAccount();
const { setSwapTransaction } = useSwapTransactionStore();
const { canDoSweepless, isContractWallet } = useWalletTransferOptions()
const sendTransactionPrepare = usePrepareSendTransaction({
enabled: !!depositAddress && isContractWallet?.ready,
to: depositAddress,
value: amount ? parseEther(amount.toString()) : undefined,
chainId: chainId,
})
const encodedData: `0x${string}` = (canDoSweepless && address !== userDestinationAddress) ? `0x${sequenceNumber}` : "0x"
const tx = {
to: depositAddress,
value: amount ? parseEther(amount?.toString()) : undefined,
gas: estimatedGas,
data: encodedData
}
const transaction = useSendTransaction(tx)
const { chain } = useNetwork();
const publicClient = createPublicClient({
chain: chain,
transport: http()
})
useEffect(() => {
(async () => {
if (address && depositAddress) {
const gasEstimate = await publicClient.estimateGas({
account: address,
to: depositAddress,
data: encodedData,
})
setEstimatedGas(gasEstimate)
}
})()
}, [address, encodedData, depositAddress, amount])
useEffect(() => {
try {
if (transaction?.data?.hash) {
setSwapTransaction(swapId, PublishedSwapTransactionStatus.Pending, transaction?.data?.hash)
if (!!isContractWallet?.isContract)
SendTransactionData(swapId, transaction?.data?.hash)
}
}
catch (e) {
//TODO log to logger
console.error(e.message)
}
}, [transaction?.data?.hash, swapId, isContractWallet?.isContract])
const waitForTransaction = useWaitForTransaction({
hash: transaction?.data?.hash || savedTransactionHash,
onSuccess: async (trxRcpt) => {
setApplyingTransaction(true)
setSwapTransaction(swapId, PublishedSwapTransactionStatus.Completed, trxRcpt.transactionHash);
setApplyingTransaction(false)
},
onError: async (err) => {
if (transaction?.data?.hash)
setSwapTransaction(swapId, PublishedSwapTransactionStatus.Error, transaction?.data?.hash, err.message);
}
})
const clickHandler = useCallback(async () => {
setButtonClicked(true)
return transaction?.sendTransaction && transaction?.sendTransaction()
}, [transaction, estimatedGas])
const isError = [
sendTransactionPrepare,
transaction,
waitForTransaction
].find(d => d.isError)
const isLoading = [
transaction,
waitForTransaction
].find(d => d.isLoading)
return <>
{
buttonClicked &&
<TransactionMessage
prepare={sendTransactionPrepare}
transaction={transaction}
wait={waitForTransaction}
applyingTransaction={applyingTransaction}
/>
}
{
!isLoading &&
<ButtonWrapper
clcikHandler={clickHandler}
disabled={sendTransactionPrepare?.isLoading || sendTransactionPrepare.status === "idle"}
icon={<WalletIcon className="stroke-2 w-6 h-6" />}
>
{(isError && buttonClicked) ? <span>Try again</span>
: <span>Send from wallet</span>}
</ButtonWrapper>
}
<Modal
height="80%"
show={openChangeAmount}
setShow={setOpenChangeAmount}
>
<MessageComponent>
<div className="space-y-4">
<div className='md:text-2xl text-lg font-bold leading-6 text-center'>
Insufficient funds for gas
</div>
<div className="text-base font-medium space-y-6 text-center">
This transfer can&apos;t be processed because you don&apos;t have enough gas.
</div>
</div>
<div className="text-base">
You have requested swap with {amount}
</div>
<MessageComponent.Buttons>
<div className="flex flex-row text-base space-x-2">
<div className='basis-1/3'>
<SubmitButton onClick={() => { setOpenChangeAmount(false); clickHandler() }} text_align='left' isDisabled={false} isSubmitting={false} buttonStyle='filled' >
Transfer
</SubmitButton>
</div>
<div className='basis-2/3'>
<SubmitButton onClick={() => setOpenChangeAmount(false)} button_align='right' text_align='left' isDisabled={false} isSubmitting={false} buttonStyle='outline' >
Cancel
</SubmitButton>
</div>
</div>
</MessageComponent.Buttons>
</MessageComponent>
</Modal>
</>
}
export default TransferNativeTokenButton
@@ -1,100 +0,0 @@
'use client'
import { FC, useEffect, useState } from "react";
import {
useAccount,
useSwitchNetwork,
useNetwork,
} from "wagmi";
import { PublishedSwapTransactions } from "../../../../../lib/BridgeApiClient";
import TransferNativeTokenButton from "./TransferNativeToken";
import { ChangeNetworkButton, ConnectWalletButton } from "./buttons";
import TransferErc20Button from "./TransferErc20";
type Props = {
sequenceNumber: number,
chainId: number,
depositAddress?: `0x${string}`,
tokenContractAddress?: `0x${string}` | null,
userDestinationAddress: `0x${string}`,
amount: number,
tokenDecimals: number,
networkDisplayName: string,
swapId: string;
}
const TransferFromWallet: FC<Props> = ({ networkDisplayName,
chainId,
depositAddress,
userDestinationAddress,
amount,
tokenContractAddress,
tokenDecimals,
sequenceNumber,
swapId,
}) => {
const { isConnected } = useAccount();
const networkChange = useSwitchNetwork({
chainId: chainId,
});
const { chain: activeChain } = useNetwork();
const [savedTransactionHash, setSavedTransactionHash] = useState<string>()
useEffect(() => {
if (activeChain?.id === chainId)
networkChange.reset()
}, [activeChain, chainId])
useEffect(() => {
try {
const data: PublishedSwapTransactions = JSON.parse(localStorage.getItem('swapTransactions') || "{}")
const hash = data.state.swapTransactions?.[swapId]?.hash;
if (hash)
setSavedTransactionHash(hash)
}
catch (e) {
//TODO log to logger
console.error(e.message)
}
}, [swapId])
const hexed_sequence_number = sequenceNumber?.toString(16)
const sequence_number_even = (hexed_sequence_number?.length % 2 > 0 ? `0${hexed_sequence_number}` : hexed_sequence_number)
if (!isConnected) {
return <ConnectWalletButton />
}
else if (activeChain?.id !== chainId) {
return <ChangeNetworkButton
chainId={chainId}
network={networkDisplayName}
/>
}
else if (tokenContractAddress) {
return <TransferErc20Button
swapId={swapId}
sequenceNumber={sequence_number_even}
amount={amount}
depositAddress={depositAddress}
userDestinationAddress={userDestinationAddress}
savedTransactionHash={savedTransactionHash as `0x${string}`}
tokenContractAddress={tokenContractAddress}
tokenDecimals={tokenDecimals}
/>
}
else {
return <TransferNativeTokenButton
swapId={swapId}
sequenceNumber={sequence_number_even}
amount={amount}
depositAddress={depositAddress}
userDestinationAddress={userDestinationAddress}
savedTransactionHash={savedTransactionHash as `0x${string}`}
chainId={chainId}
/>
}
}
export default TransferFromWallet
@@ -1,36 +0,0 @@
import { BaseError, InsufficientFundsError, EstimateGasExecutionError, UserRejectedRequestError } from 'viem'
type ResolvedError = "insufficient_funds" | "transaction_rejected"
const resolveError = (error: BaseError): ResolvedError | undefined => {
const isInsufficientFundsError = typeof error?.walk === "function" && error?.walk((e: BaseError) => (e instanceof InsufficientFundsError)
|| (e instanceof EstimateGasExecutionError) || e?.['data']?.args?.some((a: string) => a?.includes("amount exceeds")))
if (isInsufficientFundsError)
return "insufficient_funds"
const isUserRejectedRequestError = typeof error?.walk === "function" && error?.walk && error?.walk((e: BaseError) => e instanceof UserRejectedRequestError) instanceof UserRejectedRequestError
if (isUserRejectedRequestError)
return "transaction_rejected"
const code_name = error?.['code']
|| error?.["name"]
const inner_code = error?.['data']?.['code']
|| error?.['cause']?.['code']
|| error?.["cause"]?.["cause"]?.["cause"]?.["code"]
if (code_name === 'INSUFFICIENT_FUNDS'
|| code_name === 'UNPREDICTABLE_GAS_LIMIT'
|| (code_name === -32603 && inner_code === 3)
|| inner_code === -32000
|| code_name === 'EstimateGasExecutionError')
return "insufficient_funds"
else if (code_name === 4001) {
return "transaction_rejected"
}
}
export default resolveError
@@ -1,143 +0,0 @@
'use client'
import { Link, ArrowLeftRight } from 'lucide-react';
import { FC, useCallback, useEffect, useState } from 'react'
import SubmitButton from '../../../buttons/submitButton';
import toast from 'react-hot-toast';
import * as zksync from 'zksync';
import { utils } from 'ethers';
import { useEthersSigner } from '../../../../lib/ethersToViem/ethers';
import { useSwapTransactionStore } from '../../../../stores/swapTransactionStore';
import { PublishedSwapTransactionStatus } from '../../../../lib/BridgeApiClient';
import { useSwapDataState } from '../../../../context/swap';
import { ChangeNetworkButton, ConnectWalletButton } from './WalletTransfer/buttons';
import { useSettingsState } from '../../../../context/settings';
import { useNetwork } from 'wagmi';
import { TransactionReceipt } from 'zksync/build/types';
type Props = {
depositAddress: string,
amount: number
}
const ZkSyncWalletWithdrawStep: FC<Props> = ({ depositAddress, amount }) => {
const [loading, setLoading] = useState(false);
const [transferDone, setTransferDone] = useState<boolean>();
const [syncWallet, setSyncWallet] = useState<zksync.Wallet | null>()
const { setSwapTransaction } = useSwapTransactionStore();
const { swap } = useSwapDataState();
const signer = useEthersSigner();
const { chain } = useNetwork();
const { layers } = useSettingsState();
const { source_network: source_network_internal_name } = swap || {};
const source_network = layers.find(n => n.internal_name === source_network_internal_name);
const source_layer = layers.find(l => l.internal_name === source_network_internal_name)
const source_currency = source_network?.assets?.find(c => c.asset.toLocaleUpperCase() === swap?.source_asset.toLocaleUpperCase());
const defaultProvider = swap?.source_network?.split('_')?.[1]?.toLowerCase() == "mainnet" ? "mainnet" : "goerli";
const l1Network = layers.find(n => n.internal_name === source_network?.metadata?.L1Network);
useEffect(() => {
if (signer?._address !== syncWallet?.cachedAddress && source_layer) {
setSyncWallet(null)
}
}, [signer?._address]);
const handleTransaction = async (swapId: string, publishedTransaction: TransactionReceipt, txHash: string) => {
if (publishedTransaction?.failReason) {
txHash && setSwapTransaction(swapId, PublishedSwapTransactionStatus.Error, txHash, publishedTransaction?.failReason);
toast(String(publishedTransaction.failReason))
}
else {
txHash && setSwapTransaction(swapId, PublishedSwapTransactionStatus.Completed, txHash, publishedTransaction?.failReason);
setTransferDone(true)
}
};
const handleConnect = useCallback(async () => {
if (!signer)
return
setLoading(true)
try {
const syncProvider = await zksync.getDefaultProvider(defaultProvider);
const wallet = await zksync.Wallet.fromEthSigner(signer, syncProvider);
setSyncWallet(wallet)
}
catch (e) {
toast(e.message)
}
finally {
setLoading(false)
}
}, [signer, defaultProvider])
const handleTransfer = useCallback(async () => {
if (!swap) return
setLoading(true)
try {
const tf = await syncWallet?.syncTransfer({
to: depositAddress,
token: swap?.source_asset,
amount: zksync.closestPackableTransactionAmount(utils.parseUnits(amount.toString(), source_currency?.decimals)),
validUntil: zksync.utils.MAX_TIMESTAMP - swap?.sequence_number,
});
const txHash = tf?.txHash?.replace('sync-tx:', '')
if (txHash) {
const syncProvider = await zksync.getDefaultProvider(defaultProvider);
const txReceipt = await syncProvider.getTxReceipt(String(tf?.txHash));
//TODO might be unnecessary why handleTransaction does not do this
if (!txReceipt.executed)
setSwapTransaction(swap?.id, PublishedSwapTransactionStatus.Pending, txHash);
else
handleTransaction(swap?.id, txReceipt, String(tf?.txHash))
}
}
catch (e) {
if (e?.message) {
toast(e.message)
return
}
}
setLoading(false)
}, [syncWallet, swap, depositAddress, source_currency, amount])
if (!signer) {
return <ConnectWalletButton />
}
if (l1Network && chain?.id !== Number(l1Network.chain_id)) {
return (
<ChangeNetworkButton
chainId={Number(l1Network?.chain_id)}
network={l1Network?.display_name}
/>
)
}
return (
<>
<div className="w-full space-y-5 flex flex-col justify-between h-full ">
<div className='space-y-4'>
{
!syncWallet &&
<SubmitButton isDisabled={loading} isSubmitting={loading} onClick={handleConnect} icon={<Link className="h-5 w-5 ml-2" aria-hidden="true" />} >
Authorize to Send on zkSync
</SubmitButton>
}
{
syncWallet &&
<SubmitButton isDisabled={!!(loading || transferDone)} isSubmitting={!!(loading || transferDone)} onClick={handleTransfer} icon={<ArrowLeftRight className="h-5 w-5 ml-2" aria-hidden="true" />} >
Transfer
</SubmitButton>
}
</div>
</div>
</>
)
}
export default ZkSyncWalletWithdrawStep;
@@ -1,260 +0,0 @@
import { AlignLeft, X } from 'lucide-react';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
import WalletTransfer from './Wallet';
import ManualTransfer from './ManualTransfer';
import FiatTransfer from './FiatTransfer';
import { useSettingsState } from '../../../context/settings';
import { useSwapDataState, useSwapDataUpdate } from '../../../context/swap';
import KnownInternalNames from '../../../lib/knownIds';
import { Tab, TabHeader } from '../../Tabs/Index';
import SwapSummary from '../Summary';
import Coinbase from './Coinbase';
import External from './External';
import { WithdrawType } from '../../../lib/BridgeApiClient';
import WalletIcon from '../../icons/WalletIcon';
import shortenAddress, { shortenEmail } from '../../utils/ShortenAddress';
import { useAccountModal } from '@rainbow-me/rainbowkit';
import Image from 'next/image';
import SpinIcon from '../../icons/spinIcon';
import { NetworkType } from '../../../Models/CryptoNetwork';
import useWallet from '../../../hooks/useWallet';
import { useQueryState } from '../../../context/query';
import { Widget } from '../../Widget/Index';
const Withdraw: FC = () => {
const { swap } = useSwapDataState()
const { setWithdrawType } = useSwapDataUpdate()
const { layers } = useSettingsState()
const { appName, signature } = useQueryState()
const source_internal_name = swap?.source_exchange ?? swap?.source_network
const source = layers.find(n => n.internal_name === source_internal_name)
const sourceIsStarknet = swap?.source_network?.toUpperCase() === KnownInternalNames.Networks.StarkNetMainnet?.toUpperCase()
|| swap?.source_network === KnownInternalNames.Networks.StarkNetGoerli?.toUpperCase()
const sourceIsImmutableX = swap?.source_network?.toUpperCase() === KnownInternalNames.Networks.ImmutableXMainnet?.toUpperCase()
|| swap?.source_network === KnownInternalNames.Networks.ImmutableXGoerli?.toUpperCase()
const sourceIsZkSync = swap?.source_network?.toUpperCase() === KnownInternalNames.Networks.ZksyncMainnet?.toUpperCase()
const sourceIsArbitrumOne = swap?.source_network?.toUpperCase() === KnownInternalNames.Networks.ArbitrumMainnet?.toUpperCase()
|| swap?.source_network === KnownInternalNames.Networks.ArbitrumGoerli?.toUpperCase()
const sourceIsCoinbase = swap?.source_exchange?.toUpperCase() === KnownInternalNames.Exchanges.Coinbase?.toUpperCase()
const source_layer = layers.find(n => n.internal_name === swap?.source_network)
const sourceNetworkType = source_layer?.type
const manualIsAvailable = !(sourceIsStarknet || sourceIsImmutableX)
const walletIsAvailable = !swap?.source_exchange
&& (sourceNetworkType === NetworkType.EVM
|| sourceNetworkType === NetworkType.Starknet
|| sourceIsImmutableX || sourceIsZkSync)
const isImtblMarketplace = (signature && appName === "imxMarketplace" && sourceIsImmutableX)
const sourceIsSynquote = appName === "ea7df14a1597407f9f755f05e25bab42" && sourceIsArbitrumOne
let tabs: Tab[] = []
// TODO refactor
if (isImtblMarketplace || sourceIsSynquote) {
tabs = [{
id: WithdrawType.External,
label: "Withdrawal pending",
enabled: true,
icon: <WalletIcon className='stroke-2 w-6 h-6 -ml-0.5' />,
content: <External />
}]
}
else if (sourceIsStarknet || sourceIsImmutableX) {
tabs = [
{
id: WithdrawType.Wallet,
label: "Via wallet",
enabled: true,
icon: <WalletIcon className='stroke-2 w-6 h-6 -ml-0.5' />,
content: <WalletTransferContent />,
footer: <WalletTransfer />
}]
}
else {
tabs = [
// {
// id: WithdrawType.Wallet,
// label: "Via wallet",
// enabled: walletIsAvailable,
// icon: <WalletIcon className='stroke-2 w-6 h-6 -ml-0.5' />,
// content: <WalletTransferContent />,
// footer: <WalletTransfer />
// },
// {
// id: WithdrawType.Coinbase,
// label: "Automatically",
// enabled: sourceIsCoinbase,
// icon: <WalletIcon className='stroke-2 w-6 h-6 -ml-0.5' />,
// content: <WalletTransferContent />,
// footer: <Coinbase />
// },
{
id: WithdrawType.Manually,
label: "Manually",
enabled: manualIsAvailable,
icon: <AlignLeft />,
footer: <ManualTransfer />,
content: <></>
}
];
}
const [activeTabId, setActiveTabId] = useState(tabs.find(t => t.enabled)?.id);
const activeTab = tabs.find(t => t.id === activeTabId)
const showTabsHeader = tabs?.filter(t => t.enabled)?.length > 1
useEffect(() => {
activeTab && setWithdrawType(activeTab.id)
}, [activeTab])
return (
<>
<Widget.Content>
<div className="w-full flex flex-col justify-between ">
<div className='grid grid-cols-1'>
<div className="bg-level-1 rounded-lg px-3 py-4 border border-[#404040] w-full relative z-10 space-y-4">
<SwapSummary />
</div>
<span>
{
showTabsHeader &&
<>
<div className="mb-4 ml-1 text-base">Choose how you&apos;d like to complete the swap</div>
<div className="flex space-x-3 w-full">
{activeTabId && tabs.filter(t => t.enabled).map((tab) => (
<TabHeader
activeTabId={activeTabId}
onCLick={setActiveTabId}
tab={tab}
key={tab.id}
/>
))}
</div>
</>
}
</span>
<span>
{activeTab?.content}
</span>
</div>
</div>
</Widget.Content>
{
activeTab?.footer &&
<Widget.Footer sticky={true} key={activeTabId}>
{activeTab?.footer}
</Widget.Footer>
}
</>
)
}
const WalletTransferContent: FC = () => {
const { openAccountModal } = useAccountModal();
const { getWithdrawalProvider: getProvider, disconnectWallet } = useWallet()
const { layers, resolveImgSrc } = useSettingsState()
const { swap } = useSwapDataState()
const [isLoading, setIsloading] = useState(false);
const { mutateSwap } = useSwapDataUpdate()
const {
source_network: source_network_internal_name,
source_exchange: source_exchange_internal_name } = swap || {}
const source_network = layers.find(n => n.internal_name === source_network_internal_name)
const source_exchange = layers.find(n => n.internal_name === source_exchange_internal_name)
const source_layer = layers.find(n => n.internal_name === swap?.source_network)
const sourceNetworkType = source_network?.type
const provider = useMemo(() => {
return source_layer && getProvider(source_layer)
}, [source_layer, getProvider])
const wallet = provider?.getConnectedWallet()
const handleDisconnect = useCallback(async (e: React.MouseEvent<HTMLDivElement>) => {
if (!wallet) return
setIsloading(true);
await disconnectWallet(wallet.providerName, swap)
if (source_exchange) await mutateSwap()
setIsloading(false);
e?.stopPropagation();
}, [sourceNetworkType, swap?.source_exchange, disconnectWallet])
let accountAddress: string | undefined = ""
if (swap?.source_exchange) {
accountAddress = swap.exchange_account_name || ""
}
else if (wallet) {
accountAddress = wallet.address || "";
}
const canOpenAccount = sourceNetworkType === NetworkType.EVM && !swap?.source_exchange
const handleOpenAccount = useCallback(() => {
if (canOpenAccount && openAccountModal)
openAccountModal()
}, [canOpenAccount, openAccountModal])
if (!accountAddress || (swap?.source_exchange && !swap.exchange_account_connected)) {
return (
<div className='flex justify-center'>
<WalletIcon className='w-12 text-muted-3' />
</div>
)
}
return <div className="grid content-end">
{
<span className='mb-1 ml-1 text-sm'>{swap?.source_exchange ? "Connected account" : "Connected wallet"}</span>
}
<div
onClick={handleOpenAccount}
className={(canOpenAccount ? 'cursor-pointer' : 'cursor-auto') +
' text-left min-h-12 space-x-2 border border-[#404040] bg-level-1 flex text-sm rounded-md' +
' items-center w-full pl-4 pr-2 py-1.5'}
>
<div className='flex bg-level-3 flex-row items-left rounded-md p-1'>
{
!swap?.source_exchange
&& wallet?.connector
&& <wallet.icon
className="w-6 h-6 rounded-full"
/>
}
{
source_exchange
&& <Image
className="w-6 h-6 rounded-full p-0"
src={resolveImgSrc(source_exchange)}
alt={accountAddress}
width={25}
height={25} />
}
</div>
<div className="flex flex-col grow">
<div className="block text-md font-medium ">
{!swap?.source_exchange && <span>
{shortenAddress(accountAddress)}
</span>}
{swap?.source_exchange && <span>
{shortenEmail(swap?.exchange_account_name)}
</span>}
</div>
</div>
<div
onClick={handleDisconnect}
className={'cursor-pointer flex flex-row items-left p-2 rounded-md transform ' +
'hover:bg-level-2 transition duration-200 hover:border-[#404040] hover:shadow-xl'
}
>
{isLoading ? <SpinIcon className="animate-spin h-5 w-5" /> : <X className='h-5' />}
</div>
</div>
</div>
}
export default Withdraw
-75
View File
@@ -1,75 +0,0 @@
'use client'
import { FC } from 'react'
import { Widget } from '../Widget/Index';
import { useSwapDataState } from '../../context/swap';
import Withdraw from './Withdraw';
import Processing from './Withdraw/Processing';
import { PublishedSwapTransactionStatus, TransactionType } from '../../lib/BridgeApiClient';
import { SwapStatus } from '../../Models/SwapStatus';
import GasDetails from '../gasDetails';
import { useSettingsState } from '../../context/settings';
type Props = {
type: "widget" | "contained",
}
import { useSwapTransactionStore } from '../../stores/swapTransactionStore';
const SwapDetails: FC<Props> = ({ type }) => {
const { swap } = useSwapDataState()
const settings = useSettingsState()
const swapStatus = swap?.status;
const storedWalletTransactions = useSwapTransactionStore()
const swapInputTransaction = swap?.transactions?.find(t => t.type === TransactionType.Input)
const storedWalletTransaction = storedWalletTransactions.swapTransactions?.[swap?.id || '']
const sourceNetwork = settings.layers.find(l => l.internal_name === swap?.source_network)
const currency = sourceNetwork?.assets.find(c => c.asset === swap?.source_asset)
if (!swap) {
return (
<div className="w-full h-[430px]">
<div className="animate-pulse flex space-x-4">
<div className="flex-1 space-y-6 py-1">
<div className="h-32 bg-level-1 rounded-lg"></div>
<div className="h-40 bg-level-1 rounded-lg"></div>
<div className="h-12 bg-level-1 rounded-lg"></div>
</div>
</div>
</div>
)
} else {
return (
<>
<Container type={type}>
{
((swapStatus === SwapStatus.UserTransferPending
&& !(swapInputTransaction || (storedWalletTransaction && storedWalletTransaction.status !== PublishedSwapTransactionStatus.Error)))) ?
<Withdraw /> : <Processing />
}
</Container>
{
process.env.NEXT_PUBLIC_SHOW_GAS_DETAILS === 'true'
&& sourceNetwork
&& currency &&
<GasDetails network={sourceNetwork} currency={currency} />
}
</>
)
}
}
const Container = ({ type, children }: Props & {
children: JSX.Element | JSX.Element[]
}) => {
if (type === "widget")
return <Widget><>{children}</></Widget>
else
return <div className="w-full flex flex-col justify-between h-full space-y-2">
{children}
</div>
}
export default SwapDetails
-34
View File
@@ -1,34 +0,0 @@
'use client'
import { FC, useEffect } from "react";
import { useSwapDataState, useSwapDataUpdate } from "../context/swap";
import SwapDetails from "./Swap";
import { Widget } from "./Widget/Index";
import NotFound from "./Swap/NotFound";
import { BalancesDataProvider } from "../context/balances";
const SwapWithdrawal: FC = () => {
const { swap, swapApiError } = useSwapDataState()
const { mutateSwap } = useSwapDataUpdate()
useEffect(() => {
mutateSwap()
}, [])
if (!swap)
return <Widget>
<div className={`pb-6 rounded-lg w-full overflow-hidden relative h-[548px]`}>
{swapApiError &&
<NotFound />
}
</div>
</Widget>
return (
<BalancesDataProvider>
<SwapDetails type="widget" />
</BalancesDataProvider>
)
};
export default SwapWithdrawal;
-17
View File
@@ -1,17 +0,0 @@
import React from "react";
import { useAtom } from "jotai";
import { useTelepoterAtom } from "@/store/teleport";
import Teleporter from "@/components/lux/teleport/swap/Form";
import SwapFireblock from "@/components/lux/fireblocks/swap/Form";
const Swapper: React.FC = () => {
const [useTeleporter] = useAtom(useTelepoterAtom);
if (useTeleporter) {
return <Teleporter />;
} else {
return <SwapFireblock />;
}
};
export default Swapper;
-65
View File
@@ -1,65 +0,0 @@
'use client'
import HeaderWithMenu from "../HeaderWithMenu"
import { useRouter } from "next/router"
import { default as Content } from './Content';
import { default as Footer } from './Footer';
import { useCallback, useRef } from "react";
import { resolvePersistantQueryParams } from "../../helpers/querryHelper";
import BridgeApiClient from "../../lib/BridgeApiClient";
type Props = {
children: JSX.Element | JSX.Element[];
className?: string;
hideMenu?: boolean;
}
const Widget = ({ children, className, hideMenu }: Props) => {
const router = useRouter()
const wrapper = useRef(null);
const goBack = useCallback(() => {
window?.['navigation']?.['canGoBack'] ?
router.back()
: router.push({
pathname: "/",
query: resolvePersistantQueryParams(router.query)
})
}, [])
const handleBack = (router.pathname === "/") ? null : goBack
const sandbox = BridgeApiClient.apiVersion === 'sandbox'
return <>
<div className='rounded-lg w-full sm:overflow-hidden relative border border-[#404040]'>
{sandbox && (
<div className="relative z-20 pb-2">
<div className="h-1 bg-secondary" />
<div className="absolute cursor-default -top-0.5 right-[calc(50%-68px)] bg-secondary py-0.5 px-10 rounded-b-md text-xs scale-75">
TESTNET
</div>
</div>
)}
{!hideMenu && (
<HeaderWithMenu goBack={handleBack} />
)}
<div className="relative px-6">
<div className="flex items-start" ref={wrapper}>
<div className='flex flex-nowrap grow'>
<div className={'w-full pb-6 flex flex-col justify-between space-y-1 h-full' + className ?? ''}>
{children}
</div>
</div>
</div>
</div>
<div id="widget_root" />
</div>
</>
}
Widget.Content = Content
Widget.Footer = Footer
export { Widget }
@@ -1,49 +0,0 @@
'use client'
import { useRouter } from "next/router";
import { FC, useCallback } from "react";
import { useFormWizardaUpdate } from "../../context/formWizardProvider";
import { TimerProvider } from "../../context/timerContext";
import { AuthStep, SwapCreateStep } from "../../Models/Wizard";
import { TrackEvent } from "../../pages/_document";
import CodeStep from "./Steps/CodeStep";
import EmailStep from "./Steps/EmailStep";
import Wizard from "./Wizard";
import WizardItem from "./WizardItem";
import { resolvePersistantQueryParams } from "../../helpers/querryHelper";
const AuthWizard: FC = () => {
const { goToStep } = useFormWizardaUpdate()
const router = useRouter();
const { redirect } = router.query;
const CodeOnNext = useCallback(async () => {
await router.push({
pathname: redirect?.toString() || '/',
query: resolvePersistantQueryParams(router.query)
})
plausible(TrackEvent.SignedIn)
}, [redirect]);
const GoBackToEmailStep = useCallback(() => goToStep(AuthStep.Email, "back"), [])
const GoToCodeStep = useCallback(() => goToStep(AuthStep.Code), [])
const handleGoBack = useCallback(() => {
router.back()
}, [router])
return (
<TimerProvider>
<Wizard>
<WizardItem StepName={SwapCreateStep.Email} GoBack={handleGoBack}>
<EmailStep OnNext={GoToCodeStep} />
</WizardItem>
<WizardItem StepName={SwapCreateStep.Code} GoBack={GoBackToEmailStep}>
<CodeStep OnNext={CodeOnNext} />
</WizardItem>
</Wizard>
</TimerProvider>
)
}
export default AuthWizard;
-59
View File
@@ -1,59 +0,0 @@
'use client'
import { NetworkCurrency } from "../Models/CryptoNetwork"
import { Layer } from "../Models/Layer"
import { useBalancesState } from "../context/balances"
const GasDetails = ({ network, currency }: { network: Layer, currency: NetworkCurrency }) => {
const { gases } = useBalancesState()
const networkGas = gases?.[network?.internal_name]?.find(g => g?.token === currency?.asset)
if (!networkGas?.gasDetails) return
return (
<div className='grid grid-cols-1 gap-2 px-3 py-2 rounded-lg border-2 border-[#404040] bg-level-1 mt-2 w-[350px] fixed top-0 left-2'>
<div className="flex flex-row items-baseline justify-between">
<label className="block text-left text-muted-4">
Gas limit
</label>
<span className="text-right">
{
networkGas.gasDetails?.gasLimit
}
</span>
</div>
<div className="flex flex-row items-baseline justify-between">
<label className="block text-left text-muted-4">
Gas price
</label>
<span className="text-right">
{
networkGas.gasDetails?.gasPrice
}
</span>
</div>
<div className="flex flex-row items-baseline justify-between">
<label className="block text-left text-muted-4">
Max fee per gas
</label>
<span className="text-right">
{
networkGas.gasDetails?.maxFeePerGas
}
</span>
</div>
<div className="flex flex-row items-baseline justify-between">
<label className="block text-left text-muted-4">
Max priority fee per gas
</label>
<span className="text-right">
{
networkGas.gasDetails?.maxPriorityFeePerGas
}
</span>
</div>
</div>
)
}
export default GasDetails
@@ -1,9 +0,0 @@
const FailIcon = (props) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="116" height="116" viewBox="0 0 116 116" fill="none">
<circle cx="58" cy="58" r="58" fill="#E43636" fillOpacity="0.1" />
<circle cx="58" cy="58" r="45" fill="#E43636" fillOpacity="0.5" />
<circle cx="58" cy="58" r="30" fill="#E43636" />
<path d="M48 69L68 48" stroke="white" strokeWidth="3.15789" strokeLinecap="round" />
<path d="M48 48L68 69" stroke="white" strokeWidth="3.15789" strokeLinecap="round" />
</svg>;
export default FailIcon;
@@ -1,8 +0,0 @@
const QuestionIcon = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" {...props} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" >
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
<path d="M12 17h.01" />
</svg>)
export default QuestionIcon;
@@ -1,7 +0,0 @@
const WalletIcon = (props) => (
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 15.1021V6.72875C3 5.75359 3 5.266 3.19856 4.89623C3.35879 4.59782 3.60628 4.35378 3.90891 4.19579C4.28392 4 4.77841 4 5.76738 4H16.066C17.0549 4 17.5494 4 17.9244 4.19579C18.227 4.35378 18.4745 4.59782 18.6348 4.89623C18.8333 5.266 18.8333 5.75359 18.8333 6.72875V8.23385M5.76738 21H19.2326C20.2216 21 20.7161 21 21.0911 20.8042C21.3937 20.6462 21.6412 20.4022 21.8014 20.1038C22 19.734 22 19.2464 22 18.2712V10.545C22 9.56983 22 9.08225 21.8014 8.71248C21.6412 8.41407 21.3937 8.17003 21.0911 8.01204C20.7161 7.81625 20.2216 7.81625 19.2326 7.81625H5.76738C4.77841 7.81625 4.28392 7.81625 3.90891 8.01204C3.60628 8.17003 3.35879 8.41407 3.19856 8.71248C3 9.08225 3 9.56983 3 10.545V18.2713C3 19.2464 3 19.734 3.19856 20.1038C3.35879 20.4022 3.60628 20.6462 3.90891 20.8042C4.28392 21 4.77841 21 5.76738 21ZM14.0834 13.3793H17.6019C17.699 13.3793 17.7778 13.3016 17.7778 13.2058C17.7778 13.11 17.6991 13.0323 17.6019 13.0323H14.0834C13.9862 13.0323 13.9074 13.11 13.9074 13.2058C13.9074 13.3016 13.9862 13.3793 14.0834 13.3793Z" stroke="currentColor" />
</svg>
);
export default WalletIcon;
@@ -1,11 +0,0 @@
const ArgentX = (props) => {
return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="8" fill="black" />
<path d="M18.4018 7.55556H13.5982C13.4377 7.55556 13.3091 7.68747 13.3056 7.85143C13.2085 12.4603 10.8484 16.8347 6.78608 19.9331C6.65711 20.0314 6.62773 20.2162 6.72202 20.3493L9.53253 24.3196C9.62815 24.4548 9.81444 24.4853 9.94558 24.386C12.4856 22.4613 14.5287 20.1395 16 17.566C17.4713 20.1395 19.5145 22.4613 22.0545 24.386C22.1856 24.4853 22.3719 24.4548 22.4676 24.3196L25.2781 20.3493C25.3723 20.2162 25.3429 20.0314 25.214 19.9331C21.1516 16.8347 18.7915 12.4603 18.6946 7.85143C18.6911 7.68747 18.5623 7.55556 18.4018 7.55556Z" fill="white" />
<path d="M24.7236 10.492L24.2231 8.92439C24.1213 8.60614 23.8734 8.35824 23.5577 8.26023L22.0039 7.77595C21.7895 7.70906 21.7873 7.40177 22.0011 7.33201L23.5469 6.82466C23.8609 6.72146 24.106 6.46952 24.2027 6.15011L24.6798 4.57502C24.7458 4.35709 25.0489 4.35477 25.1183 4.57156L25.6188 6.13915C25.7206 6.4574 25.9686 6.70531 26.2842 6.8039L27.838 7.28761C28.0524 7.3545 28.0547 7.66179 27.8408 7.73213L26.295 8.23948C25.9811 8.3421 25.736 8.59404 25.6393 8.91402L25.1621 10.4885C25.0961 10.7065 24.793 10.7088 24.7236 10.492Z" fill="white" />
</svg>
)
}
export default ArgentX
@@ -1,19 +0,0 @@
const BitKeep = (props) => <svg {...props} version="1.0" xmlns="http://www.w3.org/2000/svg"
width="170.000000pt" height="170.000000pt" viewBox="0 0 170.000000 170.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,170.000000) scale(0.100000,-0.100000)"
fill="#7524F9" stroke="none">
<path d="M0 850 l0 -850 850 0 850 0 0 850 0 850 -850 0 -850 0 0 -850z m1120
456 c129 -75 241 -144 248 -153 8 -11 12 -39 10 -73 l-3 -54 -97 -56 c-54 -30
-98 -57 -98 -60 0 -3 32 -23 72 -45 39 -22 84 -51 100 -63 28 -23 28 -25 28
-132 0 -99 -2 -111 -22 -133 -31 -34 -480 -287 -508 -287 -23 0 -179 85 -192
104 -4 6 -4 14 0 18 4 3 115 68 247 144 132 75 244 140 248 144 11 10 -189
124 -206 117 -6 -3 -126 -71 -266 -151 l-254 -146 -36 21 c-58 35 -71 55 -71
114 l0 53 365 209 c200 114 364 211 365 215 0 4 -45 32 -100 63 l-99 57 -255
-147 c-141 -81 -260 -144 -266 -140 -15 9 -13 207 3 228 6 9 122 80 257 157
185 107 251 140 270 136 14 -3 131 -66 260 -140z"/>
</g>
</svg>
export default BitKeep;
@@ -1,6 +0,0 @@
const CoinbaseIcon = (props) => <svg {...props} width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" fill="#2C5FF6" />
<path fillRule="evenodd" clipRule="evenodd" d="M14 23.8C19.4124 23.8 23.8 19.4124 23.8 14C23.8 8.58761 19.4124 4.2 14 4.2C8.58761 4.2 4.2 8.58761 4.2 14C4.2 19.4124 8.58761 23.8 14 23.8ZM11.55 10.8C11.1358 10.8 10.8 11.1358 10.8 11.55V16.45C10.8 16.8642 11.1358 17.2 11.55 17.2H16.45C16.8642 17.2 17.2 16.8642 17.2 16.45V11.55C17.2 11.1358 16.8642 10.8 16.45 10.8H11.55Z" fill="white" />
</svg>
export default CoinbaseIcon;
@@ -1,6 +0,0 @@
const GlowIcon = (props) => <svg {...props} width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" fill="#2C5FF6" />
<path fillRule="evenodd" clipRule="evenodd" d="M14 23.8C19.4124 23.8 23.8 19.4124 23.8 14C23.8 8.58761 19.4124 4.2 14 4.2C8.58761 4.2 4.2 8.58761 4.2 14C4.2 19.4124 8.58761 23.8 14 23.8ZM11.55 10.8C11.1358 10.8 10.8 11.1358 10.8 11.55V16.45C10.8 16.8642 11.1358 17.2 11.55 17.2H16.45C16.8642 17.2 17.2 16.8642 17.2 16.45V11.55C17.2 11.1358 16.8642 10.8 16.45 10.8H11.55Z" fill="white" />
</svg>
export default GlowIcon;
@@ -1,10 +0,0 @@
const IMX = (props) => {
return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="2499" height="2499" viewBox="0 0 2499 2499" fill="none">
<rect x="447" y="528" width="1591" height="1401" fill="white" />
<path d="M1858 1857C1858 1857 1583 1536 1492 1429C1482 1417 1478 1400 1482 1385C1485 1377 1491 1367 1497 1361L1539 1314L2008 1856H1859L1858 1857ZM1568 1857L1228 1460L1211 1438C1195 1419 1195 1391 1212 1372L1846 633H2003L1365 1362C1348 1380 1348 1409 1364 1427L1732 1855H1568V1857ZM1172 1274C1041 1426 672 1857 672 1857H508L1010 1275C1026 1256 1026 1229 1010 1210L512 633H670L1171 1215C1184 1232 1186 1257 1172 1273V1274ZM1166 1604L1117 1661L949 1857H797L1119 1482C1119 1482 1155 1525 1167 1539C1183 1558 1183 1586 1166 1604ZM947 634C947 634 1217 957 1315 1057C1331 1073 1328 1093 1314 1110C1262 1171 1261 1172 1261 1172L797 634H949H947ZM1352 887L1401 830L1569 634H1721L1399 1009C1399 1009 1363 966 1351 952C1335 933 1335 905 1352 887ZM1250 -1C560 -1 0 559 0 1249C0 1939 560 2499 1250 2499C1940 2499 2500 1939 2500 1249C2500 559 1940 -1 1250 -1Z" fill="black" />
</svg>
)
}
export default IMX
@@ -1,16 +0,0 @@
const OpenMask = (props) => {
return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300" fill="none">
<path d="M85 0L150 250L215 0H85Z" fill="#A0DDFE" stroke="#A0DDFE" />
<path d="M215 0L150 250L300 85L215 0Z" fill="#88D3FF" stroke="#88D3FF" />
<path d="M300 85L150 250L300 215V85Z" fill="#73CBFF" stroke="#73CBFF" />
<path d="M300 215L150 250L215 300L300 215Z" fill="#5BC1FF" stroke="#5BC1FF" />
<path d="M215 300L150 250L85 300H215Z" fill="#80CFFF" stroke="#80CFFF" />
<path d="M85 300L150 250L0 215L85 300Z" fill="#95D9FF" stroke="#95D9FF" />
<path d="M0 215L150 250L0 85V215Z" fill="#ADE2FF" stroke="#ADE2FF" />
<path d="M0 85L150 250L85 0L0 85Z" fill="#B7E7FF" stroke="#B7E7FF" />
</svg>
)
}
export default OpenMask
@@ -1,29 +0,0 @@
const Phantom = (props) => {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 1200 1200"
fill="none"
>
<g clipPath="url(#clip0_2596_138588)">
<rect width="1200" height="1200" rx="257.592" fill="#AB9FF2" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M517.219 779.814C470.102 852.012 391.148 943.378 286.09 943.378C236.426 943.378 188.672 922.933 188.672 834.122C188.672 607.943 497.48 257.813 784.004 257.813C947.004 257.813 1011.95 370.902 1011.95 499.326C1011.95 664.168 904.98 852.651 798.648 852.651C764.902 852.651 748.347 834.122 748.347 804.732C748.347 797.065 749.621 788.759 752.168 779.814C715.875 841.789 645.836 899.292 580.254 899.292C532.5 899.292 508.305 869.263 508.305 827.094C508.305 811.76 511.488 795.787 517.219 779.814ZM904.363 494.869C904.363 532.291 882.284 551.002 857.586 551.002C832.514 551.002 810.809 532.291 810.809 494.869C810.809 457.448 832.514 438.737 857.586 438.737C882.284 438.737 904.363 457.448 904.363 494.869ZM764.031 494.871C764.031 532.293 741.952 551.004 717.254 551.004C692.182 551.004 670.477 532.293 670.477 494.871C670.477 457.449 692.182 438.739 717.254 438.739C741.952 438.739 764.031 457.449 764.031 494.871Z"
fill="#FFFDF8"
/>
</g>
<defs>
<clipPath id="clip0_2596_138588">
<rect width="1200" height="1200" fill="white" />
</clipPath>
</defs>
</svg>
);
};
export default Phantom;
@@ -1,25 +0,0 @@
const Solflare = (props) => {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 40 40"
fill="none"
>
<defs>
<linearGradient id="solflareGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style={{ stopColor: '#ff9900', stopOpacity: 1 }} />
<stop offset="100%" style={{ stopColor: '#ff3366', stopOpacity: 1 }} />
</linearGradient>
</defs>
<path
d="M20.3068 38.4893C20.7304 38.4893 21.0737 38.8274 21.0737 39.2446C21.0737 39.6618 20.7304 40 20.3068 40C19.8832 40 19.5398 39.6618 19.5398 39.2446C19.5398 38.8274 19.8832 38.4893 20.3068 38.4893ZM19.4736 2.40586C19.8484 2.43668 20.1511 2.71949 20.2018 3.08615L21.114 9.68635C21.4204 11.8669 24.0615 12.8117 25.6918 11.3299L34.8317 3.04603C35.0522 2.84621 35.3955 2.86006 35.5986 3.07697C35.785 3.27597 35.7906 3.58065 35.6117 3.78615L27.6416 12.9411C26.1743 14.6235 27.2569 17.2506 29.4936 17.4381L36.5259 18.1132C36.8762 18.1468 37.1324 18.4534 37.0982 18.7979C37.07 19.0821 36.8502 19.3117 36.5635 19.3562L29.1741 20.5052C27.0286 20.7964 26.057 23.3263 27.4525 24.9699L30.0503 28.0195C30.2596 28.2652 30.2268 28.6313 29.977 28.8372C29.7654 29.0117 29.4592 29.0185 29.2398 28.8535L26.0374 26.4452C24.3093 25.1511 21.8183 26.251 21.6357 28.3928L20.93 36.7458C20.9008 37.0908 20.5929 37.3473 20.2422 37.3186C19.9446 37.2943 19.7041 37.0698 19.6637 36.7786L18.5447 28.7099C18.2447 26.5293 15.6037 25.5845 13.9669 27.0664L4.24383 35.8824C4.04257 36.0649 3.72905 36.0523 3.54357 35.8543C3.37339 35.6726 3.36822 35.3945 3.53152 35.2068L12.0171 25.4552C13.4844 23.7728 12.4084 21.1457 10.1716 20.9582L3.13771 20.283C2.78748 20.2494 2.53126 19.9428 2.56543 19.5982C2.59361 19.314 2.81342 19.0845 3.10007 19.0399L10.4846 17.8911C12.6301 17.5999 13.6083 15.07 12.2128 13.4264L10.4444 11.3505C10.1849 11.0458 10.2255 10.5918 10.5352 10.3365C10.7977 10.1201 11.1776 10.1118 11.4496 10.3165L13.6213 11.9511C15.3494 13.2452 17.8404 12.1453 18.023 10.0035L18.6072 3.12677C18.6441 2.69231 19.032 2.36955 19.4736 2.40586ZM0.766962 18.8263C1.19054 18.8263 1.53392 19.1645 1.53392 19.5816C1.53392 19.9988 1.19054 20.337 0.766962 20.337C0.34338 20.337 0 19.9988 0 19.5816C0 19.1645 0.34338 18.8263 0.766962 18.8263ZM39.233 18.0593C39.6566 18.0593 40 18.3975 40 18.8146C40 19.2318 39.6566 19.57 39.233 19.57C38.8095 19.57 38.4661 19.2318 38.4661 18.8146C38.4661 18.3975 38.8095 18.0593 39.233 18.0593ZM19.3628 0C19.7864 0 20.1298 0.338193 20.1298 0.755375C20.1298 1.17256 19.7864 1.51075 19.3628 1.51075C18.9393 1.51075 18.5959 1.17256 18.5959 0.755375C18.5959 0.338193 18.9393 0 19.3628 0Z"
fill="url(#solflareGradient)"
/>
</svg>
);
};
export default Solflare;
@@ -1,13 +0,0 @@
const Starknet = (props) => {
return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="158" height="158" viewBox="0 0 158 158" fill="none">
<path fillRule="evenodd" clipRule="evenodd" d="M-3.68439e-06 78.9988C-3.68439e-06 122.629 35.3685 157.998 78.9988 157.998C122.629 157.998 158 122.629 158 78.9988C158 35.3685 122.629 0 78.9988 0C35.3685 0 -3.68439e-06 35.3685 -3.68439e-06 78.9988Z" fill="#0C0C4F" />
<path fillRule="evenodd" clipRule="evenodd" d="M44.1542 60.3876L46.1043 54.3628C46.5007 53.1374 47.4678 52.1839 48.6977 51.8079L54.752 49.9461C55.59 49.6901 55.5968 48.5078 54.7656 48.2383L48.7385 46.2881C47.5154 45.8918 46.5618 44.9246 46.1836 43.6947L44.324 37.6405C44.0681 36.8047 42.8858 36.7956 42.6162 37.6291L40.6661 43.654C40.2697 44.877 39.3026 45.8306 38.0727 46.2089L32.0184 48.0684C31.1804 48.3266 31.1713 49.5067 32.0048 49.7762L38.0319 51.7263C39.255 52.1227 40.2086 53.0921 40.5868 54.322L42.4464 60.374C42.7023 61.212 43.8846 61.2211 44.1542 60.3876Z" fill="#FAFAFA" />
<path fillRule="evenodd" clipRule="evenodd" d="M139.848 56.881C137.352 54.09 133.457 52.5186 129.67 51.8742C125.853 51.2553 121.85 51.3127 118.082 51.9822C110.457 53.272 103.53 56.4292 97.4881 60.3241C94.3506 62.2356 91.675 64.4474 88.903 66.7C87.5674 67.8392 86.3497 69.0524 85.0815 70.2482L81.6163 73.696C77.851 77.6317 74.1398 81.2165 70.549 84.1878C66.9437 87.1454 63.573 89.3917 60.2512 90.9604C56.9315 92.5372 53.3794 93.4645 48.7495 93.6129C44.1603 93.7748 38.7307 92.9465 32.9229 91.5794C27.0839 90.2183 20.9523 88.2782 14.1004 86.6088C16.4912 93.2414 20.0914 99.1027 24.7137 104.461C29.3902 109.726 35.2297 114.524 42.7314 117.68C50.1247 120.906 59.4157 122.064 68.1031 120.317C76.8135 118.64 84.4573 114.61 90.6359 109.948C96.8304 105.239 101.842 99.8748 106.067 94.298C107.233 92.7572 107.85 91.8949 108.694 90.6906L111.027 87.2351C112.648 85.0977 114.124 82.6641 115.728 80.5464C118.874 76.1112 121.976 71.6811 125.58 67.5997C127.394 65.5296 129.307 63.5495 131.565 61.6466C132.691 60.7178 133.908 59.8088 135.256 58.99C136.624 58.1068 138.069 57.4155 139.848 56.881Z" fill="#EC796B" />
<path fillRule="evenodd" clipRule="evenodd" d="M139.848 56.8808C137.167 50.1148 132.182 44.4194 125.491 40.2167C118.841 36.0601 109.605 33.9389 100.452 35.7471C95.9307 36.6216 91.5501 38.3052 87.7059 40.5472C83.8791 42.7803 80.4499 45.4685 77.4812 48.3524C75.9992 49.7992 74.654 51.3084 73.3176 52.8262L69.8538 57.2423L64.5039 64.3512C57.6836 73.4976 50.3391 84.2166 38.2863 87.3927C26.454 90.5108 21.3221 87.7493 14.1004 86.6085C15.4209 90.0176 17.0565 93.3284 19.2739 96.2409C21.45 99.2124 24.0204 102.003 27.216 104.396C28.8309 105.545 30.5359 106.679 32.4295 107.641C34.3146 108.57 36.3474 109.388 38.5172 110.003C42.8332 111.186 47.6923 111.601 52.397 110.964C57.1039 110.336 61.6029 108.845 65.5355 106.864C69.4971 104.901 72.9481 102.511 76.0296 99.9891C82.1551 94.9021 86.9197 89.2815 90.9444 83.6C92.9688 80.7594 94.8064 77.8652 96.5059 74.9702L98.5062 71.5236C99.1177 70.5161 99.7362 69.5024 100.364 68.5584C102.899 64.7654 105.378 61.7241 108.389 59.4413C111.358 57.0995 115.493 55.3693 121.018 54.9674C126.52 54.5606 132.872 55.312 139.848 56.8808Z" fill="#FAFAFA" />
<path fillRule="evenodd" clipRule="evenodd" d="M110.081 113.097C110.081 118.067 114.112 122.097 119.081 122.097C124.051 122.097 128.076 118.067 128.076 113.097C128.076 108.128 124.051 104.097 119.081 104.097C114.112 104.097 110.081 108.128 110.081 113.097Z" fill="#EC796B" />
</svg>
)
}
export default Starknet
@@ -1,11 +0,0 @@
const TonKeeper = (props) => {
return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M16 13L0 7L16 1L32 7L16 13Z" fill="#45AEF5" />
<path opacity="0.6" d="M16 13L32 7L16 31V13Z" fill="#45AEF5" />
<path opacity="0.8" d="M16 13L0 7L16 31V13Z" fill="#45AEF5" />
</svg>
)
}
export default TonKeeper
-152
View File
@@ -1,152 +0,0 @@
'use client'
import React, { useEffect } from "react"
import { ErrorBoundary } from "react-error-boundary";
import { useRouter } from "next/router";
import * as Sentry from "@sentry/nextjs";
import { ThemeProvider as LuxThemeProvider } from '@luxdefi/ui/context-providers'
import { HeadMetadata } from '@luxdefi/ui/common'
import ThemeWrapper from "./themeWrapper";
import MaintananceContent from "./maintanance/maintanance";
import ErrorFallback from "./ErrorFallback";
import ColorSchema from "./ColorSchema";
import TonConnectProvider from "./TonConnectProvider";
import QueryProvider from "../context/query";
import RainbowKit from "./RainbowKit";
import Solana from "./SolanaProvider";
import { BridgeAppSettings } from "../Models/BridgeAppSettings";
import { BridgeSettings } from "../Models/BridgeSettings";
import { SendErrorMessage } from "../lib/telegram";
import { QueryParams } from "../Models/QueryParams";
import { THEME_COLORS, ThemeData } from "../Models/Theme";
import { TooltipProvider } from "./shadcn/tooltip";
import { AuthProvider } from "@/context/authContext";
import { SettingsProvider } from "@/context/settings";
import { FeeProvider } from "@/context/feeContext";
import { JotaiProvider } from "@/context/jotaiContext";
import metadata from "../metadata"
type Props = {
children: JSX.Element | JSX.Element[];
hideFooter?: boolean;
settings?: BridgeSettings;
themeData?: ThemeData | null
};
export default function Layout({ children, settings, themeData }: Props) {
const router = useRouter();
useEffect(() => {
function prepareUrl(params) {
const url = new URL(location.href)
const queryParams = new URLSearchParams(location.search)
let customUrl = url.protocol + "//" + url.hostname + url.pathname.replace(/\/$/, '')
for (const paramName of params) {
const paramValue = queryParams.get(paramName)
if (paramValue) customUrl = customUrl + '/' + paramValue
}
return customUrl
}
plausible('pageview', {
u: prepareUrl([
'destNetwork', //opsolate
'sourceExchangeName', //opsolate
'addressSource', //opsolate
'from',
'to',
'appName',
'asset',
'amount'
])
})
}, [])
if (!settings)
return <ThemeWrapper>
<MaintananceContent />
</ThemeWrapper>
let appSettings = new BridgeAppSettings(settings)
const query: QueryParams = {
...router.query,
...(router.query.lockAddress === 'true' ? { lockAddress: true } : {}),
...(router.query.lockNetwork === 'true' ? { lockNetwork: true } : {}),
...(router.query.lockExchange === 'true' ? { lockExchange: true } : {}),
...(router.query.hideRefuel === 'true' ? { hideRefuel: true } : {}),
...(router.query.hideAddress === 'true' ? { hideAddress: true } : {}),
...(router.query.hideFrom === 'true' ? { hideFrom: true } : {}),
...(router.query.hideTo === 'true' ? { hideTo: true } : {}),
...(router.query.lockFrom === 'true' ? { lockFrom: true } : {}),
...(router.query.lockTo === 'true' ? { lockTo: true } : {}),
...(router.query.lockAsset === 'true' ? { lockAsset: true } : {}),
...(router.query.lockAddress === 'false' ? { lockAddress: false } : {}),
...(router.query.lockNetwork === 'false' ? { lockNetwork: false } : {}),
...(router.query.lockExchange === 'false' ? { lockExchange: false } : {}),
...(router.query.hideRefuel === 'false' ? { hideRefuel: false } : {}),
...(router.query.hideAddress === 'false' ? { hideAddress: false } : {}),
...(router.query.hideFrom === 'false' ? { hideFrom: false } : {}),
...(router.query.hideTo === 'false' ? { hideTo: false } : {}),
...(router.query.lockFrom === 'false' ? { lockFrom: false } : {}),
...(router.query.lockTo === 'false' ? { lockTo: false } : {}),
...(router.query.lockAsset === 'false' ? { lockAsset: false } : {}),
};
function logErrorToService(error, info) {
const transaction = Sentry.startTransaction({
name: "error_boundary_handler",
});
Sentry.configureScope((scope) => {
scope.setSpan(transaction);
});
if (process.env.NEXT_PUBLIC_VERCEL_ENV && !error.stack.includes("chrome-extension")) {
SendErrorMessage("UI error", `env: ${process.env.NEXT_PUBLIC_VERCEL_ENV} %0A url: ${process.env.NEXT_PUBLIC_VERCEL_URL} %0A message: ${error?.message} %0A errorInfo: ${info?.componentStack} %0A stack: ${error?.stack ?? error.stack} %0A`)
}
Sentry.captureException(error, info);
transaction?.finish();
}
themeData = themeData || THEME_COLORS.default
const basePath = router?.basePath ?? ""
return (<>
<HeadMetadata metadata={metadata} />
{themeData && (<ColorSchema themeData={themeData} />)}
<QueryProvider query={query}>
<SettingsProvider data={appSettings}>
<AuthProvider>
<TooltipProvider delayDuration={500}>
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logErrorToService}>
<LuxThemeProvider>
<ThemeWrapper>
<JotaiProvider>
<TonConnectProvider basePath={basePath} themeData={themeData}>
<RainbowKit>
<Solana>
<FeeProvider>
{(process.env.NEXT_PUBLIC_IN_MAINTANANCE === 'true') ? (
<MaintananceContent />
) : (
children
)}
</FeeProvider>
</Solana>
</RainbowKit>
</TonConnectProvider>
</JotaiProvider>
</ThemeWrapper>
</LuxThemeProvider>
</ErrorBoundary>
</TooltipProvider>
</AuthProvider>
</SettingsProvider >
</QueryProvider >
</>)
}
@@ -1,111 +0,0 @@
export const CONTRACTS = {
1: {
chain_id: 1,
teleporter: "",
vault: "",
},
137: {
chain_id: 137,
teleporter: "",
vault: "",
},
10: {
chain_id: 10,
teleporter: "",
vault: "",
},
42161: {
chain_id: 42161,
teleporter: "",
vault: "",
},
42220: {
chain_id: 42220,
teleporter: "",
vault: "",
},
8453: {
chain_id: 8453,
teleporter: "",
vault: "",
},
100: {
chain_id: 100,
teleporter: "",
vault: "",
},
43114: {
chain_id: 43114,
teleporter: "",
vault: "",
},
250: {
chain_id: 250,
teleporter: "",
vault: "",
},
1313161554: {
chain_id: 1313161554,
teleporter: "",
vault: "",
},
7777777: {
chain_id: 7777777,
teleporter: "",
vault: "",
},
81457: {
chain_id: 81457,
teleporter: "",
vault: "",
},
59144: {
chain_id: 59144,
teleporter: "",
vault: "",
},
97: {
//bsc testnet
chain_id: 97,
teleporter: "0x568BF299E115D78a1fBa57BafdAe0fD8A26BFb7e",
vault: "0xE49355609F94A4B8a2EfC6FBd077542F8EC90080",
},
8888: {
// lux testnet
chain_id: 8888,
teleporter: "0x23E4BFDAC278FFE22DbBFAc5c1474E10cE7631CD",
vault: "0x43A4Ab078Edcd3e28385C714950fd099c9595279",
},
84532: {
// base testnet
chain_id: 84532,
teleporter: "0x6374d9d1E0CB5eA5e77D92E9a306f7f2ec41660a",
vault: "0x3Aa3179Bd3A8b1d8269dd840E39f8112fb3eD27a",
},
11155111: {
// sepolia
chain_id: 11155111,
teleporter: "0x306533aaedc8d2e28F05a7d42E999730C45aB5B1",
vault: "0xF2d3BE50D818d48A4e516FbaBb61609F9792CfA6",
},
};
export const SWAP_PAIRS: Record<string, string[]> = {
LETH: ["ETH"],
LBNB: ["BNB"],
LFTM: ["FTM"],
LPOL: ["POL", "MATIC"],
LUSD: ["USDT", "USDC", "DAI"],
LCELO: ["CELO"],
LXDAI: ["XDAI"],
ETH: ["LETH"],
POL: ["LPOL"],
BNB: ["LBNB"],
FTM: ["LFTM"],
DAI: ["LUSD"],
USDT: ["LUSD"],
USDC: ["LUSD"],
CELO: ["LCELO"],
XDAI: ["LXDAI"],
};
@@ -1,65 +0,0 @@
'use client'
import {
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandWrapper
} from '@/components/shadcn/command'
import React from "react";
import Modal from '@/components/modal/modal';
import useWindowDimensions from '@/hooks/useWindowDimensions';
import SpinIcon from '@/components/icons/spinIcon';
import { Network, Token } from '@/types/teleport';
import SelectItem from './SelectItem';
interface IProps {
show: boolean;
setShow: (value: boolean) => void;
searchHint: string;
networks: Network[],
network?: Network;
setNetwork: (value: Network) => void;
}
const CommandSelect: React.FC<IProps> = ({ networks, network, setNetwork, show, setShow, searchHint }) => {
const { isDesktop } = useWindowDimensions();
return (
<Modal height='full' show={show} setShow={setShow}>
{show ?
<CommandWrapper>
<CommandInput autoFocus={isDesktop} placeholder={searchHint} />
{
networks && networks.length > 0 ?
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{
networks.map((n: Network) => (
<CommandItem
disabled={false}
value={n.internal_name}
key={n.internal_name}
onSelect={() => {
setNetwork(n)
}}
className={`${n?.status == 'active' ? 'opacity-100' : 'opacity-50'}`}
>
<SelectItem network={n} />
</CommandItem>
))
}
</CommandList>
:
<div className='flex justify-center h-full items-center'>
<SpinIcon className="animate-spin h-5 w-5" />
</div>
}
</CommandWrapper>
: <></>
}
</Modal>
)
}
export default CommandSelect;
@@ -1,34 +0,0 @@
import React from 'react';
import { Network } from '@/types/teleport';
import Image from 'next/image'
interface IProps {
network: Network
}
const SelectItem: React.FC<IProps> = ({ network }) => {
return (
<div className="flex items-center justify-between w-full">
<div className='flex items-center'>
<div className="flex-shrink-0 h-6 w-6 relative">
<Image
src={network.logo}
alt="Project Logo"
height="40"
width="40"
loading="eager"
className="rounded-md object-contain"
/>
</div>
<div className="ml-4 flex items-center gap-3 justify-between w-full">
<p className='text-md font-medium'>
{network.display_name}
</p>
</div>
</div>
<div>{network.status === 'active' && 'active'}</div>
</div>
);
}
export default SelectItem;
@@ -1,65 +0,0 @@
import {
CommandItem,
CommandList,
CommandWrapper,
} from "../../../../shadcn/command";
import { Token } from "@/types/teleport";
import React from "react";
import Image from "next/image";
interface IProps {
values: Token[];
setValue: (token: Token) => void;
}
const TokenSelect: React.FC<IProps> = ({ values, setValue }) => {
return (
<CommandWrapper>
<CommandList>
{values.map((item) => {
return (
<CommandItem
className={`border-t border-t-slate-500 justify-between gap-6 ${
item.status !== "active" && "opacity-30"
}`}
disabled={false}
value={item.asset}
key={item.asset}
onSelect={() => {
setValue(item);
}}
>
<div className="flex items-center w-full">
<div className="flex-shrink-0 h-6 w-6 relative">
{item.logo && (
<Image
src={item.logo}
alt="Project Logo"
height="40"
width="40"
loading="eager"
className="rounded-full object-contain"
/>
)}
</div>
<div className="ml-4 flex items-center gap-3 justify-between w-full">
<p className="text-md font-medium">{item.asset}</p>
</div>
</div>
<div className="text-xs text-[white]/60">
{item.status === "active" && "active"}
</div>
</CommandItem>
);
})}
</CommandList>
</CommandWrapper>
);
};
export default TokenSelect;
export enum LayerDisabledReason {
LockNetworkIsTrue = "",
InsufficientLiquidity = "Temporarily disabled. Please check later.",
}
@@ -1,372 +0,0 @@
import React from "react";
import toast from "react-hot-toast";
import Web3 from "web3";
import {
swapStatusAtom,
mpcSignatureAtom,
bridgeMintTransactionAtom,
userTransferTransactionAtom,
} from "@/store/fireblocks";
import { Contract } from "ethers";
import { CONTRACTS } from "@/components/lux/fireblocks/constants/settings";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/shadcn/tooltip";
import teleporterABI from "@/components/lux/fireblocks/constants/abi/bridge.json";
//hooks
import { useSwitchNetwork } from "wagmi";
import { useAtom } from "jotai";
import { useEthersSigner } from "@/lib/ethersToViem/ethers";
import { useNetwork } from "wagmi";
import { parseUnits } from "@/lib/resolveChain";
import axios from "axios";
import useWallet from "@/hooks/useWallet";
import SwapItems from "./SwapItems";
import shortenAddress from "@/components/utils/ShortenAddress";
import SpinIcon from "@/components/icons/spinIcon";
import { Gauge } from "@/components/gauge";
import { Network, Token } from "@/types/teleport";
import { ArrowRight } from "lucide-react";
import { formatUnits } from "viem";
interface IProps {
className?: string;
sourceNetwork: Network;
sourceAsset: Token;
destinationNetwork: Network;
destinationAsset: Token;
destinationAddress: string;
sourceAmount: string;
swapId: string;
}
const PayoutProcessor: React.FC<IProps> = ({
sourceNetwork,
sourceAsset,
destinationNetwork,
destinationAsset,
destinationAddress,
sourceAmount,
className,
swapId,
}) => {
//state
const [isGettingPayout, setIsGettingPayout] = React.useState<boolean>(false);
//atoms
const [, setBridgeMintTransactionHash] = useAtom(bridgeMintTransactionAtom);
const [userTransferTransaction] = useAtom(userTransferTransactionAtom);
const [swapStatus, setSwapStatus] = useAtom(swapStatusAtom);
const [mpcSignature] = useAtom(mpcSignatureAtom);
//hooks
const { chain } = useNetwork();
const signer = useEthersSigner();
const { switchNetwork } = useSwitchNetwork();
const { connectWallet } = useWallet();
const isWithdrawal = React.useMemo(
() => (sourceAsset.name.startsWith("Lux") ? true : false),
[sourceAsset]
);
//chain id
const chainId = chain?.id;
React.useEffect(() => {
if (!signer) {
connectWallet("evm");
} else {
if (chainId === destinationNetwork.chain_id) {
isWithdrawal ? withdrawDestinationToken() : payoutDestinationToken();
} else {
destinationNetwork.chain_id && switchNetwork!(destinationNetwork.chain_id);
}
}
}, [swapStatus, chainId, signer, isWithdrawal]);
const payoutDestinationToken = async () => {
const mintData = {
hashedTxId_: Web3.utils.keccak256(userTransferTransaction),
toTokenAddress_: destinationAsset?.contract_address,
tokenAmount_: parseUnits(String(sourceAmount), sourceAsset.decimals),
fromTokenDecimals_: sourceAsset?.decimals,
receiverAddress_: destinationAddress,
signedTXInfo_: mpcSignature,
vault_: "true",
};
console.log("data for bridge mint::", mintData);
try {
setIsGettingPayout(true);
// string memory hashedTxId_,
// address toTokenAddress_,
// uint256 tokenAmount_,
// uint256 fromTokenDecimals_,
// address receiverAddress_,
// bytes memory signedTXInfo_,
// string memory vault_
if (!destinationNetwork.chain_id) return
const bridgeContract = new Contract(
CONTRACTS[destinationNetwork.chain_id].teleporter,
teleporterABI,
signer
);
const _signer = await bridgeContract.previewBridgeStealth(
mintData.hashedTxId_,
mintData.toTokenAddress_,
mintData.tokenAmount_,
mintData.fromTokenDecimals_,
mintData.receiverAddress_,
mintData.signedTXInfo_,
mintData.vault_
);
console.log("::signer", _signer);
const _bridgePayoutTx = await bridgeContract.bridgeMintStealth(
mintData.hashedTxId_,
mintData.toTokenAddress_,
mintData.tokenAmount_,
mintData.fromTokenDecimals_,
mintData.receiverAddress_,
mintData.signedTXInfo_,
mintData.vault_
);
await _bridgePayoutTx.wait();
setBridgeMintTransactionHash(_bridgePayoutTx.hash);
await axios.post(`/api/swaps/payout/${swapId}`, {
txHash: _bridgePayoutTx.hash,
amount: sourceAmount,
from: CONTRACTS[String(sourceNetwork?.chain_id)].teleporter,
to: destinationAddress,
});
setSwapStatus("payout_success");
} catch (err) {
console.log(err);
if (String(err).includes("user rejected transaction")) {
toast.error(`User rejected transaction`);
} else {
toast.error(`Failed to run transaction`);
}
} finally {
setIsGettingPayout(false);
}
};
const withdrawDestinationToken = async () => {
const withdrawData = {
hashedTxId_: Web3.utils.keccak256(userTransferTransaction),
toTokenAddress_: destinationAsset?.contract_address,
tokenAmount_: parseUnits(String(sourceAmount), sourceAsset.decimals),
fromTokenDecimals_: sourceAsset?.decimals,
receiverAddress_: destinationAddress,
signedTXInfo_: mpcSignature,
vault_: "false",
};
// previewVaultWithdraw
console.log("::data for bridge withdraw:", withdrawData);
try {
if (!destinationNetwork.chain_id) return
const bridgeContract = new Contract(
CONTRACTS[destinationNetwork.chain_id].teleporter,
teleporterABI,
signer
);
const previewVaultWithdraw = await bridgeContract.previewVaultWithdraw(
withdrawData.toTokenAddress_
);
console.log({
balance: Number(
formatUnits(previewVaultWithdraw, destinationAsset.decimals)
),
todo: Number(sourceAmount),
});
if (
Number(formatUnits(previewVaultWithdraw, destinationAsset.decimals)) <
Number(sourceAmount)
) {
toast.error(
`Bridge doesnt have enough balance to withdraw. You can only withdraw ${Number(
formatUnits(previewVaultWithdraw, destinationAsset.decimals)
)}tokens now. Please wait for the withdrawal to be activated.`
);
return;
}
// console.log("::canWithdraw", canWithdraw);
setIsGettingPayout(true);
// string memory hashedTxId_,
// address toTokenAddress_,
// uint256 tokenAmount_,
// uint256 fromTokenDecimals_,
// address receiverAddress_,
// bytes memory signedTXInfo_,
// string memory vault_
const _signer = await bridgeContract.previewBridgeStealth(
withdrawData.hashedTxId_,
withdrawData.toTokenAddress_,
withdrawData.tokenAmount_,
withdrawData.fromTokenDecimals_,
withdrawData.receiverAddress_,
withdrawData.signedTXInfo_,
withdrawData.vault_
);
console.log("::signer", _signer);
const _bridgePayoutTx = await bridgeContract.bridgeWithdrawStealth(
withdrawData.hashedTxId_,
withdrawData.toTokenAddress_,
withdrawData.tokenAmount_,
withdrawData.fromTokenDecimals_,
withdrawData.receiverAddress_,
withdrawData.signedTXInfo_,
withdrawData.vault_
);
await _bridgePayoutTx.wait();
setBridgeMintTransactionHash(_bridgePayoutTx.hash);
await axios.post(`/api/swaps/payout/${swapId}`, {
txHash: _bridgePayoutTx.hash,
amount: sourceAmount,
from: CONTRACTS[String(sourceNetwork?.chain_id)].teleporter,
to: destinationAddress,
});
setSwapStatus("payout_success");
} catch (err) {
console.log(err);
if (String(err).includes("user rejected transaction")) {
toast.error(`User rejected transaction`);
} else {
toast.error(`Failed to run transaction`);
}
} finally {
setIsGettingPayout(false);
}
};
const handlePayoutDestinationToken = () => {
if (!signer) {
toast.error(`No connected wallet. Please connect your wallet`);
connectWallet("evm");
} else if (chainId !== destinationNetwork.chain_id) {
destinationNetwork.chain_id && switchNetwork!(destinationNetwork.chain_id);
} else {
isWithdrawal ? withdrawDestinationToken() : payoutDestinationToken();
}
};
return (
<div className={`w-full flex flex-col ${className}`}>
<div className="space-y-5">
<div className="w-full flex flex-col space-y-5">
<SwapItems
sourceNetwork={sourceNetwork}
sourceAsset={sourceAsset}
destinationNetwork={destinationNetwork}
destinationAsset={destinationAsset}
destinationAddress={destinationAddress}
sourceAmount={sourceAmount}
/>
</div>
<div>
<div className="bg-level-1 font-normal px-3 py-4 rounded-lg flex flex-col border border-[#404040] w-full relative z-10">
<div className="font-normal pb-4 flex flex-col w-full relative z-10 space-y-4 items-center border-dashed border-b-2 border-[#404040]">
<span className="animate-spin">
<Gauge value={60} size="medium" />
</span>
<div className="mt-2">
{isWithdrawal ? "Withdraw" : "Get"} Your{" "}
{destinationAsset.asset}
</div>
</div>
<div className="flex flex-col gap-2 py-5">
<div className="flex gap-3 items-center">
<span className="">
<Gauge value={100} size="verySmall" showCheckmark={true} />
</span>
<div className="flex flex-col items-center text-sm">
<span>
{sourceAsset?.asset}{" "}
{isWithdrawal ? "burnt" : "transferred"}
</span>
<div className="underline flex gap-2 items-center">
{shortenAddress(userTransferTransaction)}
<Tooltip>
<TooltipTrigger asChild>
<a
target={"_blank"}
href={sourceNetwork?.transaction_explorer_template?.replace(
"{0}",
userTransferTransaction
)}
className="cursor-pointer"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-square-arrow-out-up-right"
>
<path d="M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6" />
<path d="m21 3-9 9" />
<path d="M15 3h6v6" />
</svg>
</a>
</TooltipTrigger>
<TooltipContent>
<p>View Transaction</p>
</TooltipContent>
</Tooltip>
</div>
</div>
</div>
<div className="flex gap-3 items-center">
<span className="">
<Gauge value={100} size="verySmall" showCheckmark={true} />
</span>
<div className="flex flex-col items-center text-sm">
<span>Teleporter has confirmed your Deposit</span>
</div>
</div>
</div>
<button
disabled={isGettingPayout}
onClick={handlePayoutDestinationToken}
className="border border-muted-3 disabled:border-[#404040] items-center space-x-1 disabled:opacity-80 disabled:cursor-not-allowed relative w-full flex justify-center font-semibold rounded-md transform transition duration-200 ease-in-out hover:bg-primary-hover bg-primary-lux text-primary-fg disabled:hover:bg-primary-lux py-3 px-2 md:px-3 plausible-event-name=Swap+initiated"
>
{isGettingPayout ? (
<SpinIcon className="animate-spin h-5 w-5" />
) : (
<ArrowRight />
)}
<span className="grow">
{isWithdrawal ? "Withdraw" : "Get"} Your{" "}
{destinationAsset?.asset}
</span>
</button>
</div>
</div>
</div>
</div>
);
};
export default PayoutProcessor;
@@ -1,262 +0,0 @@
import React from "react";
import toast from "react-hot-toast";
import {
swapStatusAtom,
userTransferTransactionAtom,
} from "@/store/fireblocks";
import { ArrowRight, Router } from "lucide-react";
import { Contract } from "ethers";
import { CONTRACTS } from "@/components/lux/fireblocks/constants/settings";
import teleporterABI from "@/components/lux/fireblocks/constants/abi/bridge.json";
import erc20ABI from "@/components/lux/fireblocks/constants/abi/erc20.json";
//hooks
import { useSwitchNetwork } from "wagmi";
import { useAtom } from "jotai";
import { useEthersSigner } from "@/lib/ethersToViem/ethers";
import { useNetwork } from "wagmi";
import { parseUnits } from "@/lib/resolveChain";
import axios from "axios";
import useWallet from "@/hooks/useWallet";
import SwapItems from "./SwapItems";
import SpinIcon from "@/components/icons/spinIcon";
import { Network, Token } from "@/types/teleport";
interface IProps {
className?: string;
sourceNetwork: Network;
sourceAsset: Token;
destinationNetwork: Network;
destinationAsset: Token;
destinationAddress: string;
sourceAmount: string;
swapId: string;
}
const UserTokenDepositor: React.FC<IProps> = ({
sourceNetwork,
sourceAsset,
destinationNetwork,
destinationAsset,
destinationAddress,
sourceAmount,
className,
swapId,
}) => {
//state
const [isTokenTransferring, setIsTokenTransferring] =
React.useState<boolean>(false);
const [userDepositNotice, setUserDepositNotice] = React.useState<string>("");
//atoms
const [, setSwapStatus] = useAtom(swapStatusAtom);
const [, setUserTransferTransaction] = useAtom(userTransferTransactionAtom);
//hooks
const { chain } = useNetwork();
const signer = useEthersSigner();
const { switchNetwork } = useSwitchNetwork();
const { connectWallet } = useWallet();
const isWithdrawal = React.useMemo(
() => (sourceAsset.name.startsWith("Lux") ? true : false),
[sourceAsset]
);
//chain id
const chainId = chain?.id;
React.useEffect(() => {
if (!signer) {
connectWallet("evm");
} else {
if (chainId === sourceNetwork?.chain_id) {
isWithdrawal ? burnToken() : transferToken();
} else {
sourceNetwork.chain_id && switchNetwork!(sourceNetwork.chain_id);
}
}
}, [chainId, signer, isWithdrawal]);
const transferToken = async () => {
try {
setIsTokenTransferring(true);
const _amount = parseUnits(String(sourceAmount), sourceAsset.decimals);
if (sourceAsset.is_native) {
const _balance = await signer?.getBalance();
if (Number(_balance) < Number(_amount)) {
toast.error(`Insufficient ${sourceAsset.asset} amount`);
return;
}
} else {
const erc20Contract = new Contract(
sourceAsset?.contract_address as string,
erc20ABI,
signer
);
// approve
setUserDepositNotice(`Approving ${sourceAsset.asset}...`);
const _balance = await erc20Contract.balanceOf(
signer?._address as string
);
if (_balance < _amount) {
toast.error(`Insufficient ${sourceAsset.asset} amount`);
return;
}
if (!sourceNetwork.chain_id) return
// if allowance is less than amount, approve
const _allowance = await erc20Contract.allowance(
signer?._address as string,
CONTRACTS[sourceNetwork.chain_id].teleporter
);
if (_allowance < _amount) {
const _approveTx = await erc20Contract.approve(
CONTRACTS[sourceNetwork.chain_id].teleporter,
_amount
);
await _approveTx.wait();
}
}
setUserDepositNotice(`Transfer ${sourceAsset.asset}...`);
if (!sourceNetwork.chain_id) return
const bridgeContract = new Contract(
CONTRACTS[sourceNetwork.chain_id].teleporter,
teleporterABI,
signer
);
console.log({
_amount,
_asset: sourceAsset.contract_address,
});
const _bridgeTransferTx = await bridgeContract.vaultDeposit(
_amount,
sourceAsset.contract_address,
{
value: sourceAsset.is_native ? _amount : 0,
}
);
await _bridgeTransferTx.wait();
await axios.post(`/api/swaps/transfer/${swapId}`, {
txHash: _bridgeTransferTx.hash,
amount: sourceAmount,
from: signer?._address,
to: CONTRACTS[sourceNetwork.chain_id].teleporter,
});
setUserTransferTransaction(_bridgeTransferTx.hash);
setSwapStatus("teleport_processing_pending");
} catch (err) {
console.log(err);
if (String(err).includes("user rejected transaction")) {
toast.error(`User rejected transaction`);
} else {
toast.error(`Failed to run transaction`);
}
} finally {
setIsTokenTransferring(false);
}
};
const burnToken = async () => {
try {
setIsTokenTransferring(true);
const _amount = parseUnits(String(sourceAmount), sourceAsset.decimals);
const erc20Contract = new Contract(
sourceAsset?.contract_address as string,
erc20ABI,
signer
);
setUserDepositNotice(`Checking token balance...`);
const _balance = await erc20Contract.balanceOf(
signer?._address as string
);
if (_balance < _amount) {
toast.error(`Insufficient ${sourceAsset.asset} amount`);
return;
}
setUserDepositNotice(`Burning ${sourceAsset.asset}...`);
if (!sourceNetwork.chain_id) return
const bridgeContract = new Contract(
CONTRACTS[sourceNetwork.chain_id].teleporter,
teleporterABI,
signer
);
console.log({
_amount,
_asset: sourceAsset.contract_address,
});
const _bridgeTransferTx = await bridgeContract.bridgeBurn(
_amount,
sourceAsset.contract_address
);
await _bridgeTransferTx.wait();
await axios.post(`/api/swaps/transfer/${swapId}`, {
txHash: _bridgeTransferTx.hash,
amount: sourceAmount,
from: signer?._address,
to: CONTRACTS[sourceNetwork.chain_id].teleporter,
});
setUserTransferTransaction(_bridgeTransferTx.hash);
setSwapStatus("teleport_processing_pending");
} catch (err) {
console.log(err);
if (String(err).includes("user rejected transaction")) {
toast.error(`User rejected transaction`);
} else {
toast.error(`Failed to run transaction`);
}
} finally {
setIsTokenTransferring(false);
}
};
const handleTokenTransfer = async () => {
if (!signer) {
toast.error(`No connected wallet. Please connect your wallet`);
connectWallet("evm");
} else if (chainId !== sourceNetwork.chain_id) {
sourceNetwork.chain_id && switchNetwork!(sourceNetwork.chain_id);
} else {
isWithdrawal ? burnToken() : transferToken();
}
};
return (
<div className={`w-full flex flex-col ${className}`}>
<div className="space-y-5">
<div className="w-full flex flex-col space-y-5">
<SwapItems
sourceNetwork={sourceNetwork}
sourceAsset={sourceAsset}
destinationNetwork={destinationNetwork}
destinationAsset={destinationAsset}
destinationAddress={destinationAddress}
sourceAmount={sourceAmount}
/>
</div>
<button
disabled={isTokenTransferring}
onClick={handleTokenTransfer}
className="border border-muted-3 disabled:border-[#404040] items-center space-x-1 disabled:opacity-80 disabled:cursor-not-allowed relative w-full flex justify-center font-semibold rounded-md transform transition duration-200 ease-in-out hover:bg-primary-hover bg-primary-lux text-primary-fg disabled:hover:bg-primary-lux py-3 px-2 md:px-3 plausible-event-name=Swap+initiated"
>
{isTokenTransferring ? (
<SpinIcon className="animate-spin h-5 w-5" />
) : (
<ArrowRight />
)}
{isTokenTransferring ? (
<span className="grow">{userDepositNotice}</span>
) : (
<span className="grow">
{isWithdrawal ? "Burn" : "Transfer"} {sourceAsset.asset}
</span>
)}
</button>
</div>
</div>
);
};
export default UserTokenDepositor;
@@ -1,32 +0,0 @@
import React from "react";
import { Network } from "@/types/teleport";
import Image from "next/image";
interface IProps {
network: Network;
}
const SelectItem: React.FC<IProps> = ({ network }) => {
return (
<div className="flex items-center justify-between w-full">
<div className="flex items-center">
<div className="flex-shrink-0 h-6 w-6 relative">
<Image
src={network.logo}
alt="Project Logo"
height="40"
width="40"
loading="eager"
className="rounded-full object-contain"
/>
</div>
<div className="ml-4 flex items-center gap-3 justify-between w-full">
<p className="text-md font-medium">{network.display_name}</p>
</div>
</div>
<div>{network.status === "active" && "active"}</div>
</div>
);
};
export default SelectItem;
@@ -1,63 +0,0 @@
import React from "react";
import Image from "next/image";
import {
CommandItem,
CommandList,
CommandWrapper,
} from "../../../../shadcn/command";
import { Token } from "@/types/teleport";
interface IProps {
values: Token[];
setValue: (token: Token) => void;
}
const TokenSelect: React.FC<IProps> = ({ values, setValue }) => {
return (
<CommandWrapper>
<CommandList>
{values.map((item) => {
return (
<CommandItem
className={`border-t border-t-slate-500 justify-between gap-6 ${
item.status !== "active" && "opacity-30"
}`}
disabled={false}
value={item.asset}
key={item.asset}
onSelect={() => setValue(item)}
>
<div className="flex items-center w-full">
<div className="flex-shrink-0 h-6 w-6 relative">
{item.logo && (
<Image
src={item.logo}
alt="Project Logo"
height="40"
width="40"
loading="eager"
className="rounded-md object-contain"
/>
)}
</div>
<div className="ml-4 flex items-center gap-3 justify-between w-full">
<p className="text-md font-medium">{item.asset}</p>
</div>
</div>
<div className="text-xs text-[white]/60">
{item.status === "active" && "active"}
</div>
</CommandItem>
);
})}
</CommandList>
</CommandWrapper>
);
};
export enum LayerDisabledReason {
LockNetworkIsTrue = "",
InsufficientLiquidity = "Temporarily disabled. Please check later.",
}
export default TokenSelect;
@@ -1,94 +0,0 @@
import React from 'react';
import {
swapStatusAtom,
swapIdAtom,
} from '@/store/teleport'
//hooks
import { useAtom } from "jotai";
//types
import { Network, Token } from '@/types/teleport';
import UserTokenDepositor from './progress/TokenDepositor';
import TeleportProcessor from './progress/TeleportProcessor';
import PayoutProcessor from './progress/PayoutProcessor';
import SwapSuccess from './progress/SwapSuccess';
interface IProps {
className?: string,
sourceNetwork: Network,
sourceAsset: Token,
destinationNetwork: Network,
destinationAsset: Token,
destinationAddress: string,
sourceAmount: string
}
const SwapDetails: React.FC<IProps> = ({
sourceNetwork,
sourceAsset,
destinationNetwork,
destinationAsset,
destinationAddress,
sourceAmount,
className
}) => {
//atoms
const [swapStatus] = useAtom(swapStatusAtom);
const [swapId] = useAtom(swapIdAtom);
//chain id
if (swapStatus === 'user_transfer_pending') {
return (
<UserTokenDepositor
sourceNetwork={sourceNetwork}
sourceAsset={sourceAsset}
destinationNetwork={destinationNetwork}
destinationAsset={destinationAsset}
destinationAddress={destinationAddress}
sourceAmount={sourceAmount}
swapId={swapId}
className={className}
/>
)
} else if (swapStatus === "teleport_processing_pending") {
return (
<TeleportProcessor
sourceNetwork={sourceNetwork}
sourceAsset={sourceAsset}
destinationNetwork={destinationNetwork}
destinationAsset={destinationAsset}
destinationAddress={destinationAddress}
sourceAmount={sourceAmount}
swapId={swapId}
className={className}
/>
)
} else if (swapStatus === 'user_payout_pending') {
return (
<PayoutProcessor
sourceNetwork={sourceNetwork}
sourceAsset={sourceAsset}
destinationNetwork={destinationNetwork}
destinationAsset={destinationAsset}
destinationAddress={destinationAddress}
sourceAmount={sourceAmount}
swapId={swapId}
className={className}
/>
)
} else if (swapStatus === 'payout_success') {
return (
<SwapSuccess
sourceNetwork={sourceNetwork}
sourceAsset={sourceAsset}
destinationNetwork={destinationNetwork}
destinationAsset={destinationAsset}
destinationAddress={destinationAddress}
sourceAmount={sourceAmount}
swapId={swapId}
className={className}
/>
)
}
}
export default SwapDetails;
@@ -1,64 +0,0 @@
"use client";
import React from "react";
import AmountField from "../AmountField";
import NetworkSelectWrapper from "./NetworkSelectWrapper";
import { Network, Token } from "@/types/teleport";
import TokenSelectWrapper from "./TokenSelectWrapper";
import { useAtom } from "jotai";
import { sourceAmountAtom } from "@/store/teleport";
interface IProps {
networks: Network[],
network?: Network,
asset?: Token,
setNetwork: (network: Network) => void,
setAsset: (asset: Token) => void,
disabled: boolean
}
const NetworkFormField: React.FC<IProps> = ({ networks, network, asset, setNetwork, setAsset, disabled }) => {
const [amount, setAmount] = useAtom(sourceAmountAtom);
return (
<div className={`p-3`}>
<label htmlFor={'name'} className="block font-semibold text-xs">
From
</label>
<div className="border border-[#404040] bg-level-1 rounded-lg mt-1.5 pb-2">
<div>
<div className="w-full">
<NetworkSelectWrapper
disabled={disabled}
placeholder={'Source'}
setNetwork={setNetwork}
network={network}
networks={networks}
searchHint={'searchHint'}
className="rounded-b-none border-t-0 border-x-0"
/>
</div>
</div>
<div className="flex justify-between items-center mt-2 pl-3 pr-4">
<AmountField
disabled={!network}
value={amount}
setValue={(value: string) => setAmount(value)}
/>
<TokenSelectWrapper
placeholder="Asset"
values={network ? network.currencies : []}
value={asset}
setValue={setAsset}
disabled={true}
/>
</div>
</div>
</div>
);
};
export default NetworkFormField;
@@ -1,34 +0,0 @@
import React from 'react';
import { Network } from '@/types/teleport';
import Image from 'next/image'
interface IProps {
network: Network
}
const SelectItem: React.FC<IProps> = ({ network }) => {
return (
<div className="flex items-center justify-between w-full">
<div className='flex items-center'>
<div className="flex-shrink-0 h-6 w-6 relative">
<Image
src={network.logo}
alt="Project Logo"
height="40"
width="40"
loading="eager"
className="rounded-md object-contain"
/>
</div>
<div className="ml-4 flex items-center gap-3 justify-between w-full">
<p className='text-md font-medium'>
{network.display_name}
</p>
</div>
</div>
<div>{network.status === 'active' && 'active'}</div>
</div>
);
}
export default SelectItem;
@@ -1,93 +0,0 @@
import React from "react";
import Image from "next/image";
import shortenAddress from "@/components/utils/ShortenAddress";
import { useAtom } from "jotai";
import { ethPriceAtom } from "@/store/teleport";
import { truncateDecimals } from "@/components/utils/RoundDecimals";
import { Network, Token } from "@/types/teleport";
interface IProps {
sourceNetwork: Network;
sourceAsset: Token;
destinationNetwork: Network;
destinationAsset: Token;
destinationAddress: string;
sourceAmount: string;
}
const SwapItems: React.FC<IProps> = ({
sourceNetwork,
sourceAsset,
destinationNetwork,
destinationAddress,
destinationAsset,
sourceAmount,
}) => {
const [ethPrice] = useAtom(ethPriceAtom);
//token price
const tokenPrice = ethPrice;
return (
<div className="bg-level-1 font-normal px-3 py-4 rounded-lg flex flex-col border border-[#404040] w-full relative z-10">
<div className="font-normal flex flex-col w-full relative z-10 space-y-4">
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-3">
{sourceAsset && (
<Image
src={sourceAsset.logo}
alt={sourceAsset.logo}
width={32}
height={32}
className="rounded-full"
/>
)}
<div>
<p className=" text-sm leading-5">{sourceNetwork.display_name}</p>
<p className="text-sm ">{"Network"}</p>
</div>
</div>
<div className="flex flex-col">
<p className=" text-sm">
{truncateDecimals(Number(sourceAmount), 4)} {sourceAsset.asset}
</p>
<p className=" text-sm flex justify-end">
${truncateDecimals(Number(sourceAmount) * tokenPrice, 4)}
</p>
</div>
</div>
</div>
<div className="font-normal flex flex-col w-full relative z-10 space-y-4 mt-5">
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-3">
{
<Image
src={destinationAsset.logo}
alt={destinationAsset.logo}
width={32}
height={32}
className="rounded-full"
/>
}
<div>
<p className=" text-sm leading-5">
{destinationNetwork.display_name}
</p>
<p className="text-sm ">{shortenAddress(destinationAddress)}</p>
</div>
</div>
<div className="flex flex-col">
<p className=" text-sm">
{truncateDecimals(Number(sourceAmount), 4)}{" "}
{destinationAsset.asset}
</p>
<p className=" text-sm flex justify-end">
${truncateDecimals(Number(sourceAmount) * tokenPrice, 4)}
</p>
</div>
</div>
</div>
</div>
);
};
export default SwapItems;
@@ -1,67 +0,0 @@
"use client";
import React from "react";
import AmountField from "../AmountField";
import NetworkSelectWrapper from "./NetworkSelectWrapper";
import TokenSelectWrapper from "./TokenSelectWrapper";
import { Network, Token } from "@/types/teleport";
import { useAtom } from "jotai";
import { sourceAmountAtom } from "@/store/teleport";
interface IProps {
networks: Network[],
network?: Network,
asset?: Token,
sourceAsset?: Token,
setNetwork: (network: Network) => void,
setAsset: (asset: Token) => void,
disabled: boolean
}
const NetworkFormField: React.FC<IProps> = ({ networks, network, asset, sourceAsset, setNetwork, setAsset, disabled }) => {
const [amount] = useAtom(sourceAmountAtom);
return (
<div className={`p-3`}>
<label htmlFor={'name'} className="block font-semibold text-xs">
To
</label>
<div className="border border-[#404040] bg-level-1 rounded-lg mt-1.5 pb-2">
<div>
<div className="w-full">
<NetworkSelectWrapper
disabled={disabled}
placeholder={'Destination'}
setNetwork={setNetwork}
network={network}
networks={networks}
searchHint={'searchHint'}
className="rounded-b-none border-t-0 border-x-0"
/>
</div>
</div>
<div className="flex justify-between items-center mt-2 pl-3 pr-4">
<AmountField
value={amount}
disabled={true}
/>
<TokenSelectWrapper
placeholder="Asset"
values={network ? network.currencies : []}
value={asset}
sourceAsset={sourceAsset}
setValue={setAsset}
disabled={true}
/>
</div>
</div>
</div>
);
};
export default NetworkFormField;
-20
View File
@@ -1,20 +0,0 @@
'use client'
import { FC } from 'react';
import { SwapDataProvider } from '../context/swap';
import { TimerProvider } from '../context/timerContext';
import SwapForm from "./Swap/Form"
import { BalancesDataProvider } from '../context/balances';
const Swap: FC = () => {
return (
<SwapDataProvider >
<TimerProvider>
<BalancesDataProvider>
<SwapForm />
</BalancesDataProvider>
</TimerProvider>
</SwapDataProvider >
)
};
export default Swap;
-53
View File
@@ -1,53 +0,0 @@
import { X } from "lucide-react";
import toast, { ToastBar, Toaster } from "react-hot-toast"
import Navbar from "./navbar"
import GlobalFooter from "./globalFooter";
type Props = {
children: JSX.Element | JSX.Element[]
}
export default function ThemeWrapper({ children }: Props) {
return (
<div className='styled-scroll'>
<main className="styled-scroll">
<div className='flex flex-col items-center min-h-screen overflow-hidden relative'>
<Toaster position="top-center" toastOptions={{
duration: 5000,
style: {
background: '#131E36',
color: '#a4afc8'
},
position: 'top-center',
error: {
duration: Infinity,
},
}}>
{(t) => (
<ToastBar toast={t}>
{({ icon, message }) => (
<>
{icon}
{message}
{t.type !== 'loading' && (
<button type="button" onClick={() => toast.dismiss(t.id)}><X className="h-5" /></button>
)}
</>
)}
</ToastBar>
)}
</Toaster>
<Navbar />
<div className="w-full max-w-lg z-[1] mb-6">
<div className="flex content-center items-center justify-center space-y-5 flex-col container mx-auto sm:px-6 max-w-lg">
<div className="flex flex-col w-full text-foreground">
{children}
</div>
</div>
</div>
<div id="offset-for-stickyness" className="block md:hidden"></div>
<GlobalFooter />
</div>
</main>
</div>
)
}
@@ -1,6 +0,0 @@
export default function upperCaseKeys(obj: object) {
return Object.keys(obj).reduce((accumulator, key) => {
accumulator[key.toUpperCase()] = obj[key];
return accumulator;
}, {});
}
-30
View File
@@ -1,30 +0,0 @@
"use client";
import React, { Context, FC } from "react";
import { BridgeAppSettings } from "@/Models/BridgeAppSettings";
export const SettingsStateContext =
React.createContext<BridgeAppSettings | null>(null);
export const SettingsProvider: FC<{
data: BridgeAppSettings;
children?: React.ReactNode;
}> = ({ children, data }) => {
return (
<SettingsStateContext.Provider value={data}>
{children}
</SettingsStateContext.Provider>
);
};
export function useSettingsState() {
const data = React.useContext<BridgeAppSettings>(
SettingsStateContext as Context<BridgeAppSettings>
);
// console.log("::app settings:", Object.keys(data));
if (data === undefined) {
throw new Error("useSettingsState must be used within a SettingsProvider");
}
return data;
}
-18
View File
@@ -1,18 +0,0 @@
import BridgeApiClient from "../lib/BridgeApiClient";
import { getThemeData } from "./settingsHelper";
export async function getServerSideProps(context) {
context.res.setHeader(
'Cache-Control',
's-maxage=60, stale-while-revalidate'
);
var apiClient = new BridgeApiClient();
const { data: settings } = await apiClient.GetSettingsAsync()
const themeData = await getThemeData(context.query)
return {
props: { settings, themeData }
}
}
-29
View File
@@ -1,29 +0,0 @@
import BridgeApiClient from "../lib/BridgeApiClient";
import { getThemeData } from "./settingsHelper";
export async function getServerSideProps(context) {
// context.res.setHeader("Cache-Control", "s-maxage=60, stale-while-revalidate");
// const apiClient = new BridgeApiClient();
// const { data: networkData } = await apiClient.GetLSNetworksAsync();
// const { data: exchangeData } = await apiClient.GetExchangesAsync();
// const { data: sourceRoutes } = await apiClient.GetSourceRoutesAsync();
// const { data: destinationRoutes } =
// await apiClient.GetDestinationRoutesAsync();
// if (!networkData || !exchangeData) return;
const settings = {
networks: [],
exchanges: [],
sourceRoutes: [],
destinationRoutes: [],
};
const themeData = await getThemeData(context.query);
return {
props: { settings, themeData },
};
}
-16
View File
@@ -1,16 +0,0 @@
import { ParsedUrlQuery } from "querystring";
import { PersistantQueryParams, QueryParams } from "../Models/QueryParams";
export const resolvePersistantQueryParams = (query: ParsedUrlQuery): ParsedUrlQuery | null => {
if (!query)
return null
const persiatantParams = new PersistantQueryParams()
const res: ParsedUrlQuery = {}
Object.keys(persiatantParams).forEach(key => {
if (query[key] !== undefined) {
res[key] = query[key]
}
})
return res
}
-13
View File
@@ -1,13 +0,0 @@
import { useRouter } from "next/router"
import { useCallback } from "react"
import { resolvePersistantQueryParams } from "../helpers/querryHelper"
export const useGoHome = (): () => Promise<boolean> => {
const router = useRouter()
return useCallback(async () => {
return await router.push({
pathname: "/",
query: { ...resolvePersistantQueryParams(router.query) }
})
}, [router])
}
-121
View File
@@ -1,121 +0,0 @@
import { PublicClient, encodeFunctionData } from "viem";
import { erc20ABI } from "wagmi";
import { NetworkCurrency } from "../../../Models/CryptoNetwork";
import { Layer } from "../../../Models/Layer";
import { Gas } from "../../../Models/Balance";
export default abstract class getEVMGas {
protected publicClient: PublicClient
protected chainId: number
protected contract_address: `0x${string}`
protected account: `0x${string}`
protected from: Layer
protected currency: NetworkCurrency
protected destination: `0x${string}`
protected nativeToken: NetworkCurrency
protected isSweeplessTx: boolean
constructor(
publicClient: PublicClient,
chainId: number,
contract_address: `0x${string}`,
account: `0x${string}`,
from: Layer,
currency: NetworkCurrency,
destination: `0x${string}`,
nativeToken: NetworkCurrency,
isSweeplessTx: boolean
) {
this.publicClient = publicClient
this.chainId = chainId
this.contract_address = contract_address
this.account = account
this.from = from
this.currency = currency
this.destination = destination
this.nativeToken = nativeToken
this.isSweeplessTx = isSweeplessTx
}
abstract resolveGas(): Promise<Gas | unknown> | undefined
protected async resolveFeeData() {
let gasPrice = await this.getGasPrice();
let feesPerGas = await this.estimateFeesPerGas()
let maxPriorityFeePerGas = await this.estimateMaxPriorityFeePerGas()
return {
gasPrice,
maxFeePerGas: feesPerGas?.maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas
}
}
private async getGasPrice() {
try {
return await this.publicClient.getGasPrice()
} catch (e) {
//TODO: log the error to our logging service
console.log(e)
}
}
private async estimateFeesPerGas() {
try {
return await this.publicClient.estimateFeesPerGas()
} catch (e) {
//TODO: log the error to our logging service
console.log(e)
}
}
private async estimateMaxPriorityFeePerGas() {
try {
return await this.publicClient.estimateMaxPriorityFeePerGas()
} catch (e) {
//TODO: log the error to our logging service
console.log(e)
}
}
protected async estimateNativeGasLimit() {
const to = this.destination;
const gasEstimate = await this.publicClient.estimateGas({
account: this.account,
to: to,
data: this.constructSweeplessTxData(),
})
return gasEstimate
}
protected async estimateERC20GasLimit() {
let encodedData = encodeFunctionData({
abi: erc20ABI,
functionName: "transfer",
args: [this.destination, BigInt(1000)]
})
if (encodedData && this.isSweeplessTx) {
encodedData = this.constructSweeplessTxData(encodedData)
}
const estimatedERC20GasLimit = await this.publicClient.estimateGas({
data: encodedData,
to: this.contract_address,
account: this.account
});
return estimatedERC20GasLimit
}
protected constructSweeplessTxData = (txData: string = "0x") => {
const hexed_sequence_number = (99999999).toString(16)
const sequence_number_even = hexed_sequence_number?.length % 2 > 0 ? `0${hexed_sequence_number}` : hexed_sequence_number
return `${txData}${sequence_number_even}` as `0x${string}`;
}
}
-140
View File
@@ -1,140 +0,0 @@
import { PublicClient } from "viem"
import { Layer } from "../../../Models/Layer"
import formatAmount from "../../formatAmount"
import { erc20ABI } from "wagmi"
import { multicall, fetchBalance, FetchBalanceResult } from '@wagmi/core'
import { NetworkCurrency } from "../../../Models/CryptoNetwork"
import { Balance } from "../../../Models/Balance"
export type ERC20ContractRes = ({
error: Error;
result?: undefined | null;
status: "failure";
} | {
error?: undefined | null;
result: unknown;
status: "success";
})
export const resolveERC20Balances = async (
multicallRes: ERC20ContractRes[],
from: Layer,
) => {
const assets = from?.assets?.filter(a => a.contract_address)
if (!assets)
return null
const contractBalances = multicallRes?.map((d, index) => {
const currency = assets[index]
return {
network: from.internal_name,
token: currency.asset,
amount: formatAmount(d.result, currency.decimals),
request_time: new Date().toJSON(),
decimals: currency.decimals,
isNativeCurrency: false,
}
})
return contractBalances
}
type GetBalanceArgs = {
address: string,
chainId: number,
assets: NetworkCurrency[],
publicClient: PublicClient,
hasMulticall: boolean
}
export const getErc20Balances = async ({
address,
chainId,
assets,
publicClient,
hasMulticall = false
}: GetBalanceArgs): Promise<ERC20ContractRes[] | null> => {
const contracts = assets?.filter(a => a.contract_address).map(a => ({
address: a?.contract_address as `0x${string}`,
abi: erc20ABI,
functionName: 'balanceOf',
args: [address],
}))
try {
if (hasMulticall) {
const contractRes = await multicall({
chainId: chainId,
contracts: contracts
})
return contractRes
}
else {
const balances: ERC20ContractRes[] = []
for (let i = 0; i < contracts.length; i++) {
try {
const contract = contracts[i]
const balance = await publicClient.readContract({
address: contract?.address as `0x${string}`,
abi: erc20ABI,
functionName: 'balanceOf',
args: [address as `0x${string}`]
})
balances.push({
status: "success",
result: balance,
error: undefined
})
}
catch (e) {
balances.push({
status: "failure",
result: null,
error: e?.message
})
}
}
return balances
}
}
catch (e) {
//TODO: log the error to our logging service
console.log(e);
return null;
}
}
export const getNativeBalance = async (address: `0x${string}`, chainId: number): Promise<FetchBalanceResult | null> => {
try {
const nativeTokenRes = await fetchBalance({
address,
chainId
})
return nativeTokenRes
} catch (e) {
//TODO: log the error to our logging service
console.log(e)
return null
}
}
export const resolveNativeBalance = async (
from: Layer,
nativeTokenRes: FetchBalanceResult
) => {
const native_currency = from.assets.find(a => a.is_native)
if (!native_currency) {
return null
}
const nativeBalance: Balance = {
network: from.internal_name,
token: native_currency.asset,
amount: formatAmount(nativeTokenRes?.value, native_currency.decimals),
request_time: new Date().toJSON(),
decimals: native_currency.decimals,
isNativeCurrency: true,
}
return nativeBalance
}
@@ -1,50 +0,0 @@
//from https://github.com/ethereum-optimism/optimism/blob/develop/packages/fee-estimation/src/estimateFees.ts
import {
gasPriceOracleABI,
gasPriceOracleAddress,
} from '@eth-optimism/contracts-ts'
import {
getContract,
BlockTag,
PublicClient,
} from 'viem'
/**
* Options to query a specific block
*/
type BlockOptions = {
/**
* Block number to query from
*/
blockNumber?: bigint
/**
* Block tag to query from
*/
blockTag?: BlockTag
}
/**
* Get gas price Oracle contract
*/
export const getGasPriceOracleContract = (client: PublicClient) => {
return getContract({
address: gasPriceOracleAddress['420'],
abi: gasPriceOracleABI,
publicClient: client,
})
}
/**
* Computes the L1 portion of the fee based on the size of the rlp encoded input
* transaction, the current L1 base fee, and the various dynamic parameters.
* @example
* const L1FeeValue = await getL1Fee(data, params);
*/
export const getL1Fee = async (options: { data: `0x02${string}`, client: PublicClient } & BlockOptions) => {
const contract = getGasPriceOracleContract(options.client)
return contract.read.getL1Fee([options.data], {
blockNumber: options.blockNumber,
blockTag: options.blockTag,
})
}
@@ -1,82 +0,0 @@
import { TransactionSerializedEIP1559, encodeFunctionData, formatGwei, serializeTransaction } from "viem";
import formatAmount from "../../../formatAmount";
import getEVMGas from "../gas";
import { erc20ABI } from "wagmi";
import { getL1Fee } from "./estimateFees";
import { Gas } from "../../../../Models/Balance";
export default class getOptimismGas extends getEVMGas {
resolveGas = async (): Promise<Gas | undefined> => {
const feeData = await this.resolveFeeData()
const estimatedGasLimit = this.contract_address ?
await this.estimateERC20GasLimit()
: await this.estimateNativeGasLimit()
const multiplier = feeData.maxFeePerGas || feeData.gasPrice
if (!multiplier)
return undefined
let totalGas = (multiplier * estimatedGasLimit) + await this.GetOpL1Fee()
const formattedGas = formatAmount(totalGas, this.nativeToken?.decimals)
return {
gas: formattedGas,
token: this.currency?.asset,
gasDetails: {
gasLimit: Number(estimatedGasLimit),
maxFeePerGas: feeData?.maxFeePerGas ? Number(formatGwei(feeData?.maxFeePerGas)) : undefined,
gasPrice: feeData?.gasPrice ? Number(formatGwei(feeData?.gasPrice)) : undefined,
maxPriorityFeePerGas: feeData?.maxPriorityFeePerGas ? Number(formatGwei(feeData?.maxPriorityFeePerGas)) : undefined,
},
request_time: new Date().toJSON()
}
}
private GetOpL1Fee = async (): Promise<bigint> => {
const amount = BigInt(1000)
let serializedTransaction: TransactionSerializedEIP1559
if (this.contract_address) {
let encodedData = encodeFunctionData({
abi: erc20ABI,
functionName: "transfer",
args: [this.destination, amount]
})
if (encodedData && this.isSweeplessTx) {
encodedData = this.constructSweeplessTxData(encodedData)
}
serializedTransaction = serializeTransaction({
client: this.publicClient,
abi: erc20ABI,
functionName: "transfer",
chainId: this.chainId,
args: [this.destination, amount],
to: this.contract_address,
data: encodedData,
type: 'eip1559',
}) as TransactionSerializedEIP1559
}
else {
serializedTransaction = serializeTransaction({
client: this.publicClient,
chainId: this.chainId,
to: this.destination,
data: this.constructSweeplessTxData(),
type: 'eip1559',
}) as TransactionSerializedEIP1559
}
const fee = await getL1Fee({
data: serializedTransaction,
client: this.publicClient,
})
return fee;
}
}
-50
View File
@@ -1,50 +0,0 @@
import { type PublicClient, usePublicClient, type WalletClient, useWalletClient } from 'wagmi'
import { providers } from 'ethers'
import { type HttpTransport } from 'viem'
import { useMemo } from 'react'
function publicClientToProvider(publicClient: PublicClient) {
const { chain, transport } = publicClient
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
}
if (transport.type === 'fallback')
return new providers.FallbackProvider(
(transport.transports as ReturnType<HttpTransport>[]).map(
({ value }) => new providers.JsonRpcProvider(value?.url, network),
),
)
return new providers.Web3Provider(transport.url, network)
}
/** Hook to convert a viem Public Client to an ethers.js Provider. */
function useEthersProvider({ chainId }: { chainId?: number } = {}) {
const publicClient = usePublicClient({ chainId })
return useMemo(() => publicClientToProvider(publicClient), [publicClient])
}
function walletClientToSigner(walletClient: WalletClient) {
const { account, chain, transport } = walletClient
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
}
// Force disable type checking of transport
// See https://github.com/wagmi-dev/viem/discussions/792#discussioncomment-6297530
const provider = new providers.Web3Provider(transport, network)
const signer = provider.getSigner(account.address)
return signer
}
/** Hook to convert a viem Wallet Client to an ethers.js Signer. */
export function useEthersSigner({ chainId }: { chainId?: number } = {}) {
const { data: walletClient } = useWalletClient({ chainId })
return useMemo(
() => (walletClient ? walletClientToSigner(walletClient) : undefined),
[walletClient],
)
}
-300
View File
@@ -1,300 +0,0 @@
import { Layer } from "../Models/Layer";
import NetworkSettings from "./NetworkSettings";
import { SendErrorMessage } from "./telegram";
export default function resolveChain(network: Layer): (Chain & RainbowKitChain) | undefined {
const nativeCurrency = network.assets.find(c => c.is_native);
const blockExplorersBaseURL = new URL(network.transaction_explorer_template).origin;
const metadata = network.metadata
const { ensRegistry, ensUniversalResolver, multicall3 } = metadata || {}
if (!nativeCurrency) {
SendErrorMessage("UI Settings error", `env: ${process.env.NEXT_PUBLIC_VERCEL_ENV} %0A url: ${process.env.NEXT_PUBLIC_VERCEL_URL} %0A message: could not find native currency for ${network.internal_name} %0A`)
return
}
const res: Chain & RainbowKitChain = {
id: Number(network.chain_id),
name: network.display_name,
network: network.internal_name,
nativeCurrency: {
name: nativeCurrency.asset,
symbol: nativeCurrency.asset,
decimals: nativeCurrency.decimals
},
iconUrl: network.img_url,
rpcUrls: {
default: {
http: network.nodes.map(n => n?.url),
},
public: {
http: network.nodes.map(n => n?.url),
},
},
blockExplorers: {
default: {
name: 'name',
url: blockExplorersBaseURL,
},
},
contracts: {
...(multicall3 ? {
multicall3:
multicall3
} : {}),
...(ensRegistry ? {
ensRegistry:
ensRegistry
} : {}),
...(ensUniversalResolver ? {
ensUniversalResolver:
ensUniversalResolver
} : {}),
},
}
const defaultPriorityFee = NetworkSettings.KnownSettings[network.internal_name]?.DefaultPriorityFee?.toString()
const baseFeeMultiplier = NetworkSettings.KnownSettings[network.internal_name]?.BaseFeeMultiplier ?? 1.2
if (defaultPriorityFee) {
res.fees = {
...res.fees,
defaultPriorityFee: () => parseGwei(defaultPriorityFee),
}
}
if (baseFeeMultiplier) {
res.fees = {
...res.fees,
baseFeeMultiplier: () => baseFeeMultiplier
}
}
return res
}
type Chain<
formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
> = ChainConstants & ChainConfig<formatters>
type RainbowKitChain = {
id: number;
name?: string;
iconUrl?: string | (() => Promise<string>) | null;
iconBackground?: string;
} & Chain
/**
* Multiplies a string representation of a number by a given exponent of base 10 (10exponent).
*
* - Docs: https://viem.sh/docs/utilities/parseUnits.html
*
* @example
* import { parseUnits } from 'viem'
*
* parseUnits('420', 9)
* // 420000000000n
*/
export function parseUnits(value: string, decimals: number) {
let [integer, fraction = '0'] = value.split('.')
const negative = integer.startsWith('-')
if (negative) integer = integer.slice(1)
// trim leading zeros.
fraction = fraction.replace(/(0+)$/, '')
// round off if the fraction is larger than the number of decimals.
if (decimals === 0) {
if (Math.round(Number(`.${fraction}`)) === 1)
integer = `${BigInt(integer) + 1n}`
fraction = ''
} else if (fraction.length > decimals) {
const [left, unit, right] = [
fraction.slice(0, decimals - 1),
fraction.slice(decimals - 1, decimals),
fraction.slice(decimals),
]
const rounded = Math.round(Number(`${unit}.${right}`))
if (rounded > 9)
fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, '0')
else fraction = `${left}${rounded}`
if (fraction.length > decimals) {
fraction = fraction.slice(1)
integer = `${BigInt(integer) + 1n}`
}
fraction = fraction.slice(0, decimals)
} else {
fraction = fraction.padEnd(decimals, '0')
}
return BigInt(`${negative ? '-' : ''}${integer}${fraction}`)
}
function parseGwei(ether: string, unit: 'wei' = 'wei') {
return parseUnits(ether, gweiUnits[unit])
}
const gweiUnits = {
ether: -9,
wei: 9,
}
export type ChainBlockExplorer = {
name: string
url: string
}
export type ChainConstants = {
/** Collection of block explorers */
blockExplorers?: {
default: ChainBlockExplorer
etherscan?: ChainBlockExplorer
}
/** Collection of contracts */
contracts?: {
[key: string]: ChainContract | { [chainId: number]: ChainContract }
} & {
ensRegistry?: ChainContract
ensUniversalResolver?: ChainContract
multicall3?: ChainContract
}
/** ID in number form */
id: number
/** Human-readable name */
name: string
/**
* Internal network name
* @deprecated will be removed in v2 - use `id` instead.
*/
network: string
/** Currency used by chain */
nativeCurrency: ChainNativeCurrency
/** Collection of RPC endpoints */
rpcUrls: {
[key: string]: ChainRpcUrls
default: ChainRpcUrls
public: ChainRpcUrls
}
/** Source Chain ID (ie. the L1 chain) */
sourceId?: number
/** Flag for test networks */
testnet?: boolean
// TODO(v2): remove `rpcUrls` in favor of `publicRpcUrls`.
// publicRpcUrls: ChainRpcUrls,
}
export type ChainContract = {
address: `0x${string}`
blockCreated?: number
}
export type ChainNativeCurrency = {
name: string
/** 2-6 characters long */
symbol: string
decimals: number
}
export type ChainRpcUrls = {
http: readonly string[]
webSocket?: readonly string[]
}
export type ChainConfig<
formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
> = {
/**
* Modifies how chain data structures (ie. Blocks, Transactions, etc)
* are formatted & typed.
*/
formatters?: formatters | undefined
/** Modifies how data (ie. Transactions) is serialized. */
serializers?: ChainSerializers<formatters> | undefined
/** Modifies how fees are derived. */
fees?: ChainFees<formatters> | undefined
}
export type ChainFees<
formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
> = {
/**
* The fee multiplier to use to account for fee fluctuations.
* Used in the [`estimateFeesPerGas` Action](/docs/actions/public/estimateFeesPerGas).
*
* @default 1.2
*/
}
export type ChainFormatters = {
/** Modifies how the Block structure is formatted & typed. */
block?: ChainFormatter<'block'>
/** Modifies how the Transaction structure is formatted & typed. */
transaction?: ChainFormatter<'transaction'>
/** Modifies how the TransactionReceipt structure is formatted & typed. */
transactionReceipt?: ChainFormatter<'transactionReceipt'>
/** Modifies how the TransactionRequest structure is formatted & typed. */
transactionRequest?: ChainFormatter<'transactionRequest'>
}
export type ChainFormatter<type extends string = string> = {
format: (args: any) => any
type: type
}
export type ChainSerializers<
formatters extends ChainFormatters | undefined = undefined,
> = {
/** Modifies how Transactions are serialized. */
}
/////////////////////////////////////////////////////////////////////
// Utils
export type ExtractChain<
chains extends readonly Chain[],
chainId extends Chain['id'],
> = Extract<chains[number], { id: chainId }>
export type ExtractChainFormatterExclude<
chain extends { formatters?: Chain['formatters'] } | undefined,
type extends keyof ChainFormatters,
> = chain extends { formatters?: infer _Formatters extends ChainFormatters }
? _Formatters[type] extends { exclude: infer Exclude }
? Extract<Exclude, string[]>[number]
: ''
: ''
export type ExtractChainFormatterParameters<
chain extends { formatters?: Chain['formatters'] } | undefined,
type extends keyof ChainFormatters,
fallback,
> = chain extends { formatters?: infer _Formatters extends ChainFormatters }
? _Formatters[type] extends ChainFormatter
? Parameters<_Formatters[type]['format']>[0]
: fallback
: fallback
export type ExtractChainFormatterReturnType<
chain extends { formatters?: Chain['formatters'] } | undefined,
type extends keyof ChainFormatters,
fallback,
> = chain extends { formatters?: infer _Formatters extends ChainFormatters }
? _Formatters[type] extends ChainFormatter
? ReturnType<_Formatters[type]['format']>
: fallback
: fallback
export type GetChain<
chain extends Chain | undefined,
chainOverride extends Chain | undefined = undefined,
> = IsUndefined<chain> extends true
? { chain: chainOverride | null }
: { chain?: chainOverride | null }
type IsUndefined<T> = [undefined] extends [T] ? true : false
@@ -1,60 +0,0 @@
import MetaMaskIcon from "../../../components/icons/Wallets/MetaMask"
import WalletConnectIcon from "../../../components/icons/Wallets/WalletConnect"
import BitKeep from "../../../components/icons/Wallets/BitKeep"
import RainbowIcon from "../../../components/icons/Wallets/Rainbow"
import CoinbaseIcon from "../../../components/icons/Wallets/Coinbase"
import { Coins } from "lucide-react"
import Phantom from "../../../components/icons/Wallets/Phantom"
import { Connector } from "wagmi"
export const ResolveEVMWalletIcon = ({ connector }: { connector: Connector<any, any> }) => {
let icon: ((props: any) => JSX.Element) | null = null;
// Check first by id then by name
switch (connector?.id?.toLowerCase()) {
case KnownKonnectorIds.MetaMask:
icon = MetaMaskIcon;
break;
case KnownKonnectorIds.WalletConnect:
icon = WalletConnectIcon;
break;
case KnownKonnectorIds.Rainbow:
icon = RainbowIcon;
break;
case KnownKonnectorIds.BitKeep:
icon = BitKeep;
break;
case KnownKonnectorIds.CoinbaseWallet:
icon = CoinbaseIcon;
break;
}
if (icon == null) {
switch (connector?.name?.toLowerCase()) {
case KnownKonnectorNames.Phantom:
icon = Phantom;
}
}
if (icon == null) {
icon = CoinsIcon
}
return icon;
}
const KnownKonnectorIds = {
MetaMask: 'metamask',
WalletConnect: 'walletconnect',
Rainbow: 'rainbow',
BitKeep: 'bitkeep',
CoinbaseWallet: 'coinbasewallet',
}
const KnownKonnectorNames = {
Phantom: 'phantom',
}
const CoinsIcon = (props) => {
return <Coins {...props} strokeWidth={2} />
}
-54
View File
@@ -1,54 +0,0 @@
import { useConnectModal } from "@rainbow-me/rainbowkit"
import { useDisconnect } from 'wagmi';
import { useAccount } from "wagmi"
import { NetworkType } from "../../../Models/CryptoNetwork"
import { useSettingsState } from "../../../context/settings"
import { WalletProvider } from "../../../hooks/useWallet"
import KnownInternalNames from "../../knownIds"
import { ResolveEVMWalletIcon } from "./resolveEVMIcon"
export default function useEVM(): WalletProvider {
const { disconnect } = useDisconnect();
const { layers } = useSettingsState();
const withdrawalSupportedNetworks = [
...layers.filter(layer => layer.type === NetworkType.EVM).map(l => l.internal_name),
KnownInternalNames.Networks.ZksyncMainnet
]
const autofillSupportedNetworks = [
...withdrawalSupportedNetworks,
KnownInternalNames.Networks.ImmutableXMainnet,
KnownInternalNames.Networks.ImmutableXGoerli,
KnownInternalNames.Networks.BrineMainnet,
KnownInternalNames.Networks.LoopringGoerli,
KnownInternalNames.Networks.LoopringMainnet
]
const name = 'evm'
const account = useAccount()
const { openConnectModal } = useConnectModal()
const getWallet = () => {
if (account && account.address && account.connector) {
return {
address: account.address,
connector: account.connector?.id,
providerName: name,
icon: ResolveEVMWalletIcon({ connector: account.connector })
}
}
}
const connectWallet = () => {
return openConnectModal && openConnectModal()
}
return {
getConnectedWallet: getWallet,
connectWallet,
disconnectWallet: disconnect,
autofillSupportedNetworks,
withdrawalSupportedNetworks,
name
}
}
@@ -1,28 +0,0 @@
import { Coins } from "lucide-react"
import Argent from "../../../components/icons/Wallets/Argent"
import ArgentX from "../../../components/icons/Wallets/ArgentX"
import Braavos from "../../../components/icons/Wallets/Braavos"
export const ResolveStarknetWalletIcon = ({ connector }: { connector: string }) => {
switch (connector?.toLowerCase()) {
case KnownKonnectors.ArgentX:
return ArgentX
case KnownKonnectors.ArgentMobile:
return Argent
case KnownKonnectors.Braavos:
return Braavos
default:
return CoinsIcon
}
}
const KnownKonnectors = {
ArgentX: 'argent x',
ArgentMobile: 'argent mobile',
Braavos: 'braavos',
}
const CoinsIcon = (props) => {
return <Coins {...props} strokeWidth={2} />
}
+29
View File
@@ -0,0 +1,29 @@
// https://react-svgr.com/docs/next
module.exports = (config) => {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.('.svg'),
)
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: ['@svgr/webpack'],
},
)
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i
return config
}
@@ -0,0 +1,36 @@
const watchExternalFilesPlugin = require('webpack-watch-files-plugin').default
const disable = false
const localModules = [
//'../../../../hanzoai/react-sdk/packages/ui',
//'../../../../hanzoai/react-sdk/packages/commerce'
]
const suffixes = [
'/**/*.ts',
'/**/*.tsx',
]
const flatArray = localModules.map((p) => {
return suffixes.map((s) => (p + s))
}).flat()
module.exports = ( config ) => {
if (disable) {
return config
}
config.watchOptions = {
poll: 1000,
aggregateTimeout: 300,
}
config.plugins.push(
new watchExternalFilesPlugin({
files: flatArray,
})
)
return config
}
+1 -1
View File
@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
+26 -3
View File
@@ -3,6 +3,8 @@
const withMDX = require("@next/mdx")();
const { PHASE_PRODUCTION_SERVER } = require("next/constants");
const path = require("path")
const svgrPluginConfig = require('./next-conf/svgr.next.config')
const securityHeaders = [
// { key: "Access-Control-Allow-Origin", value: "*" },
@@ -21,6 +23,9 @@ module.exports = (phase, { defaultConfig }) => {
* @type {import('next').NextConfig}
*/
const nextConfig = {
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
reactStrictMode: true,
i18n: {
locales: ["en"],
defaultLocale: "en",
@@ -43,6 +48,15 @@ module.exports = (phase, { defaultConfig }) => {
protocol: "https",
hostname: "cdn.lux.network",
},
{
protocol: 'https',
hostname: 'img.youtube.com',
pathname: '**',
},
{
protocol: "http",
hostname: "localhost",
}
],
},
compiler: {
@@ -50,16 +64,25 @@ module.exports = (phase, { defaultConfig }) => {
},
reactStrictMode: false,
webpack: (config, { isServer }) => {
config.externals.push("pino-pretty", "lokijs", "encoding");
config.resolve.fallback = { fs: false, net: false, tls: false };
if (!isServer) {
config.resolve.alias['@'] = path.resolve(__dirname);
config.resolve.alias['@'] = path.resolve(__dirname + '/src');
}
return config;
let conf = svgrPluginConfig(config)
return conf
},
productionBrowserSourceMaps: true,
// https://stackoverflow.com/questions/72621835/how-to-fix-you-may-need-an-appropriate-loader-to-handle-this-file-type-current
transpilePackages: ["@luxdefi/ui"],
transpilePackages: [
'@hanzo/ui',
'@hanzo/auth',
'@hanzo/commerce',
'@luxfi/ui',
'@luxfi/data'
],
};
if (process.env.APP_BASE_PATH) {
nextConfig.basePath = process.env.APP_BASE_PATH;
}
+25 -16
View File
@@ -18,14 +18,16 @@
},
"dependencies": {
"@eth-optimism/contracts-ts": "^0.15.0",
"@hanzo/auth": "*",
"@hanzo/ui": "*",
"@headlessui/react": "^1.7.3",
"@imtbl/imx-sdk": "2.1.1",
"@loopring-web/loopring-sdk": "3.3.5",
"@loopring-web/web3-provider": "1.4.13",
"@luxdefi/ui": "0.2.4",
"@luxfi/ui": "*",
"@metamask/jazzicon": "^2.0.0",
"@next/mdx": "14.1.0",
"@prisma/client": "^5.16.2",
"@prisma/client": "^5.21.1",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-context-menu": "^1.0.0",
@@ -35,7 +37,7 @@
"@radix-ui/react-select": "^1.2.1",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.7",
"@rainbow-me/rainbowkit": "^1.1.2",
"@rainbow-me/rainbowkit": "^2.0.5",
"@sentry/nextjs": "^7.80.1",
"@solana/spl-token": "^0.3.9",
"@solana/wallet-adapter-base": "0.9.18",
@@ -49,8 +51,10 @@
"@solana/web3.js": "1.66.0",
"@stripe/crypto": "^0.0.4",
"@stripe/stripe-js": "^1.53.0",
"@svgr/webpack": "^8.1.0",
"@tailwindcss/forms": "^0.5.0",
"@tailwindcss/typography": "^0.5.2",
"@tanstack/react-query": "^5.59.16",
"@ton/core": "^0.53.0",
"@ton/crypto": "^3.2.0",
"@tonconnect/ui-react": "^2.0.0-beta.4",
@@ -70,24 +74,28 @@
"js-sha3": "^0.8.0",
"lucide-react": "^0.307.0",
"markdown-to-jsx": "^7.4.0",
"next": "14.1.0",
"multicoin-address-validator": "^0.5.21",
"next": "14.2.14",
"next-themes": "^0.2.1",
"prisma": "^5.16.2",
"pino-pretty": "^11.3.0",
"prisma": "^5.21.1",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "*",
"react-dom": "*",
"react-error-boundary": "^4.0.11",
"react-hot-toast": "^2.2.0",
"react-swipeable": "^7.0.0",
"react-toastify": "^10.0.6",
"react-use-intercom": "^2.0.0",
"starknet": "^5.19.5",
"starknetkit": "^1.0.10",
"starknet": "^6.11.0",
"starknetkit": "^2.3.3",
"swr": "^2.1.1",
"tailwindcss-animate": "^1.0.5",
"ton": "^13.9.0",
"use-async-effect": "^2.2.7",
"uuid": "^9.0.0",
"viem": "^1.13.1",
"wagmi": "^1.4.12",
"viem": "^2.9.9",
"wagmi": "^2.5.19",
"web3": "^4.11.1",
"zksync": "^0.13.1",
"zustand": "^4.4.1"
@@ -108,9 +116,10 @@
"@types/bn.js": "^5.1.0",
"@types/crypto-js": "^4.1.1",
"@types/mdx": "^2.0.9",
"@types/multicoin-address-validator": "^0.5.3",
"@types/node": "^20.10.7",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"@types/react": "*",
"@types/react-dom": "*",
"@types/web3": "^1.2.2",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
@@ -119,7 +128,7 @@
"chromatic": "^6.24.1",
"cross-env": "^7.0.3",
"eslint": "8.49.0",
"eslint-config-next": "^14.0.4",
"eslint-config-next": "^14.2.15",
"eslint-plugin-no-conditional-literals-in-jsx": "file:eslint-plugins/eslint-plugin-no-conditional-literals-in-jsx",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-storybook": "^0.6.14",
@@ -129,7 +138,7 @@
"storybook": "^7.4.5",
"storybook-addon-mock": "^4.3.0",
"storybook-react-context": "^0.6.0",
"tailwindcss": "^3.4.1",
"typescript": "5.3.3"
"tailwindcss": "*",
"typescript": "*"
}
}
-49
View File
@@ -1,49 +0,0 @@
import React, { useEffect } from "react"
import { ChevronLeft } from "lucide-react"
import { LinkElement } from "@luxdefi/ui/primitives"
import MessageComponent from "../components/MessageComponent"
import Navbar from "../components/navbar"
const Custom404: React.FC = () => {
useEffect(() => {
plausible("404", { props: { path: document.location.pathname } })
}, [])
return (
<div className="styled-scroll">
<main className="styled-scroll">
<div className="min-h-screen overflow-hidden relative font-robo">
<Navbar />
<div className="mx-auto max-w-xl md:shadow-card rounded-lg w-full overflow-hidden relative px-6 py-6 h-[500px] min-h-[550px]">
<MessageComponent >
<MessageComponent.Content icon="red" >
<div className="text-center text-foreground">
<p className="text-base font-semibold ">404</p>
<h1 className="mt-2 text-4xl font-bold tracking-tight sm:text-5xl">Page not found.</h1>
<p className="mt-2 text-base ">Sorry, we couldn&apos;t find the page you&apos;re looking for.</p>
</div>
</MessageComponent.Content>
<MessageComponent.Buttons>
<LinkElement
def={{
href: '/',
title: 'Back to main page',
icon: <ChevronLeft className="mr-2 h-4 w-4" /> ,
variant: 'ghost',
size: 'lg'
}}
className="text-base "
/>
</MessageComponent.Buttons>
</MessageComponent>
</div>
</div>
</main>
</div>
)
}
export default Custom404
-27
View File
@@ -1,27 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { mainnetSettings, testnetSettings } from "../../../settings";
import { CryptoNetwork } from "../../../Models/CryptoNetwork";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<{
data: CryptoNetwork[] | any;
}>
) {
try {
const { version } = req.query;
const isMainnet = true
// const isMainnet =
// version === "mainnet" ||
// process.env.NEXT_PUBLIC_API_VERSION === "mainnet";
// settings
const settings = isMainnet ? mainnetSettings : testnetSettings;
const { networks } = settings.data;
return res.status(200).json({
data: networks,
});
} catch (error) {
console.error("Error in fetching networks", error);
res.status(500).json({ data: error.message });
}
}
-26
View File
@@ -1,26 +0,0 @@
import prisma from "../../../lib/db";
import type { NextApiRequest, NextApiResponse } from "next";
/**
* update network and currencies according to settings file
* api/networks/update
*
* @param req
* @param res
* @returns
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const data = await prisma.quote.findMany({});
return res.status(200).json({
status: "success",
data,
});
} catch (error) {
console.error("Error in fetching networks", error);
res.status(500).json({ data: error.message });
}
}
-29
View File
@@ -1,29 +0,0 @@
import Layout from "../components/layout";
import { FormWizardProvider } from "../context/formWizardProvider";
import { AuthStep } from "../Models/Wizard";
import AuthWizard from "../components/Wizard/AuthWizard";
import { SwapDataProvider } from "../context/swap";
import { InferGetServerSidePropsType } from "next";
import { getServerSideProps } from "../helpers/getSettings";
export default function AuthPage({
settings,
themeData,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<>
<Layout settings={settings} themeData={themeData}>
<SwapDataProvider>
<FormWizardProvider
initialStep={AuthStep.Email}
initialLoading={false}
>
<AuthWizard />
</FormWizardProvider>
</SwapDataProvider>
</Layout>
</>
);
}
export { getServerSideProps };
@@ -1,18 +0,0 @@
import React from 'react'
import { InferGetServerSidePropsType } from 'next'
import Layout from '../../components/layout'
import CampaignDetails from '../../components/Campaigns/Details'
import { getServerSideProps } from '../../helpers/getSettings'
function RewardsPage({ settings, themeData }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<Layout settings={settings} themeData={themeData}>
<CampaignDetails />
</Layout>
)
}
export default RewardsPage
-15
View File
@@ -1,15 +0,0 @@
import Layout from '../../components/layout'
import { InferGetServerSidePropsType } from 'next'
import Campaigns from '../../components/Campaigns'
import { getServerSideProps } from '../../helpers/getSettings'
export default function CampaignsPage({ settings, themeData }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (<>
<Layout settings={settings} themeData={themeData}>
<Campaigns />
</Layout>
</>)
}
export { getServerSideProps };
-21
View File
@@ -1,21 +0,0 @@
import Layout from "../components/layout";
import React from "react";
import { InferGetServerSidePropsType } from "next";
import { getServerSideProps } from "@/helpers/getSettings";
import { useAtom } from "jotai";
import { useTelepoterAtom } from "@/store/teleport";
import Swapper from "@/components/Swapper";
export default function Home({
settings,
themeData,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
const [useTeleporter] = useAtom(useTelepoterAtom);
return (
<Layout settings={settings} themeData={themeData}>
<Swapper />
</Layout>
);
}
export { getServerSideProps };
-24
View File
@@ -1,24 +0,0 @@
import Layout from '../components/layout'
import React from 'react'
import { useRouter } from 'next/router';
import BridgeAuthApiClient from '../lib/userAuthApiClient';
import { InferGetServerSidePropsType } from 'next';
import { BridgeAppSettings } from '../Models/BridgeAppSettings';
import { getServerSideProps } from '../helpers/getSettings';
import NoCookies from '../components/NoCookies';
export default function Salon({ settings }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<div className={`flex flex-col items-center min-h-screen overflow-hidden relative font-robo`}>
<div className="w-full max-w-lg z-[1]">
<div className="flex content-center items-center justify-center space-y-5 flex-col container mx-auto sm:px-6 max-w-lg">
<div className="flex flex-col w-full ">
<NoCookies />
</div>
</div>
</div>
</div>
)
}
export { getServerSideProps };
-64
View File
@@ -1,64 +0,0 @@
import Layout from "../components/layout";
import React, { useEffect } from "react";
import { useRouter } from "next/router";
import { clearTempData, getTempData } from "../lib/openLink";
import { InferGetServerSidePropsType } from "next";
import { getServerSideProps } from "../helpers/getSettings";
export default function Salon({
settings,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
const router = useRouter();
useEffect(() => {
const temp_data = getTempData();
if (!temp_data) return;
if (temp_data.swap_id) {
clearTempData();
router.push({
pathname: `/swap/${temp_data.swap_id}`,
query: { ...(temp_data?.query || {}), coinbase_redirect: true },
});
} else {
router.push({
pathname: "/",
query: { ...(temp_data?.query || {}), coinbase_redirect: true },
});
}
}, [router]);
return (
<Layout hideFooter={true} settings={settings}>
<div className="h-full min-h-screen flex flex-col justify-center text-md font-lighter leading-6">
<div className="flex place-content-center mb-4">
<svg
xmlns="http://www.w3.org/2000/svg"
width="140"
height="140"
viewBox="0 0 116 116"
fill="none"
>
<circle cx="58" cy="58" r="58" fill="#55B585" fillOpacity="0.1" />
<circle cx="58" cy="58" r="45" fill="#55B585" fillOpacity="0.3" />
<circle cx="58" cy="58" r="30" fill="#55B585" />
<path
d="M44.5781 57.245L53.7516 66.6843L70.6308 49.3159"
stroke="white"
strokeWidth="3.15789"
strokeLinecap="round"
/>
</svg>
</div>
<div className="flex flex-col text-center place-content-center space-y-2">
<p className="block font-bold text-lg">
{" "}
Exchange account successfully connected{" "}
</p>
<p className="block"> You can close this window now</p>
</div>
</div>
</Layout>
);
}
export { getServerSideProps };
-58
View File
@@ -1,58 +0,0 @@
import type { Metadata } from 'next'
import * as Sentry from "@sentry/nextjs"
export const metadata: Metadata = {
robots: {
index: false,
follow: false,
}
}
export default function Page() {
return (
<main
style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<button
type="button"
style={{
padding: "12px",
cursor: "pointer",
backgroundColor: "#AD6CAA",
borderRadius: "4px",
border: "none",
color: "white",
fontSize: "14px",
margin: "18px",
}}
onClick={async () => {
const transaction = Sentry.startTransaction({
name: "Example Frontend Transaction",
});
Sentry.configureScope((scope) => {
scope.setSpan(transaction);
});
try {
const res = await fetch("/api/sentry-example-api");
if (!res.ok) {
throw new Error("Sentry Example Frontend Error");
}
} finally {
transaction.finish();
}
}}
>
Throw error!
</button>
</main>
)
}
-64
View File
@@ -1,64 +0,0 @@
import BridgeApiClient from "@/lib/BridgeApiClient";
import Layout from "@/components/layout";
import { InferGetServerSidePropsType } from "next";
import React from "react";
import { SwapDataProvider } from "@/context/swap";
import { TimerProvider } from "@/context/timerContext";
import { getThemeData } from "@/helpers/settingsHelper";
import SwapWithdrawal from "@/components/SwapWithdrawal";
const SwapDetails = ({
settings,
themeData,
}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
return (
<>
<Layout settings={settings} themeData={themeData}>
<SwapDataProvider>
<TimerProvider>
<SwapWithdrawal />
</TimerProvider>
</SwapDataProvider>
</Layout>
<div>swap</div>
</>
);
};
export const getServerSideProps = async (ctx) => {
const params = ctx.params;
let isValidGuid =
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
params.swapId
);
if (!isValidGuid) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
const apiClient = new BridgeApiClient();
const { data: networkData } = await apiClient.GetLSNetworksAsync();
const { data: exchangeData } = await apiClient.GetExchangesAsync();
if (!networkData || !exchangeData) return;
const settings = {
networks: networkData,
exchanges: exchangeData,
};
const themeData = await getThemeData(ctx.query);
return {
props: {
settings,
themeData,
},
};
};
export default SwapDetails;
@@ -1,62 +0,0 @@
import BridgeApiClient from "@/lib/BridgeApiClient";
import Layout from "@/components/layout";
import { InferGetServerSidePropsType } from "next";
import React from "react";
import { SwapDataProvider } from "@/context/swap";
import { TimerProvider } from "@/context/timerContext";
import { getThemeData } from "@/helpers/settingsHelper";
import SwapWithdrawal from "@/components/SwapWithdrawal";
import SwapProcess from "@/components/lux/teleport/process";
const SwapDetails = ({
settings,
themeData,
swapId,
}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
return (
<>
<Layout settings={settings} themeData={themeData}>
<SwapProcess swapId={swapId} />
</Layout>
</>
);
};
export const getServerSideProps = async (ctx: any) => {
const params = ctx.params;
let isValidGuid =
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
params.swapId
);
if (!isValidGuid) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
const apiClient = new BridgeApiClient();
const { data: networkData } = await apiClient.GetLSNetworksAsync();
const { data: exchangeData } = await apiClient.GetExchangesAsync();
if (!networkData || !exchangeData) return;
const settings = {
networks: networkData,
exchanges: exchangeData,
};
const themeData = await getThemeData(ctx.query);
return {
props: {
settings,
themeData,
swapId: params.swapId,
},
};
};
export default SwapDetails;
-22
View File
@@ -1,22 +0,0 @@
import Layout from "../components/layout";
import TransfersWrapper from "../components/SwapHistory/TransfersWrapper";
import { InferGetServerSidePropsType } from "next";
import { SwapDataProvider } from "../context/swap";
import { getServerSideProps } from "../helpers/getSettings";
export default function Transactions({
settings,
themeData,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<>
<Layout settings={settings} themeData={themeData}>
<SwapDataProvider>
<TransfersWrapper />
</SwapDataProvider>
</Layout>
</>
);
}
export { getServerSideProps };
+3 -4
View File
@@ -60,6 +60,7 @@ model Currency {
source_currency Swap[] @relation("sourceAsset")
destination_currency Swap[] @relation("destinationAsset")
@@unique([network_id, asset]) // Composite unique constraint
@@index([network_id])
@@index([asset])
}
@@ -107,9 +108,6 @@ model Swap {
fail_reason String?
metadata_sequence_number Int?
// current block_number from the chain
block_number Int
deposit_address_id Int?
deposit_address DepositAddress? @relation(fields: [deposit_address_id], references: [id])
@@ -161,9 +159,10 @@ model Quote {
}
model DepositAddress {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
type String
address String
memo String?
Swap Swap[]
@@index([type])
@@ -1,4 +1,4 @@
import { ApiError } from "./ApiError";
import { type ApiError } from "./ApiError";
export class EmptyApiResponse {
constructor(error?: ApiError) {
@@ -1,6 +1,6 @@
import { Wallet } from "../stores/walletStore"
import { NetworkCurrency } from "./CryptoNetwork"
import { Layer } from "./Layer"
import { type Wallet } from "../stores/walletStore"
import { type NetworkCurrency } from "./CryptoNetwork"
import { type Layer } from "./Layer"
export type BalanceProps = {
layer: Layer,
@@ -1,8 +1,8 @@
import { CryptoNetwork, NetworkCurrency } from "./CryptoNetwork";
import { Exchange } from "./Exchange";
import { Layer } from "./Layer";
import { BridgeSettings, Route } from "@/Models/BridgeSettings";
import { Partner } from "./Partner";
import type { CryptoNetwork, NetworkCurrency } from "./CryptoNetwork";
import { type Exchange } from "./Exchange";
import { type Layer } from "./Layer";
import type { BridgeSettings, Route } from "@/Models/BridgeSettings";
import { type Partner } from "./Partner";
export class BridgeAppSettings {
constructor(settings: BridgeSettings | any) {
@@ -1,4 +1,4 @@
export class AuthGetCodeResponse {
type AuthGetCodeResponse = {
data: {
next: Date,
already_sent: boolean
@@ -6,10 +6,15 @@ export class AuthGetCodeResponse {
error: string;
}
export class AuthConnectResponse {
type AuthConnectResponse = {
access_token: string;
expires_in: number;
token_type: string;
refresh_token: string;
scope: string;
}
export {
type AuthGetCodeResponse,
type AuthConnectResponse
}
+14
View File
@@ -0,0 +1,14 @@
import { type CryptoNetwork } from './CryptoNetwork'
import { type Exchange } from './Exchange'
export type BridgeSettings = {
exchanges: Exchange[]
networks: CryptoNetwork[]
sources?: Route[]
destinations?: Route[]
}
export type Route = {
network: string
asset: string
}
@@ -9,7 +9,7 @@ export enum NetworkType {
Bitocoin = "btc"
}
export class CryptoNetwork {
export type CryptoNetwork = {
display_name: string;
internal_name: string;
transaction_explorer_template: string;
@@ -26,7 +26,7 @@ export class CryptoNetwork {
img_url?: string
}
export class NetworkCurrency {
export type NetworkCurrency = {
asset: string;
is_refuel_enabled: boolean;
is_native: boolean
@@ -38,13 +38,13 @@ export class NetworkCurrency {
availableInSource: boolean;
availableInDestination: boolean;
}
export class NetworkNode {
export type NetworkNode = {
url: string;
}
export class ManagedAccount {
export type ManagedAccount = {
address: `0x${string}`;
}
export class Metadata {
export type Metadata = {
multicall3?: {
address: `0x${string}`
blockCreated: number
@@ -1,4 +1,4 @@
export class CurrencyExchange {
export type CurrencyExchange = {
exchange_id: string;
fee: number;
is_off_ramp_enabled: boolean;
@@ -1,4 +1,4 @@
export class Exchange {
export type Exchange = {
display_name: string;
internal_name: string;
is_featured: boolean;
@@ -1,4 +1,4 @@
import { ManagedAccount, Metadata, NetworkCurrency, NetworkType } from "./CryptoNetwork";
import type { ManagedAccount, Metadata, NetworkCurrency, NetworkType } from "./CryptoNetwork";
export type LayerStatus = "active" | "inactive" | 'insufficient_liquidity';
export type Layer = {
@@ -1,4 +1,4 @@
export class Partner {
export type Partner = {
display_name: string;
logo_url: string;
is_wallet: boolean;

Some files were not shown because too many files have changed in this diff Show More