diff --git a/.gitignore b/.gitignore
index f9752eb5..6ddd0081 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.idea
.vscode
.eslintcache
+.env
# dependencies
/node_modules
diff --git a/public/static/exchange-icons/svg/component.svg b/public/static/exchange-icons/svg/component.svg
new file mode 100644
index 00000000..3bbf8717
--- /dev/null
+++ b/public/static/exchange-icons/svg/component.svg
@@ -0,0 +1,33 @@
+
+
+
diff --git a/public/static/exchange-icons/svg/maker.svg b/public/static/exchange-icons/svg/maker.svg
new file mode 100644
index 00000000..38b10316
--- /dev/null
+++ b/public/static/exchange-icons/svg/maker.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/static/exchange-icons/svg/saddle.svg b/public/static/exchange-icons/svg/saddle.svg
new file mode 100644
index 00000000..9bb30edf
--- /dev/null
+++ b/public/static/exchange-icons/svg/saddle.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/static/exchange-icons/svg/smoothy.svg b/public/static/exchange-icons/svg/smoothy.svg
new file mode 100644
index 00000000..2a054d80
--- /dev/null
+++ b/public/static/exchange-icons/svg/smoothy.svg
@@ -0,0 +1,9 @@
+
diff --git a/public/static/exchange-icons/svg/xsigma.svg b/public/static/exchange-icons/svg/xsigma.svg
new file mode 100644
index 00000000..864bf535
--- /dev/null
+++ b/public/static/exchange-icons/svg/xsigma.svg
@@ -0,0 +1,15 @@
+
diff --git a/public/static/okcoin_icon.png b/public/static/okcoin_icon.png
deleted file mode 100644
index 55929135..00000000
Binary files a/public/static/okcoin_icon.png and /dev/null differ
diff --git a/src/components/AccountModal.js b/src/components/AccountModal/index.js
similarity index 88%
rename from src/components/AccountModal.js
rename to src/components/AccountModal/index.js
index b8a52025..9a55202c 100644
--- a/src/components/AccountModal.js
+++ b/src/components/AccountModal/index.js
@@ -23,7 +23,7 @@ export default function AccountModal({ address, onboard }) {
return (
<>
@@ -36,10 +36,10 @@ export default function AccountModal({ address, onboard }) {
- Connected Wallet: {`${walletState.wallet.name}`}
+ Connected Wallet: {walletState.wallet.name}
- {`${address?.substr(0, 8)}...${address?.substr(address.length - 6)}`}
+ {address?.substr(0, 8)}...{address?.substr(address.length - 6)}
- {watchTokenIn && watchTokenOut && watchAmountIn && price ? (
+ {watchTokenIn && watchTokenOut && watchAmountIn > 0 && price ? (
{onboardState.address ? (
-
- {errors.amountIn ? 'Input Amount required' : 'Swap Tokens'}
-
+ <>
+
+ {errors.amountIn ? 'Input Amount required' : 'Swap Tokens'}
+
+
+ >
) : (
- readyToTransact()}
- >
- Connect Wallet
-
+
)}
- {/* {errors.amountIn ? 'Input amount is required' : null} */}
);
diff --git a/src/components/SwapModal.js b/src/components/SwapModal.js
new file mode 100644
index 00000000..8aeff8fa
--- /dev/null
+++ b/src/components/SwapModal.js
@@ -0,0 +1,116 @@
+/* eslint-disable */
+import {
+ Box,
+ Button,
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalFooter,
+ ModalBody,
+ ModalCloseButton,
+ useClipboard,
+ Text,
+ Flex,
+} from '@chakra-ui/react';
+import SwapInfo from './SwapForm/SwapInfo';
+import React from 'react';
+
+export default function SwapModal({
+ address,
+ onboard,
+ errors,
+ isOpen,
+ onClose,
+ setSwapConfirmed,
+ watchAmountIn,
+ watchTokenIn,
+ watchTokenOut,
+ price,
+ defaults,
+ exchanges,
+ gasPrice,
+ estimatedGas,
+ getPicture,
+}) {
+ const walletState = onboard.getState();
+ const { onCopy } = useClipboard(address);
+
+ return (
+ <>
+
+
+
+
+ Confirm Swap
+
+
+
+
+
+
+
+
+ {watchAmountIn}
+
+
+
+ {watchTokenIn.value}
+
+
+
+
+
+
+ {price}
+
+
+
+ {watchTokenOut.value}
+
+
+
+
+
+
+ {
+ onClose();
+ setSwapConfirmed(true);
+ }}
+ >
+ {errors.amountIn ? 'Input Amount required' : 'Confirm Swap'}
+
+
+
+
+ >
+ );
+}
diff --git a/src/constants/exchanges.js b/src/constants/exchanges.js
index 2d8d5bcf..f8ecf7c6 100644
--- a/src/constants/exchanges.js
+++ b/src/constants/exchanges.js
@@ -8,6 +8,10 @@ export default {
name: 'Balancer',
iconSVG: '/static/exchange-icons/svg/balancer.svg',
},
+ Balancer_V2: {
+ name: 'Balancer V2',
+ iconSVG: '/static/exchange-icons/svg/balancer.svg',
+ },
Bancor: {
name: 'Bancor',
iconSVG: '/static/exchange-icons/svg/bancor.svg',
@@ -16,6 +20,10 @@ export default {
name: 'Cream',
iconSVG: '/static/exchange-icons/svg/cream.svg',
},
+ Component: {
+ name: 'Component',
+ iconSVG: '/static/exchange-icons/svg/component.svg',
+ },
CryptoCom: {
name: 'DeFi Swap',
iconSVG: '/static/exchange-icons/svg/defiswap.svg',
@@ -40,6 +48,10 @@ export default {
name: 'Kyber',
iconSVG: '/static/exchange-icons/svg/kyber.svg',
},
+ KyberDMM: {
+ name: 'Kyber DMM',
+ iconSVG: '/static/exchange-icons/svg/kyber.svg',
+ },
Linkswap: {
name: 'Linkswap',
iconSVG: '/static/exchange-icons/svg/linkswap.svg',
@@ -48,6 +60,10 @@ export default {
name: '0x Private Market Maker',
iconSVG: '/static/exchange-icons/svg/0x.svg',
},
+ MakerPsm: {
+ name: 'Maker PSM',
+ iconSVG: '/static/exchange-icons/svg/maker.svg',
+ },
Mooniswap: {
name: 'Mooniswap',
iconSVG: '/static/exchange-icons/svg/mooniswap.svg',
@@ -60,10 +76,18 @@ export default {
name: '0x MultiHop',
iconSVG: '/static/exchange-icons/svg/0x.svg',
},
+ Saddle: {
+ name: 'Saddle',
+ iconSVG: '/static/exchange-icons/svg/saddle.svg',
+ },
Shell: {
name: 'Shell',
iconSVG: '/static/exchange-icons/svg/shell.svg',
},
+ Smoothy: {
+ name: 'Smoothy',
+ iconSVG: '/static/exchange-icons/svg/smoothy.svg',
+ },
SnowSwap: {
name: 'Snowswap',
iconSVG: '/static/exchange-icons/svg/snowswap.svg',
@@ -84,33 +108,48 @@ export default {
name: 'Uniswap V2',
iconSVG: '/static/exchange-icons/svg/uniswap.svg',
},
+ Uniswap_V3: {
+ name: 'Uniswap V3',
+ iconSVG: '/static/exchange-icons/svg/uniswap.svg',
+ },
mStable: {
name: 'mStable',
iconSVG: '/static/exchange-icons/svg/mstable.svg',
},
+ xSigma: {
+ name: 'xSigma',
+ iconSVG: '/static/exchange-icons/svg/xsigma',
+ },
},
exchanges: [
'0x',
'Balancer',
+ 'Balancer_V2',
'Bancor',
'CREAM',
+ 'Component',
'CryptoCom',
'Curve',
'DODO',
'DODO_V2',
'Eth2Dai',
'Kyber',
+ 'KyberDMM',
'Linkswap',
'LiquidityProvider',
+ 'MakerPsm',
'Mooniswap',
- 'MultiBridge',
'MultiHop',
+ 'Saddle',
'Shell',
+ 'Smoothy',
'SnowSwap',
'SushiSwap',
'Swerve',
'Uniswap',
'Uniswap_V2',
+ 'Uniswap_V3',
'mStable',
+ 'xSigma',
],
};
diff --git a/src/constants/toasts.js b/src/constants/toasts.js
index 51acf7ce..974a353d 100644
--- a/src/constants/toasts.js
+++ b/src/constants/toasts.js
@@ -1,8 +1,9 @@
export default {
success: {
title: 'Swap Success',
- description: 'Your swap was successfully executed',
+ description: 'Your swap was successfully executed.',
status: 'success',
+ position: 'bottom-left',
duration: 9000,
isClosable: true,
},
@@ -10,6 +11,23 @@ export default {
title: 'Swap Error',
description: 'There was an error while executing your swap, check the console',
status: 'error',
+ position: 'bottom-left',
+ duration: 9000,
+ isClosable: true,
+ },
+ transactionReject: {
+ title: 'Swap Error',
+ description: 'Transaction rejected.',
+ status: 'error',
+ position: 'bottom-left',
+ duration: 9000,
+ isClosable: true,
+ },
+ apiError: {
+ title: 'Swap Error',
+ description: 'API timed out while requesting data.',
+ status: 'error',
+ position: 'bottom-left',
duration: 9000,
isClosable: true,
},
diff --git a/src/hooks/use0xPrice.js b/src/hooks/use0xPrice.js
index 76653177..a46eeef1 100644
--- a/src/hooks/use0xPrice.js
+++ b/src/hooks/use0xPrice.js
@@ -3,43 +3,67 @@ import axios from 'axios';
import BD from 'js-big-decimal';
import Web3 from 'web3';
-const getPrice = async (tokenIn, tokenOut, sellAmount) => {
- if (!tokenIn || !tokenOut || !sellAmount) {
+function handleError(err) {
+ // Default message
+ let reason = 'Server error';
+
+ // Input out of range
+ if (err.message && err.message === 'Cannot divide by 0') {
+ reason = 'Input too large';
+ }
+
+ // Validation Error
+ if (err.response && err.response.data.code === 100) {
+ reason = err.response.data.validationErrors[0].reason;
+ reason = reason.charAt(0) + reason.slice(1).toLowerCase().replaceAll('_', ' ');
+ }
+
+ return reason;
+}
+
+async function getPrice(tokenIn, tokenOut, sellAmount) {
+ if (!tokenIn || !tokenOut || !sellAmount || sellAmount <= 0) {
return [];
}
- const conversionRate = new BD(`1.0e${tokenIn.decimals}`);
- const converted = new BD(sellAmount).multiply(conversionRate);
-
- const params = new URLSearchParams({
- sellToken: tokenIn.symbol,
- buyToken: tokenOut.symbol,
- sellAmount: converted.getValue(),
- });
-
- const { data } = await axios.get(
- `${
- process.env.REACT_APP_ENV === 'production'
- ? process.env.REACT_APP_ZEROEX_PROD
- : process.env.REACT_APP_ZEROEX_DEV
- }/swap/v1/price?${params.toString()}`
- );
- const { price, gasPrice, estimatedGas, sources } = data;
- const getDex = data.sources.filter((item) => item.proportion !== '0');
- const inverse = new BD(1).divide(new BD(data.price));
-
- return {
- exchanges: getDex,
- sources: sources.filter((source) => source.proportion !== '0'),
- price,
- inverse: inverse.getValue(),
- gasPrice: Web3.utils.fromWei(gasPrice, 'Gwei'),
- estimatedGas: new BD(estimatedGas).getPrettyValue(),
- };
-};
-
-export default function use0xPrice(tokenIn, tokenOut, sellAmount) {
- return useQuery(['price', '0x', tokenIn, tokenOut, sellAmount], () =>
- getPrice(tokenIn, tokenOut, sellAmount)
+ try {
+ const conversionRate = new BD(`1.0e${tokenIn.decimals}`);
+ const converted = new BD(sellAmount).multiply(conversionRate);
+
+ const params = new URLSearchParams({
+ sellToken: tokenIn.symbol,
+ buyToken: tokenOut.symbol,
+ sellAmount: converted.getValue(),
+ });
+
+ const { data } = await axios.get(
+ `${
+ process.env.REACT_APP_ENV === 'production'
+ ? process.env.REACT_APP_ZEROEX_PROD
+ : process.env.REACT_APP_ZEROEX_DEV
+ }/swap/v1/price?${params.toString()}`
+ );
+ const { price, gasPrice, estimatedGas, sources } = data;
+ const getDex = data.sources.filter((item) => item.proportion !== '0');
+ const inverse = new BD(1).divide(new BD(data.price));
+
+ return {
+ exchanges: getDex,
+ sources: sources.filter((source) => source.proportion !== '0'),
+ price,
+ inverse: inverse.getValue(),
+ gasPrice: Web3.utils.fromWei(gasPrice, 'Gwei'),
+ estimatedGas: new BD(estimatedGas).getPrettyValue(),
+ };
+ } catch (err) {
+ return { apiError: handleError(err) };
+ }
+}
+
+export default function use0xPrice(tokenIn, tokenOut, sellAmount, onError) {
+ return useQuery(
+ ['price', '0x', tokenIn, tokenOut, sellAmount],
+ () => getPrice(tokenIn, tokenOut, sellAmount),
+ { onError, retry: 3, retryDelay: (attempt) => attempt * 100 }
);
}
diff --git a/src/utils/getTokenIcon.js b/src/utils/getTokenIcon.js
index 30d096d1..5e8ae05e 100644
--- a/src/utils/getTokenIcon.js
+++ b/src/utils/getTokenIcon.js
@@ -5,7 +5,7 @@ const iconDirectory = '/static/token-icons';
* @param tokenSymbol The symbol for the desired token
*/
export function getTokenIconPNG32(tokenSymbol) {
- const symbol = tokenSymbol.toLowerCase();
+ const symbol = tokenSymbol?.toLowerCase();
return `${iconDirectory}/32/${symbol}.png`;
}