diff --git a/src/components/AdminSecurityCatalog/TokenPopup.tsx b/src/components/AdminSecurityCatalog/TokenPopup.tsx index e1e099418b..39da070bcd 100644 --- a/src/components/AdminSecurityCatalog/TokenPopup.tsx +++ b/src/components/AdminSecurityCatalog/TokenPopup.tsx @@ -107,7 +107,7 @@ export const TokenPopup: FC = ({ setTokenData, setIsOpenTokenForm }) => { } const onSelectNetwork = (value: any) => { - formik.setFieldTouched('tokenAddress', true) + formik.setFieldTouched('network', true) formik.setFieldValue('network', value) } diff --git a/src/components/Header/NetworkCard.tsx b/src/components/Header/NetworkCard.tsx index af4acff270..6790bc2c1f 100644 --- a/src/components/Header/NetworkCard.tsx +++ b/src/components/Header/NetworkCard.tsx @@ -12,10 +12,11 @@ import { ChevronElement } from 'components/ChevronElement' import { MEDIA_WIDTHS } from 'theme' import { ReactComponent as Checked } from 'assets/images/checked-blue.svg' import { VioletCard } from '../Card' +import { CHAINS } from 'components/Web3Provider/constants' export const NetworkCard = () => { const { chainId, provider, account } = useActiveWeb3React() - const { chains, isPending, switchChain } = useSwitchChain() + const { isPending, switchChain } = useSwitchChain() const node = useRef() const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR) @@ -57,7 +58,7 @@ export const NetworkCard = () => { {open && ( - {chains.map((chain: any) => { + {CHAINS.map((chain: any) => { const active = chainId === chain.id const targetChain = chain.id return ( diff --git a/src/components/Launchpad/InvestmentCard/RaisedFund.tsx b/src/components/Launchpad/InvestmentCard/RaisedFund.tsx index 93d8b28d25..ea9c4425c3 100644 --- a/src/components/Launchpad/InvestmentCard/RaisedFund.tsx +++ b/src/components/Launchpad/InvestmentCard/RaisedFund.tsx @@ -20,7 +20,12 @@ export const RaisedFund: React.FC = ({ totalInvestment, symbol }) => { Calculating ) : ( - + )} diff --git a/src/components/LaunchpadOffer/InvestDialog/content/Sale.tsx b/src/components/LaunchpadOffer/InvestDialog/content/Sale.tsx index 3d3a04590e..0e4a7e1334 100644 --- a/src/components/LaunchpadOffer/InvestDialog/content/Sale.tsx +++ b/src/components/LaunchpadOffer/InvestDialog/content/Sale.tsx @@ -33,6 +33,7 @@ import { FlexVerticalCenter } from 'components/LaunchpadMisc/styled' import { OfferStageStatus, getTokenSymbol } from 'components/LaunchpadOffer/OfferSidebar/OfferDetails' import { KYCPromptIconContainer } from 'components/Launchpad/KYCPrompt/styled' import { WalletEvent, INVEST_FLOW_EVENTS } from 'utils/event-logs' +import usePostbackJumpTask from 'hooks/usePostbackJumpTask' interface Props { offer: Offer @@ -64,6 +65,7 @@ export const SaleStage: React.FC = ({ offer, investedData, openSuccess, o const invest = useInvest(id) const getPresaleProof = usePresaleProof(id) const getInvestPublicSaleStructData = useInvestPublicSaleStructData(id) + const { callPostbackEndpoint } = usePostbackJumpTask() const [amount, setAmount] = useState() @@ -219,6 +221,7 @@ export const SaleStage: React.FC = ({ offer, investedData, openSuccess, o submitState.setSuccess() openSuccess() + callPostbackEndpoint() } } } diff --git a/src/components/NumericalInput/index.tsx b/src/components/NumericalInput/index.tsx index 7cc1d4c1b8..08d176ff2e 100644 --- a/src/components/NumericalInput/index.tsx +++ b/src/components/NumericalInput/index.tsx @@ -49,28 +49,28 @@ export const Input = React.memo(function InnerInput({ route={isActiveRoute} {...rest} value={prependSymbol && value ? prependSymbol + value : formatNumberValue(value)} - onChange={(event: { target: { value: string } }) => { - if (prependSymbol) { - const value = event.target.value + onChange={(event) => { + const inputValue = event.target.value; - // cut off prepended symbol - const formattedValue = value.toString().includes(prependSymbol) - ? value.toString().slice(1, value.toString().length + 1) - : value + if (prependSymbol) { + // Cut off prepended symbol + const formattedValue = inputValue.includes(prependSymbol) + ? inputValue.slice(prependSymbol.length) + : inputValue; - // replace commas with periods, because ixswap exclusively uses period as the decimal separator - enforcer(formattedValue.replace(/,/g, '')) + // Replace commas with periods, because ixswap exclusively uses period as the decimal separator + enforcer(formattedValue.replace(/,/g, '.')); } else { - enforcer(event.target.value.replace(/,/g, '')) + enforcer(inputValue.replace(/,/g, '.')); } }} - // universal input options + // Universal input options inputMode="decimal" autoComplete="off" autoCorrect="off" - // text-specific options + // Text-specific options type="text" - pattern="^[0-9]*[.,]?[0-9]*$" + pattern="[0-9]*[.]?[0-9]*" placeholder={placeholder || '0.00'} minLength={1} maxLength={maxLength} diff --git a/src/components/Vault/FeeStatus.tsx b/src/components/Vault/FeeStatus.tsx index 7911482f1c..0cabe73530 100644 --- a/src/components/Vault/FeeStatus.tsx +++ b/src/components/Vault/FeeStatus.tsx @@ -23,7 +23,7 @@ export const FeeStatus = ({ status, feePrice, estimatedPrice }: Props) => { } if (paid || feePrice) { - return `${+feePrice} ${native.symbol}` + return `${formatAmount(+feePrice)} ${native.symbol}` } return `~${estimatedPrice ? `${formatAmount(estimatedPrice)} ${native.symbol}` : ' - '}` }, [estimatedPrice, feePrice, paid]) diff --git a/src/components/Vault/WithdrawRequestForm.tsx b/src/components/Vault/WithdrawRequestForm.tsx index 96aa373f7a..2c0b840e21 100644 --- a/src/components/Vault/WithdrawRequestForm.tsx +++ b/src/components/Vault/WithdrawRequestForm.tsx @@ -230,7 +230,7 @@ export const WithdrawRequestForm = ({ currency, changeModal, token, onRedirect } disabled={!!inputError || loadingFee || loadingWithdraw} onClick={onClick} > - {loadingFee ? ( + {loadingFee || loadingWithdraw ? ( {`Sending Withdrawal Fee`} diff --git a/src/components/Web3Provider/wagmi.ts b/src/components/Web3Provider/wagmi.ts index d81c94e5fc..b88a380faa 100644 --- a/src/components/Web3Provider/wagmi.ts +++ b/src/components/Web3Provider/wagmi.ts @@ -2,7 +2,7 @@ import { QueryClient } from '@tanstack/react-query' import { getDefaultConfig } from '@rainbow-me/rainbowkit' import * as wallets from '@rainbow-me/rainbowkit/wallets' -import { CHAINS, transports } from './constants' +import { CHAINS, transports } from './constants' import walletConnectConfig from 'walletConnectConfig.json' import { tryClearIndexedDB } from 'utils' diff --git a/src/config/index.ts b/src/config/index.ts index 6fb63e25a6..6fb1f5bfb7 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -20,4 +20,4 @@ export const IXSALE_ADDRESS_BASE_SEPOLIA = process.env.REACT_APP_IXSALE_ADDRESS_ export const IXSALE_ADDRESS_POLYGON = process.env.REACT_APP_IXSALE_ADDRESS_POLYGON || '' export const IXSALE_ADDRESS_AMOY = process.env.REACT_APP_IXSALE_ADDRESS_AMOY || '' export const IXSALE_ADDRESS_MUMBAI = process.env.REACT_APP_IXSALE_ADDRESS_MUMBAI || '' -export const SUPPORTED_TGE_CHAINS = process.env.REACT_APP_SUPPORTED_TGE_CHAINS || [84532, 80002] +export const SUPPORTED_TGE_CHAINS = process.env.REACT_APP_SUPPORTED_TGE_CHAINS || [84532, 80002] \ No newline at end of file diff --git a/src/hooks/personalSign.ts b/src/hooks/personalSign.ts index 15ae5c6238..f058266955 100644 --- a/src/hooks/personalSign.ts +++ b/src/hooks/personalSign.ts @@ -26,6 +26,7 @@ export const useSignMessage = () => { if (e?.name === 'ConnectorChainMismatchError') { const defaultChain = ENV_SUPPORTED_TGE_CHAINS[0] await switchChain({ chainId: defaultChain }) + window.location.reload() } return null diff --git a/src/hooks/usePostbackJumpTask.ts b/src/hooks/usePostbackJumpTask.ts new file mode 100644 index 0000000000..94605e038e --- /dev/null +++ b/src/hooks/usePostbackJumpTask.ts @@ -0,0 +1,27 @@ +import { useDispatch } from 'react-redux' +import axios from 'axios' +import { useJumpTaskState } from 'state/jumpTask/hooks' +import { resetJumpTaskState } from 'state/jumpTask' + +const usePostbackJumpTask = () => { + const dispatch = useDispatch() + const { transactionId, affUnique1 } = useJumpTaskState() + + const callPostbackEndpoint = async () => { + const url = `https://jumptask.go2cloud.org/aff_lsr?transaction_id=${transactionId}&adv_sub=${affUnique1}` + + try { + const { status } = await axios.get(url) + + if (status === 200) { + dispatch(resetJumpTaskState()) + } + } catch (error) { + console.error('Error calling postback endpoint:', error) + } + } + + return { callPostbackEndpoint } +} + +export default usePostbackJumpTask diff --git a/src/hooks/useQuery.ts b/src/hooks/useQuery.ts new file mode 100644 index 0000000000..61adad6807 --- /dev/null +++ b/src/hooks/useQuery.ts @@ -0,0 +1,7 @@ +import { useLocation } from 'react-router-dom'; + +const useQuery = () => { + return new URLSearchParams(useLocation().search); +}; + +export default useQuery; \ No newline at end of file diff --git a/src/pages/App.tsx b/src/pages/App.tsx index 14831826a1..6ead207e8e 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -3,8 +3,6 @@ import { Redirect, RouteComponentProps, Route, Switch, useLocation } from 'react import styled from 'styled-components/macro' import { useDispatch } from 'react-redux' -import { ENV_SUPPORTED_TGE_CHAINS } from 'constants/addresses' - import { useActiveWeb3React } from 'hooks/web3' import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader' @@ -45,8 +43,11 @@ import { blockedCountries } from 'constants/countriesList' import Portal from '@reach/portal' import { CenteredFixed } from 'components/LaunchpadMisc/styled' import SignMessageModal from 'components/SignMessageModal' +import useQuery from 'hooks/useQuery' +import { setJumpTaskState } from 'state/jumpTask' +import { CHAINS } from 'components/Web3Provider/constants' -const chains = ENV_SUPPORTED_TGE_CHAINS || [42] +const chains = CHAINS ? CHAINS.map((chain) => chain.id) : [] const lbpAdminRoutes = [routes.lbpCreate, routes.lbpEdit, routes.lbpDashboard, routes.adminDetails] const initSafary = () => { @@ -76,23 +77,15 @@ export default function App() { const { kyc } = useKYCState() const { isConnected, walletName } = useWalletState() const { authenticate } = useAccount() - const isWhitelisted = isUserWhitelisted({ account, chainId }) + const query = useQuery() + const [countryCode, setCountryCode] = useState() + const transactionId = query.get('transaction_id') + const affUnique1 = query.get('aff_unique1') const isIxSwap = whiteLabelConfig?.isIxSwap ?? false const routeFinalConfig = isAdmin ? routeConfigs : routeConfigs.filter((route) => !lbpAdminRoutes.includes(route.path)) - useEffect(() => { - const getCountryCode = async () => { - const response = await axios.get(ip.getIPAddress) - setCountryCode(response?.data?.countryCode) - } - getCountryCode() - }, []) - - useEffect(() => { - initSafary() - }, []) const canAccessKycForm = (kycType: string) => { if (!account) return false @@ -124,6 +117,15 @@ export default function App() { [config] ) + const clearLocaleStorage = () => { + const cleared = localStorage.getItem('clearedLS-28-04-22') + if (!cleared) { + dispatch(clearStore()) + localStorage.clear() + localStorage.setItem('clearedLS-28-04-22', 'true') + } + } + const defaultPage = useMemo(() => { const defaultPath = [routes.launchpad, routes.issuance].includes(pathname) ? routes.launchpad : routes.kyc if (isAllowed({ path: routes.kyc }) && (kyc?.status !== KYCStatuses.APPROVED || !account)) { @@ -142,37 +144,6 @@ export default function App() { return (config?.pages ?? []).length > 0 ? config?.pages[0] : defaultPath }, [kyc, account, chainId, isWhitelisted, chains]) - useEffect(() => { - getMyKyc() - }, [account, token, getMyKyc]) - - const clearLocaleStorage = () => { - const cleared = localStorage.getItem('clearedLS-28-04-22') - if (!cleared) { - dispatch(clearStore()) - localStorage.clear() - localStorage.setItem('clearedLS-28-04-22', 'true') - } - } - - useEffect(() => { - if (token) { - getMe() - } - }, [token, getMe]) - - useEffect(() => { - clearLocaleStorage() - }, [isConnected, walletName]) - - useEffect(() => { - getWitelabelConfig() - }, []) - - useEffect(() => { - window.scrollTo(0, 0) - }, [pathname]) - const isAdminKyc = pathname.includes('admin') const isWhiteBackground = pathname === routes.launchpad || @@ -224,6 +195,49 @@ export default function App() { [isAllowed, canAccessKycForm, chainId, isWhitelisted, userRole, account] ) + useEffect(() => { + getMyKyc() + }, [account, token, getMyKyc]) + + useEffect(() => { + if (token) { + getMe() + } + }, [token, getMe]) + + useEffect(() => { + clearLocaleStorage() + }, [isConnected, walletName]) + + useEffect(() => { + getWitelabelConfig() + }, []) + + useEffect(() => { + window.scrollTo(0, 0) + }, [pathname]) + + useEffect(() => { + const getCountryCode = async () => { + const response = await axios.get(ip.getIPAddress) + setCountryCode(response?.data?.countryCode) + } + getCountryCode() + }, []) + + useEffect(() => { + initSafary() + }, []) + + useEffect(() => { + if (transactionId) { + dispatch(setJumpTaskState({ transactionId })) + } + if (affUnique1) { + dispatch(setJumpTaskState({ affUnique1 })) + } + }, [transactionId, affUnique1]) + if (!config) { return } diff --git a/src/pages/KYC/index.tsx b/src/pages/KYC/index.tsx index 0519f2cb52..c471947a0c 100644 --- a/src/pages/KYC/index.tsx +++ b/src/pages/KYC/index.tsx @@ -2,7 +2,7 @@ import React, { useCallback, FC, useEffect, useState, useMemo } from 'react' import { Trans } from '@lingui/macro' import { isMobile } from 'react-device-detect' import { Flex, Text } from 'rebass' -import { Link } from 'react-router-dom' +import { Link, useHistory } from 'react-router-dom' import dayjs from 'dayjs' import { useCookies } from 'react-cookie' import _get from 'lodash/get' @@ -98,6 +98,7 @@ const KYC = () => { const description = useMemo(() => kyc?.message || getStatusDescription(status), [kyc, status]) const [referralCode, setReferralCode] = useState('') const getMe = useGetMe() + const history = useHistory() const supportEmail = _get(config, 'supportEmail', 'c@ixswap.io') @@ -160,7 +161,7 @@ const KYC = () => { sx={{ gap: '1rem', marginTop: '40px' }} > openModal('individual')} + onClick={() => history.push(getKYCLink())} sx={{ border: '1px solid #E6E6FF', marginBottom: isMobile ? '32px' : '0px', @@ -183,11 +184,9 @@ const KYC = () => { > Pass KYC as Individual - - - Start Now - - + + Start Now + diff --git a/src/pages/Launchpad/index.tsx b/src/pages/Launchpad/index.tsx index 3016a39948..c1b19cdb37 100644 --- a/src/pages/Launchpad/index.tsx +++ b/src/pages/Launchpad/index.tsx @@ -1,9 +1,15 @@ import React from 'react' import styled from 'styled-components' +import Portal from '@reach/portal' + import { Offers } from 'components/Launchpad/Offers' import { useWhitelabelState } from 'state/whitelabel/hooks' import { Banner } from './Banner' import { MEDIA_WIDTHS } from 'theme' +import { CHAINS } from 'components/Web3Provider/constants' +import { useActiveWeb3React } from 'hooks/web3' +import { NotAvailablePage } from 'components/NotAvailablePage' +import { CenteredFixed } from 'components/LaunchpadMisc/styled' const BannerWrapper = styled.div` background-color: #ffffff; @@ -23,9 +29,11 @@ const BannerWrapper = styled.div` export default function Launchpad() { const { config } = useWhitelabelState() + const { chainId, account } = useActiveWeb3React() const isIxSwap = config?.isIxSwap ?? false const enableLaunchpadBanner = config?.enableLaunchpadBanner ?? false + const chains = CHAINS ? CHAINS.map((chain) => chain.id) : [] return ( <> @@ -35,6 +43,14 @@ export default function Launchpad() { ) : null} + + {account && !chains.includes(chainId) ? ( + + + + + + ) : null} ) } diff --git a/src/state/auth/hooks.ts b/src/state/auth/hooks.ts index cd5413ff0e..1f35acc7b9 100644 --- a/src/state/auth/hooks.ts +++ b/src/state/auth/hooks.ts @@ -8,11 +8,9 @@ import { metamask } from 'services/apiUrls' import { AppDispatch, AppState } from 'state' import { clearEventLog } from 'state/eventLog/actions' import { clearUserData, saveAccount } from 'state/user/actions' -import { logout, postLogin } from './actions' -import { useConnections, useDisconnect } from 'wagmi' +import { postLogin } from './actions' +import { useDisconnect } from 'wagmi' import { setWalletState } from 'state/wallet' -import { useHistory } from 'react-router-dom' -import { routes } from 'utils/routes' import { tryClearIndexedDB } from 'utils' export enum LOGIN_STATUS { @@ -60,12 +58,9 @@ export function useUserisLoggedIn() { export function useLogout() { const dispatch = useDispatch() const { disconnect } = useDisconnect() - const connections = useConnections() const disconnectWallet = () => { - connections.forEach(({ connector }) => { - disconnect({ connector }) - }) + disconnect(); dispatch(setWalletState({ isConnected: false, walletName: '', isSignLoading: false })) dispatch(clearUserData()) dispatch(clearEventLog()) diff --git a/src/state/index.ts b/src/state/index.ts index 1c3f55bc5c..f2857c6236 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -38,6 +38,7 @@ import tokenManager from './token-manager/reducer' import issuance from './issuance/reducer' import wallet from './wallet' import global from './global' +import jumpTask from './jumpTask' const PERSISTED_KEYS: string[] = ['auth', 'lists', 'swap', 'swapHelper', 'transactions', 'user', 'wallet', 'global'] @@ -82,6 +83,7 @@ const combinedReducer = combineReducers({ issuance, wallet, global, + jumpTask }) const rootReducer = (state: any, action: any) => { diff --git a/src/state/jumpTask/hooks.ts b/src/state/jumpTask/hooks.ts new file mode 100644 index 0000000000..c3cdc6812c --- /dev/null +++ b/src/state/jumpTask/hooks.ts @@ -0,0 +1,8 @@ +import { useSelector } from 'react-redux' +import { AppState } from 'state' + +export const useJumpTaskState = () => { + const walletState = useSelector((state: AppState) => state.jumpTask) + + return walletState +} diff --git a/src/state/jumpTask/index.ts b/src/state/jumpTask/index.ts new file mode 100644 index 0000000000..1732f11476 --- /dev/null +++ b/src/state/jumpTask/index.ts @@ -0,0 +1,27 @@ +import { createSlice } from '@reduxjs/toolkit' + +export interface JumpTaskState { + transactionId: string + affUnique1: string +} + +const initialState: JumpTaskState = { + transactionId: '', + affUnique1: '', +} + +const jumpTaskSlice = createSlice({ + name: 'jumpTask', + initialState, + reducers: { + setJumpTaskState(state, action) { + const newState = { ...state, ...action.payload } + + return newState + }, + resetJumpTaskState: () => initialState, + }, +}) + +export const { setJumpTaskState, resetJumpTaskState } = jumpTaskSlice.actions +export default jumpTaskSlice.reducer