├── jest.setup.ts ├── .gitattributes ├── public ├── robots.txt ├── favicon.ico ├── static │ └── images │ │ ├── AppLogo.png │ │ ├── DeiLogoFull.png │ │ ├── fallback │ │ ├── loader.gif │ │ └── not_found.png │ │ ├── wallets │ │ ├── metamask.png │ │ ├── walletConnect.png │ │ ├── coinbaseWalletIcon.png │ │ └── injected.svg │ │ ├── pages │ │ ├── dashboard │ │ │ └── DEI_Dashboard.png │ │ ├── common │ │ │ └── down.svg │ │ └── clqdr │ │ │ └── ic_clqdr.svg │ │ ├── networks │ │ ├── heco.svg │ │ ├── binance.svg │ │ ├── fantom.svg │ │ ├── polygon.svg │ │ ├── xdai.svg │ │ └── mainnet.svg │ │ ├── tokens │ │ ├── solid.svg │ │ ├── clqdr.svg │ │ ├── lqdr.svg │ │ ├── deus.svg │ │ └── usdc.svg │ │ ├── footer │ │ ├── Telegram.svg │ │ ├── GitBook.svg │ │ ├── Twitter.svg │ │ ├── Github.svg │ │ └── Discord.svg │ │ ├── DeiText.svg │ │ └── LegacyDeiLogo.svg ├── images │ ├── 192x192_App_Icon.png │ └── 512x512_App_Icon.png └── manifest.json ├── .babelrc ├── src ├── constants │ ├── popup.ts │ ├── files │ │ └── routes.json │ ├── vest.ts │ ├── providers.ts │ ├── abi │ │ ├── ERC20.ts │ │ └── ERC20_BYTES32.json │ ├── misc.ts │ ├── chains.ts │ ├── wallet.ts │ ├── stakings.ts │ └── path.ts ├── components │ ├── StableCoin │ │ ├── index.tsx │ │ └── Navigation │ │ │ └── index.tsx │ ├── Title │ │ └── index.tsx │ ├── LiveChat │ │ └── index.tsx │ ├── Partition │ │ └── index.tsx │ ├── App │ │ ├── Bond │ │ │ ├── index.tsx │ │ │ └── Search.tsx │ │ ├── Vest │ │ │ ├── index.tsx │ │ │ ├── GeneralLockInformation.tsx │ │ │ └── Search.tsx │ │ ├── StableCoin │ │ │ ├── Tableau.tsx │ │ │ ├── InfoItem.tsx │ │ │ ├── PoolStats.tsx │ │ │ └── Search.tsx │ │ ├── CLqdr │ │ │ ├── Tableau.tsx │ │ │ ├── index.tsx │ │ │ ├── FirebirdBox2.tsx │ │ │ ├── FirebirdBox1.tsx │ │ │ └── FirebirdBox3.tsx │ │ └── Dashboard │ │ │ └── Chart.tsx │ ├── Icons │ │ ├── Connected.tsx │ │ ├── Network.tsx │ │ ├── Info.tsx │ │ ├── Search.tsx │ │ ├── Close.tsx │ │ ├── Gift.tsx │ │ ├── LottieDei.tsx │ │ ├── GreenCircle.tsx │ │ ├── ArrowDownDark.tsx │ │ ├── Droplet.tsx │ │ ├── Markets.tsx │ │ ├── NavToggle.tsx │ │ ├── ThemeToggle.tsx │ │ ├── Link.tsx │ │ ├── Lock.tsx │ │ ├── CreditCard.tsx │ │ ├── ArrowBubble.tsx │ │ ├── Settings.tsx │ │ ├── Portfolio.tsx │ │ ├── VeDeus.tsx │ │ ├── CheckMark.tsx │ │ ├── Confirmation.tsx │ │ ├── Analytics.tsx │ │ ├── Chevron.tsx │ │ ├── Dashboard.tsx │ │ ├── DeiBonds.tsx │ │ ├── ArrowDownLight.tsx │ │ ├── Loader.tsx │ │ ├── DotFlashing.tsx │ │ ├── Wallet.tsx │ │ ├── Copy.tsx │ │ ├── Redeem.tsx │ │ ├── Error.tsx │ │ ├── Mint.tsx │ │ ├── Trade.tsx │ │ ├── QuestionMark.tsx │ │ └── index.tsx │ ├── Layout │ │ ├── Warning.tsx │ │ ├── NavLogo.tsx │ │ └── index.tsx │ ├── DualImage │ │ └── index.tsx │ ├── Card │ │ └── index.tsx │ ├── Box │ │ └── index.tsx │ ├── Warning │ │ └── index.tsx │ ├── Web3ProviderNetwork │ │ └── index.tsx │ ├── ToolTip │ │ └── index.tsx │ ├── Tab │ │ └── index.tsx │ ├── Column │ │ └── index.tsx │ ├── Hero │ │ └── index.tsx │ ├── ReviewModal │ │ ├── ModalInfo.tsx │ │ ├── ThemeSelector.tsx │ │ ├── index.tsx │ │ └── TokenBox.tsx │ ├── Copy │ │ └── index.tsx │ ├── ImageWithFallback │ │ └── index.tsx │ ├── Row │ │ └── index.tsx │ ├── Web3ReactManager │ │ └── index.tsx │ ├── Web3Network │ │ └── index.tsx │ ├── analytics │ │ └── GoogleAnalyticsProvider.tsx │ ├── InfoHeader │ │ └── index.tsx │ ├── Popups │ │ ├── index.tsx │ │ └── PopupItem.tsx │ ├── InvalidContext │ │ └── index.tsx │ ├── Pagination │ │ └── index.tsx │ ├── AccountDetails │ │ └── Transaction.tsx │ └── Input │ │ └── index.tsx ├── pages │ ├── dei-incident-old │ │ └── index.tsx │ ├── dei-incident │ │ └── index.tsx │ ├── 404.tsx │ ├── _app.tsx │ └── _document.tsx ├── apollo │ └── client │ │ ├── health.ts │ │ ├── index.ts │ │ ├── solidly.ts │ │ ├── vdeus.ts │ │ ├── deiPriceStats.ts │ │ ├── ecosystemStats.ts │ │ └── totalPayment.ts ├── state │ ├── wallet │ │ └── types.ts │ ├── user │ │ ├── actions.ts │ │ ├── updater.tsx │ │ ├── reducer.ts │ │ └── hooks.ts │ ├── reducer.ts │ ├── updaters.tsx │ ├── dashboard │ │ ├── updater.ts │ │ └── hooks.ts │ ├── multicall │ │ ├── utils.ts │ │ └── actions.ts │ ├── application │ │ └── actions.ts │ ├── transactions │ │ └── actions.ts │ └── mint │ │ └── reducer.ts ├── lib │ └── muon │ │ ├── config.ts │ │ ├── error.ts │ │ ├── types.ts │ │ └── client │ │ └── base.ts ├── utils │ ├── hex.ts │ ├── account.ts │ ├── http.ts │ ├── prices.ts │ ├── validate.ts │ ├── queue.ts │ ├── numbers.test.ts │ ├── library.ts │ ├── currency.ts │ ├── parse.ts │ ├── address.ts │ ├── explorers.ts │ ├── parseError.ts │ ├── array.ts │ └── time.ts ├── hooks │ ├── useInjectedAddress.ts │ ├── useTokenList.ts │ ├── usePrevious.ts │ ├── useHover.ts │ ├── useOnOutsideClick.ts │ ├── useCopyClipboard.ts │ ├── useSupportedChainId.ts │ ├── useDebounce.ts │ ├── useWindowSize.ts │ ├── useIsWindowVisible.ts │ ├── useDistRewards.ts │ ├── useERC20Allowance.ts │ ├── useAddTokenToMetaMask.ts │ ├── useEventListener.ts │ ├── useCurrencyLogo.ts │ ├── useRpcChangerCallback.ts │ └── useClqdrCallback.ts ├── connectors │ └── index.ts └── theme │ └── styled.d.ts ├── .eslintignore ├── .prettierrc.json ├── README.md ├── next-env.d.ts ├── custom.d.ts ├── next.config.js ├── tsconfig.json ├── jest.config.js ├── .eslintrc.json └── .gitignore /jest.setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [["styled-components", { "ssr": true }]] 4 | } -------------------------------------------------------------------------------- /src/constants/popup.ts: -------------------------------------------------------------------------------- 1 | export const REMOVE_AFTER_MS = 10000 2 | export const L2_REMOVE_AFTER_MS = 7000 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ 3 | src/assets 4 | .next/ 5 | .eslintrc.js 6 | next.config.js 7 | 8 | -------------------------------------------------------------------------------- /src/components/StableCoin/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Navigation, NavigationTypes } from './Navigation' 2 | -------------------------------------------------------------------------------- /public/static/images/AppLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/AppLogo.png -------------------------------------------------------------------------------- /public/images/192x192_App_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/images/192x192_App_Icon.png -------------------------------------------------------------------------------- /public/images/512x512_App_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/images/512x512_App_Icon.png -------------------------------------------------------------------------------- /public/static/images/DeiLogoFull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/DeiLogoFull.png -------------------------------------------------------------------------------- /public/static/images/fallback/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/fallback/loader.gif -------------------------------------------------------------------------------- /public/static/images/wallets/metamask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/wallets/metamask.png -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "printWidth": 120 7 | } 8 | -------------------------------------------------------------------------------- /public/static/images/fallback/not_found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/fallback/not_found.png -------------------------------------------------------------------------------- /public/static/images/wallets/walletConnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/wallets/walletConnect.png -------------------------------------------------------------------------------- /public/static/images/wallets/coinbaseWalletIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/wallets/coinbaseWalletIcon.png -------------------------------------------------------------------------------- /public/static/images/pages/dashboard/DEI_Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deusfinance/dei-interface-v2/HEAD/public/static/images/pages/dashboard/DEI_Dashboard.png -------------------------------------------------------------------------------- /src/pages/dei-incident-old/index.tsx: -------------------------------------------------------------------------------- 1 | import Incident from 'components/App/Incident' 2 | 3 | export default function IncidentPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Title/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const CardTitle = styled.div` 4 | font-size: 1.2rem; 5 | margin-bottom: 10px; 6 | ` 7 | -------------------------------------------------------------------------------- /src/pages/dei-incident/index.tsx: -------------------------------------------------------------------------------- 1 | import ShutDownIncident from 'components/App/Incident/ShutDownIncident' 2 | 3 | export default function IncidentPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /src/apollo/client/health.ts: -------------------------------------------------------------------------------- 1 | import { createApolloClient } from './index' 2 | 3 | export function getApolloClient() { 4 | return createApolloClient('https://api.thegraph.com/index-node/graphql') 5 | } 6 | -------------------------------------------------------------------------------- /src/components/LiveChat/index.tsx: -------------------------------------------------------------------------------- 1 | import { LiveChatWidget } from '@livechat/widget-react' 2 | 3 | export default function LiveChat() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /src/state/wallet/types.ts: -------------------------------------------------------------------------------- 1 | import { CurrencyAmount, Token } from '@sushiswap/core-sdk' 2 | 3 | type TokenAddress = string 4 | 5 | export type TokenBalancesMap = Record> 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEI Finance Interface V2 2 | 3 | ### Setup 4 | `yarn install` 5 | 6 | ### Run 7 | We don't require any env variables in order to run the app. Simply run `npm run dev` and open your app on localhost:3000! 8 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /src/constants/files/routes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "text": "Dashboard", 4 | "path": "/dashboard", 5 | "exact": true 6 | }, 7 | { 8 | "text": "Incident", 9 | "path": "/dei-incident", 10 | "exact": true 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /src/lib/muon/config.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | 3 | export const MUON_BASE_URL = 'https://node-balancer.muon.net/v1' 4 | 5 | export const MUON_NETWORK_NAMES: { [chainId: number]: string } = { 6 | [SupportedChainId.FANTOM]: 'fantom', 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Partition/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const HorPartition = styled.div<{ 4 | color?: string 5 | }>` 6 | width: 100%; 7 | height: 0px; 8 | border-bottom: 1px solid ${({ theme, color }) => color ?? theme.border2}; 9 | ` 10 | -------------------------------------------------------------------------------- /src/components/App/Bond/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as NFTBox } from './NFTBox' 2 | export { default as NFTsModal } from './NFTsModal' 3 | export { default as Table } from './Table' 4 | export { useSearch, SearchField } from './Search' 5 | export { default as ReviewModal } from './ReviewModal' 6 | -------------------------------------------------------------------------------- /src/constants/vest.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '@sushiswap/core-sdk' 2 | import { SupportedChainId } from 'constants/chains' 3 | 4 | export const DEUS_TOKEN = new Token( 5 | SupportedChainId.FANTOM, 6 | '0xDE5ed76E7c05eC5e4572CfC88d1ACEA165109E44', 7 | 18, 8 | 'DEUS', 9 | 'DEUS' 10 | ) 11 | -------------------------------------------------------------------------------- /src/utils/hex.ts: -------------------------------------------------------------------------------- 1 | import { BigintIsh, JSBI } from '@sushiswap/core-sdk' 2 | 3 | export function toHex(bigintIsh: BigintIsh) { 4 | const bigInt = JSBI.BigInt(bigintIsh) 5 | let hex = bigInt.toString(16) 6 | if (hex.length % 2 !== 0) { 7 | hex = `0${hex}` 8 | } 9 | return `0x${hex}` 10 | } 11 | -------------------------------------------------------------------------------- /public/static/images/wallets/injected.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/Icons/Connected.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Connected({ size = 8, ...rest }: { size?: number; [x: string]: any }) { 4 | return ( 5 | 6 | 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/App/Vest/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as InputDate, SelectDatePresets } from './InputDate' 2 | export { default as GeneralLockInformation } from './GeneralLockInformation' 3 | export { default as UserLockInformation } from './UserLockInformation' 4 | export { default as Table } from './Table' 5 | export { useSearch, SearchField } from './Search' 6 | -------------------------------------------------------------------------------- /src/components/Icons/Network.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Activity } from 'react-feather' 3 | 4 | export const Network = styled(Activity)<{ 5 | size?: string | number 6 | }>` 7 | margin-left: 0.25rem; 8 | margin-right: 0.5rem; 9 | width: ${({ size }) => size ?? '16px'}; 10 | height: ${({ size }) => size ?? '16px'}; 11 | ` 12 | -------------------------------------------------------------------------------- /src/utils/account.ts: -------------------------------------------------------------------------------- 1 | import { isAddress } from './validate' 2 | 3 | export function truncateAddress(address: string, chars = 4) { 4 | const parsed = isAddress(address) 5 | if (!parsed) { 6 | console.error(`Invalid 'address' parameter '${address}'.`) 7 | return null 8 | } 9 | return `${parsed.substring(0, chars + 2)}...${parsed.substring(42 - chars)}` 10 | } 11 | -------------------------------------------------------------------------------- /public/static/images/pages/common/down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/constants/providers.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider } from '@ethersproject/providers' 2 | 3 | import { NETWORK_URLS, SupportedChainId } from './chains' 4 | 5 | export const Providers: { [chainId: number]: JsonRpcProvider } = { 6 | [SupportedChainId.RINKEBY]: new JsonRpcProvider(NETWORK_URLS[SupportedChainId.RINKEBY]), 7 | [SupportedChainId.FANTOM]: new JsonRpcProvider(NETWORK_URLS[SupportedChainId.FANTOM]), 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Layout/Warning.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Wrapper = styled.div` 5 | color: #000000; 6 | padding: 10px 10px; 7 | text-align: center; 8 | background-color: #f66; 9 | font-weight: 600; 10 | ` 11 | 12 | export default function Warning({ children }: { children: React.ReactNode }) { 13 | return {children} 14 | } 15 | -------------------------------------------------------------------------------- /src/constants/abi/ERC20.ts: -------------------------------------------------------------------------------- 1 | import { Interface } from '@ethersproject/abi' 2 | 3 | import ERC20_ABI from './ERC20.json' 4 | import ERC20_BYTES32_ABI from './ERC20_BYTES32.json' 5 | 6 | const ERC20_INTERFACE = new Interface(ERC20_ABI) 7 | 8 | const ERC20_BYTES32_INTERFACE = new Interface(ERC20_BYTES32_ABI) 9 | 10 | export default ERC20_INTERFACE 11 | export { ERC20_ABI, ERC20_BYTES32_ABI, ERC20_BYTES32_INTERFACE } 12 | -------------------------------------------------------------------------------- /src/components/DualImage/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const DualImageWrapper = styled.div` 4 | display: flex; 5 | flex-flow: row nowrap; 6 | align-items: center; 7 | justify-content: center; 8 | 9 | & > * { 10 | &:first-child { 11 | transform: translateX(10%); 12 | } 13 | &:nth-child(2) { 14 | transform: translateX(-10%); 15 | } 16 | } 17 | ` 18 | -------------------------------------------------------------------------------- /src/components/Icons/Info.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Info as InfoIcon } from 'react-feather' 3 | 4 | const Icon = styled(InfoIcon)<{ 5 | size?: string 6 | [x: string]: any 7 | }>` 8 | &:hover { 9 | cursor: pointer; 10 | } 11 | ` 12 | 13 | export default function Info({ size, ...rest }: { size?: number; [x: string]: any }) { 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Icons/Search.tsx: -------------------------------------------------------------------------------- 1 | import { Search as SearchIcon } from 'react-feather' 2 | 3 | import { useTheme } from 'styled-components' 4 | import { IconWrapper } from './index' 5 | 6 | export default function Search({ size }: { size?: number }) { 7 | const theme = useTheme() 8 | return ( 9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/apollo/client/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' 2 | 3 | export function createApolloClient(uri: string) { 4 | return new ApolloClient({ 5 | link: new HttpLink({ 6 | uri, 7 | }), 8 | ssrMode: typeof window === 'undefined', 9 | connectToDevTools: typeof window !== 'undefined' && process.NODE_ENV === 'development', 10 | cache: new InMemoryCache(), 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/state/user/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | 3 | export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('user/updateMatchesDarkMode') 4 | export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode') 5 | 6 | export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number | 'auto' }>( 7 | 'user/updateUserSlippageTolerance' 8 | ) 9 | -------------------------------------------------------------------------------- /src/hooks/useInjectedAddress.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import { isAddress } from '@ethersproject/address' 3 | import { useRouter } from 'next/router' 4 | 5 | export function useInjectedAddress() { 6 | const router = useRouter() 7 | const WalletAddress = router.query?.address 8 | return useMemo( 9 | () => (WalletAddress && isAddress(WalletAddress.toString()) ? WalletAddress.toString() : ''), 10 | [WalletAddress] 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/muon/error.ts: -------------------------------------------------------------------------------- 1 | type Result = T | Error 2 | export type Type = Result 3 | 4 | export function getErrorMessage(error: unknown) { 5 | if (error instanceof Error) return error.message 6 | return String(error) 7 | } 8 | 9 | export function isError(result: Result): result is Error { 10 | return result instanceof Error 11 | } 12 | 13 | export function isSuccess(result: Result): result is T { 14 | return !isError(result) 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Icons/Close.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { X } from 'react-feather' 3 | 4 | export const Close = styled(X)<{ 5 | size?: string 6 | color?: string 7 | onClick?: () => void 8 | }>` 9 | width: ${(props) => props.size ?? '15px'}; 10 | height: ${(props) => props.size ?? '15px'}; 11 | color: ${({ theme, color }) => color ?? theme.text1}; 12 | &:hover { 13 | cursor: pointer; 14 | opacity: 0.6; 15 | } 16 | ` 17 | -------------------------------------------------------------------------------- /src/components/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const Card = styled.div` 4 | display: flex; 5 | flex-flow: column nowrap; 6 | justify-content: flex-start; 7 | overflow: hidden; 8 | background: ${({ theme }) => theme.bg2}; 9 | border: 1px solid ${({ theme }) => theme.border1}; 10 | border-radius: 2px; 11 | padding: 2rem; 12 | 13 | ${({ theme }) => theme.mediaWidth.upToMedium` 14 | padding: 1rem; 15 | `} 16 | ` 17 | -------------------------------------------------------------------------------- /src/hooks/useTokenList.ts: -------------------------------------------------------------------------------- 1 | // we need to create this file for dealing with currencies 2 | // study the patterns used in: 3 | // https://github.com/dsynths/dsynths-app-v2/blob/main/src/hooks/useCurrency.ts#L10 4 | // which points to: 5 | // https://github.com/dsynths/dsynths-app-v2/blob/main/src/hooks/useAssetList.ts 6 | 7 | import { useMemo } from 'react' 8 | 9 | export function useTokensFromMap(): { [x: string]: any } { 10 | return useMemo(() => ({}), []) 11 | } 12 | -------------------------------------------------------------------------------- /src/components/Icons/Gift.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Gift as GiftIcon } from 'react-feather' 3 | 4 | const Icon = styled(GiftIcon)<{ 5 | size?: string 6 | [x: string]: any 7 | }>` 8 | stroke-width: 1; 9 | color: ${({ theme }) => theme.text1}; 10 | &:hover { 11 | cursor: pointer; 12 | } 13 | ` 14 | 15 | export default function Gift({ size, ...rest }: { size?: number; [x: string]: any }) { 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/http.ts: -------------------------------------------------------------------------------- 1 | export const makeHttpRequest = async function ( 2 | url: string, 3 | options: { 4 | [x: string]: string 5 | } = { 6 | cache: 'no-cache', 7 | } 8 | ) { 9 | try { 10 | const response = await fetch(url, options) 11 | if (response.ok) { 12 | return await response.json() 13 | } else { 14 | throw new Error(response.statusText) 15 | } 16 | } catch (err) { 17 | console.error(`Error fetching ${url}: `, err) 18 | return null 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /public/static/images/networks/heco.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/Box/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const Box = styled.div` 4 | display: flex; 5 | flex-flow: row nowrap; 6 | justify-content: space-between; 7 | width: 100%; 8 | align-items: center; 9 | background: ${({ theme }) => theme.bg1}; 10 | border: 1px solid ${({ theme }) => theme.border1}; 11 | border-radius: 4px; 12 | padding: 20px; 13 | color: ${({ theme }) => theme.text2}; 14 | overflow: hidden; 15 | white-space: nowrap; 16 | ` 17 | 18 | export default Box 19 | -------------------------------------------------------------------------------- /src/utils/prices.ts: -------------------------------------------------------------------------------- 1 | import { Percent, JSBI } from '@sushiswap/core-sdk' 2 | 3 | export const PERCENT_DENOMINATOR = 100 4 | export const PERCENT_SCALE = 10000 5 | export const ONE_HUNDRED_PERCENT = new Percent(JSBI.BigInt(PERCENT_SCALE), JSBI.BigInt(PERCENT_SCALE)) 6 | 7 | export function constructPercentage(value: number) { 8 | const percent = ~~(value * PERCENT_SCALE) // bitwise remove decimals 9 | return new Percent(JSBI.BigInt(percent), PERCENT_DENOMINATOR * PERCENT_SCALE).multiply(PERCENT_DENOMINATOR) 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Icons/LottieDei.tsx: -------------------------------------------------------------------------------- 1 | import Lottie from 'react-lottie' 2 | import * as animationData from 'constants/lottie/dei-loading.json' 3 | 4 | export default function LottieDei({ height = 150, width = 340 }: { height?: number; width?: number }) { 5 | const defaultOptions = { 6 | loop: true, 7 | autoplay: true, 8 | animationData, 9 | rendererSettings: { 10 | preserveAspectRatio: 'xMidYMid slice', 11 | }, 12 | } 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Warning/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Card } from 'components/Card' 3 | 4 | export const Warning = styled.div` 5 | text-align: center; 6 | width: 100%; 7 | height: fit-content; 8 | padding: 10px; 9 | font-size: 0.6rem; 10 | border: 1px solid ${({ theme }) => theme.red1}; 11 | box-shadow: 1px 1px ${({ theme }) => theme.red2}; 12 | ` 13 | 14 | export const Maintenance = styled(Card)` 15 | border: 1px solid ${({ theme }) => theme.warning}; 16 | margin-bottom: 30px; 17 | ` 18 | -------------------------------------------------------------------------------- /src/components/Web3ProviderNetwork/index.tsx: -------------------------------------------------------------------------------- 1 | import { createWeb3ReactRoot } from '@web3-react/core' 2 | 3 | import { NETWORK_CONTEXT_NAME } from 'constants/misc' 4 | 5 | const Web3ReactProviderDefault = createWeb3ReactRoot(NETWORK_CONTEXT_NAME) 6 | 7 | export default function Web3ReactProviderDefaultSSR({ 8 | children, 9 | getLibrary, 10 | }: { 11 | children: React.ReactNode 12 | getLibrary: (provider: any) => void 13 | }) { 14 | return {children} 15 | } 16 | -------------------------------------------------------------------------------- /src/components/ToolTip/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import ReactTooltip from 'react-tooltip' 3 | 4 | export const ToolTip = styled(ReactTooltip).attrs(({ place = 'right' }) => ({ 5 | place, 6 | type: 'info', 7 | effect: 'solid', 8 | multiline: true, 9 | }))` 10 | color: ${({ theme }) => theme.text1} !important; 11 | background: ${({ theme }) => theme.primary1} !important; 12 | opacity: 1 !important; 13 | padding: 3px 7px !important; 14 | font-size: 0.6rem !important; 15 | max-width: 180px !important; 16 | ` 17 | -------------------------------------------------------------------------------- /src/components/Icons/GreenCircle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Wrapper = styled.div` 5 | display: flex; 6 | flex-flow: row nowrap; 7 | justify-content: center; 8 | align-items: center; 9 | &:first-child { 10 | height: 8px; 11 | width: 8px; 12 | margin-right: 8px; 13 | background-color: green; 14 | border-radius: 50%; 15 | } 16 | ` 17 | 18 | export default function GreenCircle() { 19 | return ( 20 | 21 |
22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /public/static/images/tokens/solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/Icons/ArrowDownDark.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function ArrowDownDark({ ...rest }: { [x: string]: any }) { 4 | return ( 5 | 6 | 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/hooks/usePrevious.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | 3 | export default function usePrevious(value: T): T { 4 | // The ref object is a generic container whose current property is mutable ... 5 | // ... and can hold any value, similar to an instance property on a class 6 | const ref: any = useRef() 7 | // Store current value in ref 8 | useEffect(() => { 9 | ref.current = value 10 | }, [value]) // Only re-run if value changes 11 | // Return previous value (happens before update in useEffect above) 12 | return ref.current 13 | } 14 | -------------------------------------------------------------------------------- /custom.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace NodeJS { 3 | interface Process { 4 | NODE_ENV: 'development' | 'production' 5 | } 6 | } 7 | 8 | interface Window { 9 | walletLinkExtension?: any 10 | ethereum?: { 11 | isCoinbaseWallet?: true 12 | isMetaMask?: true 13 | on?: (...args: any[]) => void 14 | removeListener?: (...args: any[]) => void 15 | removeAllListeners?: (...args: any[]) => void 16 | autoRefreshOnNetworkChange?: boolean 17 | } 18 | web3?: Record 19 | } 20 | } 21 | 22 | export {} 23 | -------------------------------------------------------------------------------- /src/state/reducer.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from '@reduxjs/toolkit' 2 | 3 | import application from './application/reducer' 4 | import multicall from './multicall/reducer' 5 | import transactions from './transactions/reducer' 6 | import dei from './dei/reducer' 7 | import user from './user/reducer' 8 | import dashboard from './dashboard/reducer' 9 | import mint from './mint/reducer' 10 | 11 | const reducer = combineReducers({ 12 | dashboard, 13 | application, 14 | dei, 15 | multicall, 16 | transactions, 17 | user, 18 | mint, 19 | }) 20 | 21 | export default reducer 22 | -------------------------------------------------------------------------------- /src/components/App/StableCoin/Tableau.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | 4 | import { TopTableau, TitleIMGWrap, TableauTitle } from '.' 5 | 6 | export default function Tableau({ title, imgSrc }: { title: string; imgSrc: string }) { 7 | return ( 8 | 9 | {imgSrc && ( 10 | 11 | img 12 | 13 | )} 14 | 15 | {title} 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/constants/misc.ts: -------------------------------------------------------------------------------- 1 | import { RetryOptions } from 'utils/retry' 2 | 3 | export const NETWORK_CONTEXT_NAME = 'NETWORK' 4 | 5 | // Only applies to L2 6 | export const RETRY_OPTIONS_BY_CHAIN_ID: { [chainId: number]: RetryOptions } = {} 7 | export const DEFAULT_RETRY_OPTIONS: RetryOptions = { n: 3, minWait: 1000, maxWait: 3000 } 8 | 9 | // Only applies to L2 10 | export const NETWORK_POLLING_INTERVALS: { [chainId: number]: number } = {} 11 | 12 | export const INFO_URL = 'https://info.deus.finance' 13 | 14 | //oracle api 15 | export const ORACLE_BASE_URL = new URL('https://oracle4.deus.finance') 16 | -------------------------------------------------------------------------------- /src/components/Tab/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const TabWrapper = styled.div` 4 | display: flex; 5 | flex-flow: row nowrap; 6 | justify-content: flex-start; 7 | /* gap: 10px; */ 8 | align-items: center; 9 | font-size: 0.9rem; 10 | ` 11 | 12 | export const TabButton = styled.div<{ 13 | active: boolean 14 | }>` 15 | padding: 10px; 16 | /* padding-bottom: 15px; */ 17 | /* border-bottom: 2px solid ${({ active, theme }) => (active ? theme.border1 : 'transparent')}; */ 18 | font-weight: bold; 19 | &:hover { 20 | cursor: pointer; 21 | } 22 | ` 23 | -------------------------------------------------------------------------------- /src/state/updaters.tsx: -------------------------------------------------------------------------------- 1 | import ApplicationUpdater from './application/updater' 2 | import MulticallUpdater from './multicall/updater' 3 | import TransactionUpdater from './transactions/updater' 4 | import UserUpdater from './user/updater' 5 | import DeiUpdater from './dei/updater' 6 | import DashboardUpdater from './dashboard/updater' 7 | 8 | export default function Updaters() { 9 | return ( 10 | <> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Icons/Droplet.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useTheme } from 'styled-components' 4 | 5 | export default function Droplet({ size, ...rest }: { size: number; [x: string]: any }) { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 20 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/state/dashboard/updater.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { AppThunkDispatch, useAppDispatch } from 'state' 3 | import { autoRefresh } from 'utils/retry' 4 | import { fetchDeiPrice, fetchDeusPrice } from './reducer' 5 | 6 | export default function Updater(): null { 7 | const thunkDispatch: AppThunkDispatch = useAppDispatch() 8 | 9 | useEffect(() => { 10 | return autoRefresh(() => thunkDispatch(fetchDeusPrice()), 300) 11 | }, [thunkDispatch]) 12 | 13 | useEffect(() => { 14 | return autoRefresh(() => thunkDispatch(fetchDeiPrice()), 300) 15 | }, [thunkDispatch]) 16 | 17 | return null 18 | } 19 | -------------------------------------------------------------------------------- /src/constants/abi/ERC20_BYTES32.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bytes32" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": true, 18 | "inputs": [], 19 | "name": "symbol", 20 | "outputs": [ 21 | { 22 | "name": "", 23 | "type": "bytes32" 24 | } 25 | ], 26 | "payable": false, 27 | "stateMutability": "view", 28 | "type": "function" 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /src/utils/validate.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from '@ethersproject/address' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | export function isAddress(value: any): string | false { 5 | try { 6 | return getAddress(value) 7 | } catch { 8 | return false 9 | } 10 | } 11 | 12 | export function isZero(hexNumberString: string): boolean { 13 | return /^0x0*$/.test(hexNumberString) 14 | } 15 | 16 | export function isEmptyValue(text: string): boolean { 17 | return BigNumber.isBigNumber(text) 18 | ? BigNumber.from(text).isZero() 19 | : text === '' || text.replace(/0/g, '').replace(/\./, '') === '' 20 | } 21 | -------------------------------------------------------------------------------- /src/hooks/useHover.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useState } from 'react' 2 | 3 | // See: https://usehooks-ts.com/react-hook/use-event-listener 4 | import useEventListener from './useEventListener' 5 | 6 | function useHover(elementRef: RefObject): boolean { 7 | const [value, setValue] = useState(false) 8 | 9 | const handleMouseEnter = () => setValue(true) 10 | const handleMouseLeave = () => setValue(false) 11 | 12 | useEventListener('mouseenter', handleMouseEnter, elementRef) 13 | useEventListener('mouseleave', handleMouseLeave, elementRef) 14 | 15 | return value 16 | } 17 | 18 | export default useHover 19 | -------------------------------------------------------------------------------- /src/components/Icons/Markets.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useTheme } from 'styled-components' 4 | 5 | export default function Markets({ size, ...rest }: { size: number; [x: string]: any }) { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 17 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Icons/NavToggle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function NavToggle({ ...rest }: { [x: string]: any }) { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Icons/ThemeToggle.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from 'styled-components' 2 | import { Moon, Sun } from 'react-feather' 3 | 4 | import { useIsDarkMode } from 'state/user/hooks' 5 | import { IconWrapper } from './index' 6 | 7 | export default function ThemeToggle({ size }: { size?: number }) { 8 | const darkMode = useIsDarkMode() 9 | const theme = useTheme() 10 | 11 | return darkMode ? ( 12 | 13 | 14 | 15 | ) : ( 16 | 17 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Icons/Link.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Link({ 4 | size = 8, 5 | color = '#EBEBEC', 6 | ...rest 7 | }: { 8 | size?: number 9 | color?: string 10 | [x: string]: any 11 | }) { 12 | return ( 13 | 14 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/state/dashboard/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useAppSelector, AppState } from 'state' 2 | 3 | export const useDashboardState = () => { 4 | return useAppSelector((state: AppState) => state.dashboard) 5 | } 6 | 7 | export const useDeusMetrics = () => { 8 | const { deusCirculatingSupply } = useDashboardState() 9 | return { deusCirculatingSupply } 10 | } 11 | 12 | export const useDeusPrice = (): string => { 13 | const dashboardState = useDashboardState() 14 | return dashboardState.deusPrice.toString() 15 | } 16 | 17 | // export const useDeiPrice = (): string => { 18 | // const dashboardState = useDashboardState() 19 | // return dashboardState.deiPrice.toString() 20 | // } 21 | -------------------------------------------------------------------------------- /src/components/Icons/Lock.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useTheme } from 'styled-components' 4 | 5 | export default function Lock({ size, ...rest }: { size: number; [x: string]: any }) { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/queue.ts: -------------------------------------------------------------------------------- 1 | import Bottleneck from 'bottleneck' 2 | 3 | class Queue { 4 | private limiter: Bottleneck 5 | constructor() { 6 | this.limiter = new Bottleneck({ 7 | reservoir: 50, 8 | reservoirRefreshAmount: 50, 9 | reservoirRefreshInterval: 1 * 60 * 1000, // the 'per minute', must be divisible by 250 10 | maxConcurrent: 50, 11 | }) 12 | } 13 | 14 | // When adding to the queue use this method, takes in a promise. Read the documentation of 'bottleneck' if you want to use async/await or callbacks instead. 15 | add(promise: any) { 16 | return this.limiter.schedule(promise) 17 | } 18 | } 19 | 20 | export const CoingeckoQueue = new Queue() 21 | -------------------------------------------------------------------------------- /public/static/images/footer/Telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/Icons/CreditCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useTheme } from 'styled-components' 4 | 5 | export default function CreditCard({ size, ...rest }: { size: number; [x: string]: any }) { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/components/App/StableCoin/InfoItem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { InfoWrapper } from './index' 5 | import { Loader } from 'components/Icons' 6 | 7 | const Name = styled.div` 8 | color: ${({ theme }) => theme.text2}; 9 | ` 10 | const ItemValue = styled.div` 11 | color: ${({ theme }) => theme.text1}; 12 | ` 13 | 14 | export default function InfoItem({ loading = false, name, value }: { loading?: boolean; name: string; value: string }) { 15 | return ( 16 | <> 17 | 18 | {name} 19 | {loading ? : {value} } 20 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /public/static/images/DeiText.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/hooks/useOnOutsideClick.ts: -------------------------------------------------------------------------------- 1 | import { RefObject } from 'react' 2 | 3 | import useEventListener from './useEventListener' 4 | 5 | type Handler = (event: MouseEvent) => void 6 | 7 | export default function useOnOutsideClick( 8 | ref: RefObject, 9 | handler: Handler, 10 | mouseEvent: 'mousedown' | 'mouseup' = 'mousedown' 11 | ): void { 12 | useEventListener(mouseEvent, (event) => { 13 | const el = ref?.current 14 | 15 | // Do nothing if clicking ref's element or descendent elements 16 | if (!el || el.contains(event.target as Node)) { 17 | return 18 | } 19 | 20 | // Explicit type for "mousedown" event. 21 | handler(event as unknown as MouseEvent) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Column/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const Column = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: flex-start; 7 | ` 8 | export const ColumnCenter = styled(Column)` 9 | align-items: center; 10 | ` 11 | 12 | export const AutoColumn = styled.div<{ 13 | gap?: 'sm' | 'md' | 'lg' | string 14 | justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between' 15 | }>` 16 | display: grid; 17 | grid-auto-rows: auto; 18 | grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap}; 19 | justify-items: ${({ justify }) => justify && justify}; 20 | ` 21 | 22 | export default Column 23 | -------------------------------------------------------------------------------- /src/components/Icons/ArrowBubble.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { ArrowDown } from 'react-feather' 4 | 5 | const Circle = styled.div<{ 6 | size: number 7 | }>` 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | border-radius: 50%; 12 | width: ${({ size }) => size + 'px'}; 13 | height: ${({ size }) => size + 'px'}; 14 | background: ${({ theme }) => theme.bg1}; 15 | box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); 16 | ` 17 | 18 | export default function ArrowBubble({ size = 30, ...rest }: { size?: number; [x: string]: any }) { 19 | return ( 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Icons/Settings.tsx: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components' 2 | import { Settings as SettingsIcon } from 'react-feather' 3 | 4 | interface SvgStyledProps { 5 | className?: string 6 | isOpen: boolean 7 | } 8 | 9 | export const Settings = styled(({ isOpen, ...props }) => )` 10 | width: ${(props) => props.size ?? '15px'}; 11 | height: ${(props) => props.size ?? '15px'}; 12 | color: ${(props) => props.color ?? '#919191'}; 13 | margin-right: 2px; 14 | &:hover { 15 | cursor: pointer; 16 | opacity: 0.6; 17 | } 18 | transition: transform 0.3s ease-in; 19 | ${(props) => 20 | props.isOpen && 21 | css` 22 | transform: rotate(90deg); 23 | `}; 24 | ` 25 | -------------------------------------------------------------------------------- /src/hooks/useCopyClipboard.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react' 2 | import copy from 'copy-to-clipboard' 3 | 4 | export default function useCopyClipboard(timeout = 500): [boolean, (text: string) => void] { 5 | const [isCopied, setIsCopied] = useState(false) 6 | 7 | const staticCopy = useCallback((text: string) => { 8 | const didCopy = copy(text) 9 | setIsCopied(didCopy) 10 | }, []) 11 | 12 | useEffect(() => { 13 | if (isCopied) { 14 | const hide = setTimeout(() => { 15 | setIsCopied(false) 16 | }, timeout) 17 | 18 | return () => { 19 | clearTimeout(hide) 20 | } 21 | } 22 | return undefined 23 | }, [isCopied, setIsCopied, timeout]) 24 | 25 | return [isCopied, staticCopy] 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/numbers.test.ts: -------------------------------------------------------------------------------- 1 | import { formatBalance } from './numbers' 2 | 3 | describe('Number Utils', () => { 4 | it('rounds to significant digits', () => { 5 | expect(formatBalance('0')).toBe('0') 6 | expect(formatBalance('1.23')).toBe('1.23') 7 | expect(formatBalance('12345.678')).toBe('12345.6') 8 | expect(formatBalance('0.1234567', 3)).toBe('0.123') 9 | expect(formatBalance('99', 6)).toBe('99') 10 | }) 11 | it('rounds decimals with high amount of digits to significant digits', () => { 12 | expect(formatBalance('0.00000051511515', 6)).toBe('0.000000515115') 13 | expect(formatBalance('6416465651684.00000051511515', 6)).toBe('6416465651684') 14 | expect(formatBalance('64164656.5168400000051511515', 6)).toBe('64164656') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /src/hooks/useSupportedChainId.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import useWeb3React from './useWeb3' 3 | import { SolidlyChains, SupportedChainId } from 'constants/chains' 4 | 5 | // Allow user to connect any chain globally, but restrict unsupported ones if needed 6 | export function useSupportedChainId() { 7 | const { chainId, account } = useWeb3React() 8 | return useMemo(() => { 9 | if (!chainId || !account) return false 10 | return SolidlyChains.includes(chainId) 11 | }, [chainId, account]) 12 | } 13 | 14 | export function useArbitrumSupportedChainId() { 15 | const { chainId, account } = useWeb3React() 16 | return useMemo(() => { 17 | if (!chainId || !account) return false 18 | return chainId === SupportedChainId.ARBITRUM 19 | }, [chainId, account]) 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Icons/Portfolio.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useTheme } from 'styled-components' 4 | 5 | export default function Portfolio({ size, ...rest }: { size: number; [x: string]: any }) { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 17 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | // modified from https://usehooks.com/useDebounce/ 4 | export default function useDebounce(value: any, delay: number): any { 5 | const [debouncedValue, setDebouncedValue] = useState(value) 6 | 7 | useEffect(() => { 8 | // Update debounced value after delay 9 | const handler = setTimeout(() => { 10 | setDebouncedValue(value) 11 | }, delay) 12 | 13 | // Cancel the timeout if value changes (also on delay change or unmount) 14 | // This is how we prevent debounced value from updating if value is changed ... 15 | // .. within the delay period. Timeout gets cleared and restarted. 16 | return () => { 17 | clearTimeout(handler) 18 | } 19 | }, [value, delay]) 20 | 21 | return debouncedValue 22 | } 23 | -------------------------------------------------------------------------------- /src/hooks/useWindowSize.ts: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect, useState } from 'react' 2 | 3 | import useEventListener from './useEventListener' 4 | 5 | interface WindowSize { 6 | width: number 7 | height: number 8 | } 9 | 10 | export default function useWindowSize(): WindowSize { 11 | const [windowSize, setWindowSize] = useState({ 12 | width: 0, 13 | height: 0, 14 | }) 15 | 16 | const handleSize = () => { 17 | setWindowSize({ 18 | width: window.innerWidth, 19 | height: window.innerHeight, 20 | }) 21 | } 22 | 23 | useEventListener('resize', handleSize) 24 | 25 | // Set size at the first client-side load 26 | useLayoutEffect(() => { 27 | handleSize() 28 | // eslint-disable-next-line react-hooks/exhaustive-deps 29 | }, []) 30 | 31 | return windowSize 32 | } 33 | -------------------------------------------------------------------------------- /src/components/App/CLqdr/Tableau.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import styled from 'styled-components' 4 | 5 | import { TopTableau, TitleIMGWrap, TableauTitle } from 'components/App/StableCoin' 6 | 7 | const Wrapper = styled(TopTableau)` 8 | background: ${({ theme }) => theme.bg1}; 9 | ` 10 | const Title = styled(TableauTitle)` 11 | color: ${({ theme }) => theme.cLqdrColor}; 12 | ` 13 | 14 | export default function Tableau({ title, imgSrc }: { title: string; imgSrc: string }) { 15 | return ( 16 | 17 | {imgSrc && ( 18 | 19 | img 20 | 21 | )} 22 | 23 | {title} 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/components/App/Vest/GeneralLockInformation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const data = [ 5 | '1 DEUS locked for 4 years = 1.00 veDEUS', 6 | '1 DEUS locked for 3 years = 0.75 veDEUS', 7 | '1 DEUS locked for 2 years = 0.50 veDEUS', 8 | '1 DEUS locked for 1 years = 0.25 veDEUS', 9 | ] 10 | 11 | const Wrap = styled.div` 12 | border-top: 1px solid ${({ theme }) => theme.border1}; 13 | padding-top: 15px; 14 | ` 15 | 16 | const Text = styled.div` 17 | font-style: italic; 18 | font-size: 0.8rem; 19 | color: ${({ theme }) => theme.text2}; 20 | ` 21 | 22 | export default function GeneralLockInformation() { 23 | return ( 24 | 25 | {data.map((text, index) => ( 26 | {text} 27 | ))} 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/state/multicall/utils.ts: -------------------------------------------------------------------------------- 1 | export interface Call { 2 | address: string 3 | callData: string 4 | gasRequired?: number 5 | } 6 | 7 | export function toCallKey(call: Call): string { 8 | let key = `${call.address}-${call.callData}` 9 | if (call.gasRequired) { 10 | if (!Number.isSafeInteger(call.gasRequired)) { 11 | throw new Error(`Invalid number: ${call.gasRequired}`) 12 | } 13 | key += `-${call.gasRequired}` 14 | } 15 | return key 16 | } 17 | 18 | export function parseCallKey(callKey: string): Call { 19 | const pcs = callKey.split('-') 20 | if (![2, 3].includes(pcs.length)) { 21 | throw new Error(`Invalid call key: ${callKey}`) 22 | } 23 | return { 24 | address: pcs[0], 25 | callData: pcs[1], 26 | ...(pcs[2] ? { gasRequired: Number.parseInt(pcs[2]) } : {}), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/apollo/client/solidly.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | import { createApolloClient } from './index' 3 | 4 | const fantomClient = createApolloClient(`https://api.thegraph.com/subgraphs/name/${getSubgraphName(250)}`) 5 | 6 | export function getApolloClient(chainId: SupportedChainId) { 7 | switch (chainId) { 8 | case SupportedChainId.FANTOM: 9 | return fantomClient 10 | default: 11 | console.error(`${chainId} is not a supported subgraph network`) 12 | return null 13 | } 14 | } 15 | 16 | export function getSubgraphName(chainId: SupportedChainId) { 17 | switch (chainId) { 18 | case SupportedChainId.FANTOM: 19 | return 'deusfinance/solidly' 20 | default: 21 | console.error(`${chainId} is not a supported subgraph network`) 22 | return null 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/library.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from '@ethersproject/providers' 2 | import { NETWORK_POLLING_INTERVALS } from 'constants/misc' 3 | 4 | export function getLibrary(provider: any): Web3Provider { 5 | const library = new Web3Provider( 6 | provider, 7 | typeof provider.chainId === 'number' 8 | ? provider.chainId 9 | : typeof provider.chainId === 'string' 10 | ? parseInt(provider.chainId) 11 | : 'any' 12 | ) 13 | library.pollingInterval = 15000 14 | library.detectNetwork().then((network) => { 15 | const networkPollingInterval = NETWORK_POLLING_INTERVALS[network.chainId] 16 | if (networkPollingInterval) { 17 | console.debug('Setting polling interval', networkPollingInterval) 18 | library.pollingInterval = networkPollingInterval 19 | } 20 | }) 21 | return library 22 | } 23 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "DEI", 3 | "name": "DEI Solidly", 4 | "background_color": "#000000", 5 | "theme_color": "#000000", 6 | "display": "standalone", 7 | "homepage_url": "https://solidly.dei.finance", 8 | "providedBy": { "name": "DEI Finance", "url": "https://solidly.dei.finance" }, 9 | "icons": [ 10 | { 11 | "src": "./images/192x192_App_Icon.png", 12 | "sizes": "192x192", 13 | "type": "image/png", 14 | "purpose": "any maskable" 15 | }, 16 | { 17 | "src": "./images/512x512_App_Icon.png", 18 | "sizes": "512x512", 19 | "type": "image/png", 20 | "purpose": "any maskable" 21 | } 22 | ], 23 | "orientation": "portrait", 24 | "description": "veNFTs with DEI Finance", 25 | "iconPath": "./images/512x512_App_Icon.png", 26 | "start_url": "/" 27 | } 28 | -------------------------------------------------------------------------------- /public/static/images/networks/binance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | bi 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/apollo/client/vdeus.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | import { createApolloClient } from './index' 3 | 4 | const fantomClient = createApolloClient(`https://api.thegraph.com/subgraphs/name/${getSubgraphName(250)}`) 5 | 6 | export function getApolloClient(chainId: SupportedChainId) { 7 | switch (chainId) { 8 | case SupportedChainId.FANTOM: 9 | return fantomClient 10 | default: 11 | console.error(`${chainId} is not a supported subgraph network`) 12 | return null 13 | } 14 | } 15 | 16 | export function getSubgraphName(chainId: SupportedChainId) { 17 | switch (chainId) { 18 | case SupportedChainId.FANTOM: 19 | return 'mmd-mostafaee/tranche-subgraph' 20 | default: 21 | console.error(`${chainId} is not a supported subgraph network`) 22 | return null 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Icons/VeDeus.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function VeDeus({ size, ...rest }: { size: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /public/static/images/footer/GitBook.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/apollo/client/deiPriceStats.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | import { createApolloClient } from './index' 3 | 4 | const fantomClient = createApolloClient(`https://api.thegraph.com/subgraphs/name/${getSubgraphName(250)}`) 5 | 6 | export function getDeiPriceStatsApolloClient(chainId: SupportedChainId) { 7 | switch (chainId) { 8 | case SupportedChainId.FANTOM: 9 | return fantomClient 10 | default: 11 | console.error(`${chainId} is not a supported subgraph network`) 12 | return null 13 | } 14 | } 15 | 16 | export function getSubgraphName(chainId: SupportedChainId) { 17 | switch (chainId) { 18 | case SupportedChainId.FANTOM: 19 | return 'shadowcrypto1/dei-price-stats' 20 | default: 21 | console.error(`${chainId} is not a supported subgraph network`) 22 | return null 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/apollo/client/ecosystemStats.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | import { createApolloClient } from './index' 3 | 4 | const fantomClient = createApolloClient(`https://api.thegraph.com/subgraphs/name/${getSubgraphName(250)}`) 5 | 6 | export function getEcosystemStatsApolloClient(chainId: SupportedChainId) { 7 | switch (chainId) { 8 | case SupportedChainId.FANTOM: 9 | return fantomClient 10 | default: 11 | console.error(`${chainId} is not a supported subgraph network`) 12 | return null 13 | } 14 | } 15 | 16 | export function getSubgraphName(chainId: SupportedChainId) { 17 | switch (chainId) { 18 | case SupportedChainId.FANTOM: 19 | return 'shadowcrypto1/ecosystem-stats' 20 | default: 21 | console.error(`${chainId} is not a supported subgraph network`) 22 | return null 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | images: { 3 | domains: ['raw.githubusercontent.com', 'assets.spooky.fi'], 4 | }, 5 | async redirects() { 6 | return [ 7 | { 8 | source: '/', 9 | destination: '/dashboard', 10 | permanent: true, 11 | }, 12 | { 13 | source: '/bonds', 14 | destination: '/bond', 15 | permanent: true, 16 | }, 17 | { 18 | source: '/clqdr', 19 | destination: 'http://app.deus.finance/clqdr', 20 | permanent: false, 21 | }, 22 | { 23 | source: '/vest', 24 | destination: 'http://app.deus.finance/vest', 25 | permanent: false, 26 | }, 27 | { 28 | source: '/vest/create', 29 | destination: 'http://app.deus.finance/vest/create', 30 | permanent: false, 31 | }, 32 | ] 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /src/components/App/Dashboard/Chart.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import styled from 'styled-components' 3 | import MEN_AT_WORK from '/public/static/images/pages/dashboard/menAtWork.svg' 4 | 5 | const Wrapper = styled.div` 6 | /* background: ${({ theme }) => theme.bg0}; */ 7 | /* border-radius: 12px; */ 8 | /* width: 100%; */ 9 | /* height: 457px; */ 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: space-between; 13 | /* padding: 38px 36px; */ 14 | /* padding-top: 230px; */ 15 | text-align: center; 16 | justify-content: center; 17 | vertical-align: baseline; 18 | ${({ theme }) => theme.mediaWidth.upToSmall` 19 | flex-direction: column; 20 | `}; 21 | ` 22 | 23 | export default function Chart() { 24 | return ( 25 | 26 | MEN_AT_WORK 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/apollo/client/totalPayment.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | import { createApolloClient } from './index' 3 | 4 | const ArbClient = createApolloClient( 5 | `https://api.thegraph.com/subgraphs/name/${getSubgraphName(SupportedChainId.ARBITRUM)}` 6 | ) 7 | 8 | export function getTotalPaymentApolloClient(chainId: SupportedChainId) { 9 | switch (chainId) { 10 | case SupportedChainId.ARBITRUM: 11 | return ArbClient 12 | default: 13 | console.error(`${chainId} is not a supported subgraph network`) 14 | return null 15 | } 16 | } 17 | 18 | export function getSubgraphName(chainId: SupportedChainId) { 19 | switch (chainId) { 20 | case SupportedChainId.ARBITRUM: 21 | return 'navid-fkh/reimbursewatcher' 22 | default: 23 | console.error(`${chainId} is not a supported subgraph network`) 24 | return null 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/muon/types.ts: -------------------------------------------------------------------------------- 1 | export interface IError { 2 | success: false 3 | error: string 4 | } 5 | 6 | export interface MuonResponse { 7 | success: boolean 8 | error?: string 9 | result: { 10 | app: string 11 | cid: string 12 | confirmed: boolean 13 | confirmedAt: number 14 | startedAt: number 15 | _id: string 16 | method: string 17 | nSign: number 18 | owner: string 19 | peerId: string 20 | data: { 21 | timestamp: number 22 | init: { 23 | none: string 24 | nonceAddress: string 25 | party: string 26 | } 27 | params: any 28 | result: any 29 | } 30 | signatures: { 31 | signature: string 32 | timestamp: number 33 | owner: string 34 | ownerPubKey: { 35 | x: string 36 | yParity: string 37 | } 38 | result: any 39 | }[] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Icons/CheckMark.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function CheckMark({ 4 | size = 12, 5 | color = '#00E376', 6 | ...rest 7 | }: { 8 | size?: number 9 | color?: string 10 | [x: string]: any 11 | }) { 12 | return ( 13 | 14 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/hooks/useIsWindowVisible.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react' 2 | 3 | const VISIBILITY_STATE_SUPPORTED = 'visibilityState' in document 4 | 5 | function isWindowVisible() { 6 | return !VISIBILITY_STATE_SUPPORTED || document.visibilityState !== 'hidden' 7 | } 8 | 9 | /** 10 | * Returns whether the window is currently visible to the user. 11 | */ 12 | export default function useIsWindowVisible(): boolean { 13 | const [focused, setFocused] = useState(isWindowVisible()) 14 | const listener = useCallback(() => { 15 | setFocused(isWindowVisible()) 16 | }, [setFocused]) 17 | 18 | useEffect(() => { 19 | if (!VISIBILITY_STATE_SUPPORTED) return undefined 20 | 21 | document.addEventListener('visibilitychange', listener) 22 | return () => { 23 | document.removeEventListener('visibilitychange', listener) 24 | } 25 | }, [listener]) 26 | 27 | return focused 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Icons/Confirmation.tsx: -------------------------------------------------------------------------------- 1 | import styled, { keyframes } from 'styled-components' 2 | import { Settings as SettingsIcon } from 'react-feather' 3 | 4 | interface SvgStyledProps { 5 | className?: string 6 | size?: string 7 | color?: string 8 | } 9 | 10 | const rotate = keyframes` 11 | from { 12 | -webkit-transform: rotate(0deg); 13 | -o-transform: rotate(0deg); 14 | transform: rotate(0deg); 15 | } 16 | to { 17 | -webkit-transform: rotate(360deg); 18 | -o-transform: rotate(360deg); 19 | transform: rotate(360deg); 20 | } 21 | ` 22 | 23 | export const ConfirmationAnimation = styled(({ size = '15px', color = '#919191', ...props }) => ( 24 | 25 | ))` 26 | width: ${(props) => props.size}; 27 | height: ${(props) => props.size}; 28 | color: ${(props) => props.color}; 29 | animation: ${rotate} 15s linear infinite; 30 | ` 31 | -------------------------------------------------------------------------------- /src/components/Hero/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const Hero = styled.div` 4 | display: flex; 5 | flex-flow: column nowrap; 6 | justify-content: center; 7 | width: 100%; 8 | min-height: 200px; 9 | align-items: center; 10 | text-align: center; 11 | font-size: 60px; 12 | font-weight: bold; 13 | background: ${({ theme }) => theme.bg0}; 14 | padding: 10px; 15 | padding-bottom: 10px; 16 | 17 | ${({ theme }) => theme.mediaWidth.upToMedium` 18 | min-height: 100px; 19 | font-size: 40px; 20 | `} 21 | ${({ theme }) => theme.mediaWidth.upToSmall` 22 | font-size: 30px; 23 | `} 24 | ` 25 | 26 | export const HeroSubtext = styled.div` 27 | font-size: 0.8rem; 28 | font-weight: normal; 29 | text-align: center; 30 | width: 50%; 31 | 32 | ${({ theme }) => theme.mediaWidth.upToSmall` 33 | width: 100%; 34 | font-size: 0.7rem; 35 | `} 36 | ` 37 | 38 | export default Hero 39 | -------------------------------------------------------------------------------- /src/components/Icons/Analytics.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Analytics({ size, ...rest }: { size: number; [x: string]: any }) { 4 | return ( 5 | 6 | 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/currency.ts: -------------------------------------------------------------------------------- 1 | import { Currency, CurrencyAmount, JSBI } from '@sushiswap/core-sdk' 2 | 3 | const MIN_NATIVE_CURRENCY_FOR_GAS: JSBI = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(16)) // .01 ETH 4 | 5 | /** 6 | * Given some token amount, return the max that can be spent of it 7 | * @param currencyAmount to return max of 8 | */ 9 | export function maxAmountSpend(currencyAmount?: CurrencyAmount): CurrencyAmount | undefined { 10 | if (!currencyAmount) return undefined 11 | if (currencyAmount.currency.isNative) { 12 | if (JSBI.greaterThan(currencyAmount.quotient, MIN_NATIVE_CURRENCY_FOR_GAS)) { 13 | return CurrencyAmount.fromRawAmount( 14 | currencyAmount.currency, 15 | JSBI.subtract(currencyAmount.quotient, MIN_NATIVE_CURRENCY_FOR_GAS) 16 | ) 17 | } else { 18 | return CurrencyAmount.fromRawAmount(currencyAmount.currency, JSBI.BigInt(0)) 19 | } 20 | } 21 | return currencyAmount 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Icons/Chevron.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { 3 | ChevronLeft as ChevronLeftIcon, 4 | ChevronDown as ChevronDownIcon, 5 | ChevronUp as ChevronUpIcon, 6 | } from 'react-feather' 7 | 8 | export const ChevronLeft = styled(ChevronLeftIcon)<{ 9 | color?: string 10 | }>` 11 | color: ${({ theme, color }) => color ?? theme.text2}; 12 | &:hover { 13 | cursor: pointer; 14 | opacity: 0.6; 15 | } 16 | ` 17 | 18 | export const ChevronDown = styled(ChevronDownIcon)<{ 19 | color?: string 20 | disabled?: boolean 21 | }>` 22 | color: ${({ theme, color }) => color ?? theme.text2}; 23 | &:hover { 24 | cursor: pointer; 25 | opacity: ${({ disabled }) => (disabled ? '1' : '0.6')}; 26 | } 27 | ` 28 | 29 | export const ChevronUp = styled(ChevronUpIcon)<{ 30 | color?: string 31 | }>` 32 | color: ${({ theme, color }) => color ?? theme.text2}; 33 | &:hover { 34 | cursor: pointer; 35 | opacity: 0.6; 36 | } 37 | ` 38 | -------------------------------------------------------------------------------- /src/components/Icons/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Dashboard({ size, ...rest }: { size: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /src/state/application/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | 3 | import { ApplicationModal, PopupContent } from './reducer' 4 | 5 | export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('application/updateBlockNumber') 6 | export const updateBlockTimestamp = createAction<{ 7 | chainId: number 8 | blockTimestamp: number 9 | }>('application/updateBlockTimestamp') 10 | export const updateChainId = createAction<{ chainId: number }>('application/updateChainId') 11 | export const setChainConnectivityWarning = createAction<{ chainConnectivityWarning: boolean }>( 12 | 'application/setChainConnectivityWarning' 13 | ) 14 | export const setOpenModal = createAction('application/setOpenModal') 15 | export const addPopup = createAction<{ 16 | key?: string 17 | removeAfterMs?: number | null 18 | content: PopupContent 19 | }>('application/addPopup') 20 | export const removePopup = createAction<{ key: string }>('application/removePopup') 21 | -------------------------------------------------------------------------------- /src/utils/parse.ts: -------------------------------------------------------------------------------- 1 | import { parseUnits } from '@ethersproject/units' 2 | import JSBI from 'jsbi' 3 | import { Currency, CurrencyAmount } from '@sushiswap/core-sdk' 4 | 5 | export function parseBalance(value: string, decimals = 18) { 6 | return parseUnits(value || '0', decimals) 7 | } 8 | 9 | // try to parse a user entered amount for a given token 10 | export function tryParseAmount(value?: string, currency?: T): CurrencyAmount | null | undefined { 11 | if (!value || !currency) { 12 | return undefined 13 | } 14 | try { 15 | const typedValueParsed = parseUnits(value, currency.decimals).toString() 16 | return CurrencyAmount.fromRawAmount(currency, JSBI.BigInt(typedValueParsed)) 17 | } catch (error) { 18 | // should fail if the user specifies too many decimal places of precision (or maybe exceed max uint?) 19 | console.debug(`Failed to parse input amount: "${value}"`, error) 20 | } 21 | // necessary for all paths to return a value 22 | return undefined 23 | } 24 | -------------------------------------------------------------------------------- /src/components/ReviewModal/ModalInfo.tsx: -------------------------------------------------------------------------------- 1 | import { RowBetween, RowEnd, RowStart } from 'components/Row' 2 | import React from 'react' 3 | import styled from 'styled-components' 4 | 5 | const Wrapper = styled.div` 6 | display: flex; 7 | gap: 20px; 8 | flex-direction: column; 9 | justify-content: center; 10 | margin: 10px 15px; 11 | ` 12 | 13 | const Title = styled(RowStart)` 14 | font-size: 12px; 15 | line-height: 16px; 16 | color: ${({ theme }) => theme.text2}; 17 | ` 18 | 19 | const Value = styled(RowEnd)` 20 | font-size: 12px; 21 | line-height: 16px; 22 | color: ${({ theme }) => theme.text1}; 23 | ` 24 | 25 | export default function ModalInfo({ info }: { info: { title: string; value: string }[] }) { 26 | return ( 27 | 28 | {info.map((info, index) => { 29 | return ( 30 | 31 | {info.title}: 32 | {info.value} 33 | 34 | ) 35 | })} 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/address.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from '@ethersproject/address' 2 | 3 | export interface AddressMap { 4 | [chainId: number]: string 5 | } 6 | export interface DecimalMap { 7 | [chainId: number]: number 8 | } 9 | 10 | export function isAddress(value: string) { 11 | try { 12 | return getAddress(value) 13 | } catch { 14 | return false 15 | } 16 | } 17 | 18 | export function truncateAddress(address: string, size = 4) { 19 | const parsed = isAddress(address) 20 | if (!parsed) { 21 | console.error(`Invalid 'address' parameter '${address}'.`) 22 | return null 23 | } 24 | return `${parsed.substring(0, size + 2)}...${parsed.substring(address.length - size)}` 25 | } 26 | 27 | export function constructSameAddressMap(address: string, chainMapping: number[]): AddressMap { 28 | return chainMapping.reduce((acc: AddressMap, chainId: number) => { 29 | acc[chainId] = address 30 | return acc 31 | }, {}) 32 | } 33 | 34 | export interface ProxyPath { 35 | [key: number]: { 36 | [key: string]: string[] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/explorers.ts: -------------------------------------------------------------------------------- 1 | import { ChainInfo } from 'constants/chainInfo' 2 | import { FALLBACK_CHAIN_ID, SupportedChainId } from 'constants/chains' 3 | 4 | export enum ExplorerDataType { 5 | TRANSACTION = 'transaction', 6 | ADDRESS = 'address', 7 | TOKEN = 'token', 8 | } 9 | 10 | /** 11 | * Return the explorer link for the given data and data type 12 | * @param chainId the ID of the chain for which to return the data 13 | * @param type the type of the data 14 | * @param data the data to return a link for 15 | */ 16 | export function getExplorerLink(chainId: SupportedChainId, type: ExplorerDataType, data: string): string { 17 | const base = 18 | chainId in ChainInfo ? ChainInfo[chainId]['blockExplorerUrl'] : ChainInfo[FALLBACK_CHAIN_ID]['blockExplorerUrl'] 19 | switch (type) { 20 | case ExplorerDataType.TRANSACTION: 21 | return `${base}/tx/${data}` 22 | case ExplorerDataType.ADDRESS: 23 | return `${base}/address/${data}` 24 | case ExplorerDataType.TOKEN: 25 | return `${base}/token/${data}` 26 | default: 27 | return base 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/static/images/footer/Twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/hooks/useDistRewards.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | 3 | import { toBN } from 'utils/numbers' 4 | import { useSingleContractMultipleData } from 'state/multicall/hooks' 5 | import { useVeDistContract } from 'hooks/useContract' 6 | import { useOwnerVeDeusNFTs } from 'hooks/useOwnerNfts' 7 | 8 | export default function useDistRewards(): number[] { 9 | const veDistContract = useVeDistContract() 10 | const nftIds = useOwnerVeDeusNFTs().results 11 | 12 | const callInputs = useMemo(() => (!nftIds.length ? [] : nftIds.map((id) => [id])), [nftIds]) 13 | 14 | const results = useSingleContractMultipleData(veDistContract, 'getPendingReward', callInputs) 15 | 16 | return useMemo(() => { 17 | return results.reduce((acc: number[], value) => { 18 | if (!value.result) { 19 | acc.push(0) 20 | return acc 21 | } 22 | const result = value.result[0].toString() 23 | if (!result || result === '0') { 24 | acc.push(0) 25 | } else acc.push(toBN(result).div(1e18).toNumber()) 26 | return acc 27 | }, []) 28 | }, [results]) 29 | } 30 | -------------------------------------------------------------------------------- /src/state/transactions/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | 3 | import { Approval } from './reducer' 4 | import { Vest } from './reducer' 5 | 6 | export interface SerializableTransactionReceipt { 7 | to: string 8 | from: string 9 | contractAddress: string 10 | transactionIndex: number 11 | blockHash: string 12 | transactionHash: string 13 | blockNumber: number 14 | status?: number 15 | } 16 | 17 | export const addTransaction = createAction<{ 18 | chainId: number 19 | from: string 20 | hash: string 21 | summary?: string 22 | approval?: Approval 23 | vest?: Vest 24 | }>('transactions/addTransaction') 25 | export const clearAllTransactions = createAction<{ chainId: number }>('transactions/clearAllTransactions') 26 | export const finalizeTransaction = createAction<{ 27 | chainId: number 28 | hash: string 29 | receipt: SerializableTransactionReceipt 30 | }>('transactions/finalizeTransaction') 31 | export const checkedTransaction = createAction<{ 32 | chainId: number 33 | hash: string 34 | blockNumber: number 35 | }>('transactions/checkedTransaction') 36 | -------------------------------------------------------------------------------- /public/static/images/networks/fantom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | fa 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/state/user/updater.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useAppDispatch } from 'state' 3 | 4 | import { updateMatchesDarkMode } from './actions' 5 | 6 | export default function Updater(): null { 7 | const dispatch = useAppDispatch() 8 | 9 | // keep dark mode in sync with the system 10 | useEffect(() => { 11 | const darkHandler = (match: MediaQueryListEvent) => { 12 | dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches })) 13 | } 14 | 15 | const match = window?.matchMedia('(prefers-color-scheme: dark)') 16 | dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches })) 17 | 18 | if (match?.addListener) { 19 | match?.addListener(darkHandler) 20 | } else if (match?.addEventListener) { 21 | match?.addEventListener('change', darkHandler) 22 | } 23 | 24 | return () => { 25 | if (match?.removeListener) { 26 | match?.removeListener(darkHandler) 27 | } else if (match?.removeEventListener) { 28 | match?.removeEventListener('change', darkHandler) 29 | } 30 | } 31 | }, [dispatch]) 32 | 33 | return null 34 | } 35 | -------------------------------------------------------------------------------- /src/hooks/useERC20Allowance.ts: -------------------------------------------------------------------------------- 1 | import { useMemo, useState } from 'react' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | import { Token } from '@sushiswap/core-sdk' 4 | 5 | import useWeb3React from './useWeb3' 6 | import { useERC20Contract } from './useContract' 7 | import { useSingleCallResult } from 'state/multicall/hooks' 8 | 9 | export default function useERC20Allowance(token: Token | undefined, spender: string | undefined): BigNumber { 10 | const { account } = useWeb3React() 11 | const [cachedResult, setCachedResult] = useState(BigNumber.from('0')) 12 | const contract = useERC20Contract(token?.address, true) 13 | 14 | const inputs = useMemo(() => [account ?? undefined, spender ?? undefined], [account, spender]) 15 | const allowance = useSingleCallResult(contract, 'allowance', inputs) 16 | 17 | return useMemo(() => { 18 | const loading = !token || allowance.loading || !allowance.result || allowance.syncing 19 | if (loading) { 20 | return cachedResult 21 | } 22 | setCachedResult(allowance.result[0]) 23 | return allowance.result[0] 24 | }, [token, allowance, cachedResult]) 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "paths": { 5 | "assets/*": ["./assets/*"], 6 | "components/*": ["./components/*"], 7 | "constants/*": ["./constants/*"], 8 | "hooks/*": ["./hooks/*"], 9 | "lib/*": ["./lib/*"], 10 | "state": ["./state"], 11 | "state/*": ["./state/*"], 12 | "theme": ["./theme"], 13 | "theme/*": ["./theme/*"], 14 | "utils/*": ["./utils/*"] 15 | }, 16 | "target": "es5", 17 | "lib": ["dom", "dom.iterable", "esnext"], 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "strict": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "noEmit": true, 23 | "esModuleInterop": true, 24 | "allowSyntheticDefaultImports": true, 25 | "module": "esnext", 26 | "useUnknownInCatchVariables": false, 27 | "moduleResolution": "node", 28 | "resolveJsonModule": true, 29 | "isolatedModules": true, 30 | "jsx": "preserve", 31 | "downlevelIteration": true, 32 | "incremental": true 33 | }, 34 | "include": ["next-env.d.ts", "custom.d.ts", "**/*.ts", "**/*.tsx"], 35 | "exclude": ["node_modules"] 36 | } 37 | -------------------------------------------------------------------------------- /src/components/Layout/NavLogo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import Image from 'next/image' 4 | import { isMobile } from 'react-device-detect' 5 | 6 | import { ExternalLink } from 'components/Link' 7 | 8 | const Container = styled.div` 9 | margin-right: auto; 10 | ` 11 | 12 | const Wrapper = styled.div` 13 | display: flex; 14 | flex-flow: row nowrap; 15 | align-items: center; 16 | width: fit-content; 17 | 18 | &:hover { 19 | cursor: pointer; 20 | } 21 | 22 | & > div { 23 | display: flex; 24 | align-items: center; 25 | &:first-child { 26 | margin-right: 10px; 27 | } 28 | } 29 | ` 30 | 31 | export default function NavLogo() { 32 | function getImageSize() { 33 | return isMobile ? 30 : 45 34 | } 35 | 36 | return ( 37 | 38 | 39 | 40 |
41 | App Logo 42 |
43 |
44 |
45 |
46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/state/multicall/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | 3 | import { Call } from './utils' 4 | 5 | export interface ListenerOptions { 6 | // how often this data should be fetched, by default 1 7 | readonly blocksPerFetch: number 8 | } 9 | 10 | export const addMulticallListeners = createAction<{ chainId: number; calls: Call[]; options: ListenerOptions }>( 11 | 'multicall/addMulticallListeners' 12 | ) 13 | export const removeMulticallListeners = createAction<{ chainId: number; calls: Call[]; options: ListenerOptions }>( 14 | 'multicall/removeMulticallListeners' 15 | ) 16 | export const fetchingMulticallResults = createAction<{ chainId: number; calls: Call[]; fetchingBlockNumber: number }>( 17 | 'multicall/fetchingMulticallResults' 18 | ) 19 | export const errorFetchingMulticallResults = createAction<{ 20 | chainId: number 21 | calls: Call[] 22 | fetchingBlockNumber: number 23 | }>('multicall/errorFetchingMulticallResults') 24 | export const updateMulticallResults = createAction<{ 25 | chainId: number 26 | blockNumber: number 27 | results: { 28 | [callKey: string]: string | null 29 | } 30 | }>('multicall/updateMulticallResults') 31 | -------------------------------------------------------------------------------- /public/static/images/networks/polygon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/Icons/DeiBonds.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function DeiBonds({ size, ...rest }: { size: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /public/static/images/tokens/clqdr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/Icons/ArrowDownLight.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function ArrowDownLight({ size, ...rest }: { size: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import Image from 'next/image' 4 | import { isMobile } from 'react-device-detect' 5 | 6 | import { RowCenter } from 'components/Row' 7 | import NotFound from 'components/Icons/404' 8 | import HUMAN from '/public/static/images/pages/404/Human.svg' 9 | 10 | const Wrapper = styled(RowCenter)` 11 | margin-top: 147px; 12 | flex-direction: column; 13 | position: absolute; 14 | ` 15 | 16 | const ImageWrapper = styled.div` 17 | position: absolute; 18 | bottom: 60px; 19 | width: 127px; 20 | height: 250px; 21 | 22 | ${({ theme }) => theme.mediaWidth.upToMedium` 23 | width: 100px; 24 | height: 200px; 25 | bottom: 40px; 26 | `} 27 | ` 28 | 29 | export default function Custom404() { 30 | function getImageWidth() { 31 | return isMobile ? 345 : 455 32 | } 33 | function getImageHeight() { 34 | return isMobile ? 240 : 314 35 | } 36 | 37 | return ( 38 | <> 39 | 40 | 41 | 42 | Logo 43 | 44 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/components/Icons/Loader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { keyframes, useTheme } from 'styled-components' 3 | 4 | const rotate = keyframes` 5 | from { 6 | transform: rotate(0deg); 7 | } 8 | to { 9 | transform: rotate(360deg); 10 | } 11 | ` 12 | 13 | const StyledSVG = styled.svg<{ 14 | duration: string 15 | size: string 16 | }>` 17 | animation: ${({ duration }) => duration} ${rotate} linear infinite; 18 | height: ${({ size }) => size}; 19 | width: ${({ size }) => size}; 20 | ` 21 | 22 | export default function Loader({ 23 | size = '16px', 24 | stroke, 25 | duration = '2s', 26 | ...rest 27 | }: { 28 | size?: string 29 | stroke?: string 30 | duration?: string 31 | [x: string]: any 32 | }) { 33 | const theme = useTheme() 34 | const color = stroke ?? theme.text2 35 | 36 | return ( 37 | 38 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/parseError.ts: -------------------------------------------------------------------------------- 1 | function getErrorState(error: any): string | undefined { 2 | let reason: string | undefined 3 | let message: string | undefined 4 | 5 | while (Boolean(error)) { 6 | reason = error.reason ?? error.message ?? reason 7 | message = error.data?.message ?? message 8 | error = error.error ?? error.data?.originalError 9 | } 10 | reason = message ?? reason 11 | if (reason?.indexOf('execution reverted: ') === 0) reason = reason.substr('execution reverted: '.length) 12 | return reason 13 | } 14 | 15 | export function DefaultHandlerError(error: any): string | null { 16 | const reason = getErrorState(error) 17 | return `${reason ? `"${reason}"` : ''}.` 18 | } 19 | 20 | //TODO: get All error and make a readable message here 21 | export function CollateralPoolErrorToUserReadableMessage(error: any): string { 22 | const reason = getErrorState(error) 23 | 24 | switch (reason) { 25 | case 'TwapUniOracle: NOT_UPDATED': 26 | return `please "Update Oracle".` 27 | 28 | case 'DEIPool: COLLATERAL_COLLECTION_DELAY': 29 | return `wait a few seconds and try again` 30 | 31 | case 'DEIV2Pool: DEUS_COLLECTION_DELAY': 32 | return `wait a few seconds and try again` 33 | } 34 | 35 | return `${reason ? `"${reason}"` : ''}.` 36 | } 37 | -------------------------------------------------------------------------------- /public/static/images/networks/xdai.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | Group 6 10 | Created with Sketch. 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/Copy/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { useTheme } from 'styled-components' 3 | 4 | import useCopyClipboard from 'hooks/useCopyClipboard' 5 | import { Copy as CopyIcon, CheckMark } from 'components/Icons' 6 | 7 | const Wrapper = styled.div` 8 | display: flex; 9 | flex-flow: row nowrap; 10 | justify-content: flex-start; 11 | gap: 5px; 12 | align-items: center; 13 | 14 | &:hover, 15 | &:focus { 16 | cursor: pointer; 17 | } 18 | ` 19 | 20 | export default function Copy({ 21 | toCopy, 22 | text, 23 | placement = 'left', 24 | noFeedback, 25 | }: { 26 | toCopy: string 27 | text?: any 28 | placement?: 'left' | 'right' 29 | noFeedback?: boolean 30 | }): JSX.Element { 31 | const [isCopied, setCopied] = useCopyClipboard() 32 | const theme = useTheme() 33 | 34 | return isCopied ? ( 35 | 36 | {placement == 'right' && !noFeedback && 'Copied'} 37 | 38 | {placement == 'left' && !noFeedback && 'Copied'} 39 | 40 | ) : ( 41 | setCopied(toCopy)}> 42 | {placement == 'right' && text} 43 | 44 | {placement == 'left' && text} 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/components/App/CLqdr/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const Wrapper = styled.div` 4 | margin-top: 16px; 5 | border-radius: 12px; 6 | width: clamp(250px, 90%, 500px); 7 | background: ${({ theme }) => theme.bg3}; 8 | padding: 12px; 9 | height: 88px; 10 | position: relative; 11 | overflow: hidden; 12 | ` 13 | 14 | export const BuyButtonWrapper = styled.div` 15 | padding: 2px; 16 | width: 452px; 17 | height: 56px; 18 | background: linear-gradient(90deg, #f34038 0%, #ffb396 52.08%, #f78c2a 100%); 19 | border-radius: 12px; 20 | ` 21 | 22 | export const BuyButton = styled.div` 23 | width: 100%; 24 | height: 100%; 25 | 26 | background: linear-gradient(90deg, #f78c2a 0%, #f34038 100%), 27 | linear-gradient(90deg, #f34038 0%, #ffb396 52.08%, #f78c2a 100%); 28 | 29 | border-radius: 12px; 30 | 31 | font-family: 'Inter'; 32 | font-style: normal; 33 | font-weight: 600; 34 | font-size: 16px; 35 | line-height: 19px; 36 | 37 | text-align: center; 38 | outline: none; 39 | text-decoration: none; 40 | display: flex; 41 | justify-content: center; 42 | flex-wrap: nowrap; 43 | align-items: center; 44 | 45 | color: ${({ theme }) => theme.text1}; 46 | 47 | ${({ theme }) => theme.mediaWidth.upToExtraSmall` 48 | font-size: 12px; 49 | `} 50 | ` 51 | -------------------------------------------------------------------------------- /src/components/ImageWithFallback/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import Image from 'next/image' 3 | import styled from 'styled-components' 4 | 5 | import NotFound from '/public/static/images/fallback/not_found.png' 6 | 7 | const Wrapper = styled.div<{ 8 | round?: boolean 9 | }>` 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | border-radius: ${({ round }) => (round ? '50%' : '0px')}; 14 | overflow: hidden; 15 | ` 16 | 17 | export default function ImageWithFallback({ 18 | src, 19 | alt, 20 | width, 21 | height, 22 | loading = false, 23 | round = false, 24 | ...rest 25 | }: { 26 | src: StaticImageData | string 27 | alt: string 28 | width: number 29 | height: number 30 | loading?: boolean 31 | round?: boolean 32 | [x: string]: any 33 | }) { 34 | const [imgSrc, setImgSrc] = useState('/static/images/fallback/loader.gif') 35 | 36 | useEffect(() => { 37 | if (loading) { 38 | setImgSrc('/static/images/fallback/loader.gif') 39 | } else { 40 | setImgSrc(src) 41 | } 42 | }, [src, loading]) 43 | 44 | return ( 45 | 46 | {alt} setImgSrc(NotFound.src)} {...rest} /> 47 | 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /src/hooks/useAddTokenToMetaMask.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from 'react' 2 | import { Currency, Token } from '@sushiswap/core-sdk' 3 | 4 | import useWeb3React from './useWeb3' 5 | 6 | export default function useAddTokenToMetaMask(currencyToAdd: Currency | undefined): { 7 | addToken: () => void 8 | success: boolean | undefined 9 | } { 10 | const { library } = useWeb3React() 11 | const token: Token | undefined = currencyToAdd?.wrapped 12 | const [success, setSuccess] = useState() 13 | 14 | const addToken = useCallback(() => { 15 | if (library && library.provider.isMetaMask && library.provider.request && token) { 16 | library.provider 17 | .request({ 18 | method: 'wallet_watchAsset', 19 | params: { 20 | // @ts-ignore // need this for incorrect ethers provider type 21 | type: 'ERC20', 22 | options: { 23 | address: token.address, 24 | symbol: token.symbol, 25 | decimals: token.decimals, 26 | }, 27 | }, 28 | }) 29 | .then((success) => { 30 | setSuccess(success) 31 | }) 32 | .catch(() => setSuccess(false)) 33 | } else { 34 | setSuccess(false) 35 | } 36 | }, [library, token]) 37 | 38 | return { addToken, success } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/Icons/DotFlashing.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { keyframes } from 'styled-components' 3 | 4 | const FlashingAnimation = keyframes` 5 | 0% { 6 | background: #ffffff; 7 | } 8 | 50% { 9 | background: rgba(255, 255, 255, 0.5); 10 | } 11 | 100% { 12 | background: rgba(255, 255, 255, 0.25); 13 | } 14 | ` 15 | 16 | export const Wrapper = styled.div` 17 | display: flex; 18 | align-items: center; 19 | justify-content: center; 20 | margin-left: 6px; 21 | ` 22 | 23 | export const Dot = styled.div<{ 24 | size: string 25 | gap: string 26 | delay: string 27 | }>` 28 | background-color: ${({ theme }) => theme.text1}; 29 | border-radius: 50%; 30 | width: ${(props) => props.size}; 31 | height: ${(props) => props.size}; 32 | margin: 0 ${(props) => props.gap}; 33 | animation: ${FlashingAnimation} 1s infinite linear alternate; 34 | animation-delay: ${(props) => props.delay}; 35 | ` 36 | 37 | export default function DotFlashing({ 38 | size = '5px', 39 | gap = '1.5px', 40 | ...rest 41 | }: { 42 | size?: string 43 | gap?: string 44 | [x: string]: any 45 | }) { 46 | return ( 47 | 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/connectors/index.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from '@ethersproject/providers' 2 | import { InjectedConnector } from '@web3-react/injected-connector' 3 | import { WalletConnectConnector } from '@web3-react/walletconnect-connector' 4 | import { WalletLinkConnector } from '@web3-react/walletlink-connector' 5 | 6 | import { NetworkConnector } from './NetworkConnector' 7 | 8 | import { getLibrary } from 'utils/library' 9 | 10 | import { FALLBACK_CHAIN_ID, NETWORK_URLS, SUPPORTED_CHAIN_IDS } from 'constants/chains' 11 | 12 | let networkLibrary: Web3Provider | undefined 13 | export function getNetworkLibrary(): Web3Provider { 14 | return (networkLibrary = networkLibrary ?? getLibrary(network.provider)) 15 | } 16 | 17 | export const network = new NetworkConnector({ 18 | urls: NETWORK_URLS, 19 | defaultChainId: FALLBACK_CHAIN_ID, 20 | }) 21 | 22 | export const injected = new InjectedConnector({ 23 | supportedChainIds: SUPPORTED_CHAIN_IDS, 24 | }) 25 | 26 | export const walletconnect = new WalletConnectConnector({ 27 | supportedChainIds: SUPPORTED_CHAIN_IDS, 28 | rpc: NETWORK_URLS, 29 | qrcode: true, 30 | }) 31 | 32 | export const walletlink = new WalletLinkConnector({ 33 | url: NETWORK_URLS[FALLBACK_CHAIN_ID], 34 | appName: 'DEI Finance', 35 | appLogoUrl: require('/public/static/images/AppLogo.png'), 36 | supportedChainIds: SUPPORTED_CHAIN_IDS, 37 | }) 38 | -------------------------------------------------------------------------------- /src/hooks/useEventListener.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect, useRef } from 'react' 2 | 3 | export default function useEventListener( 4 | eventName: keyof WindowEventMap | string, // string to allow custom event 5 | handler: (event: Event) => void, 6 | element?: RefObject 7 | ) { 8 | // Create a ref that stores handler 9 | const savedHandler = useRef<(event: Event) => void>() 10 | 11 | useEffect(() => { 12 | // Define the listening target 13 | const targetElement: T | Window = element?.current || window 14 | if (!(targetElement && targetElement.addEventListener)) { 15 | return 16 | } 17 | 18 | // Update saved handler if necessary 19 | if (savedHandler.current !== handler) { 20 | savedHandler.current = handler 21 | } 22 | 23 | // Create event listener that calls handler function stored in ref 24 | const eventListener = (event: Event) => { 25 | // eslint-disable-next-line no-extra-boolean-cast 26 | if (!!savedHandler?.current) { 27 | savedHandler.current(event) 28 | } 29 | } 30 | 31 | targetElement.addEventListener(eventName, eventListener) 32 | 33 | // Remove event listener on cleanup 34 | return () => { 35 | targetElement.removeEventListener(eventName, eventListener) 36 | } 37 | }, [eventName, element, handler]) 38 | } 39 | -------------------------------------------------------------------------------- /src/components/App/StableCoin/PoolStats.tsx: -------------------------------------------------------------------------------- 1 | import { formatAmount, formatDollarAmount } from 'utils/numbers' 2 | import { useMemo } from 'react' 3 | import { truncateAddress } from 'utils/address' 4 | import { SupportedChainId } from 'constants/chains' 5 | import { CollateralPool } from 'constants/addresses' 6 | 7 | import { useDeusPrice } from 'state/dashboard/hooks' 8 | 9 | import { useDeiStats } from 'hooks/useDeiStats' 10 | 11 | export default function usePoolStats(): { name: string; value: number | string; link?: string }[] { 12 | const { collateralRatio } = useDeiStats() 13 | const deusPrice = useDeusPrice() 14 | 15 | const usdcBackingPerDei = useMemo(() => { 16 | if (collateralRatio > 100) return '100%' 17 | else if (collateralRatio < 90) return '90%' 18 | return `${formatAmount(collateralRatio, 1).toString()}%` 19 | }, [collateralRatio]) 20 | 21 | return useMemo( 22 | () => [ 23 | { name: 'DEI Price', value: '$1.00' }, 24 | { name: 'Collateral Ratio', value: usdcBackingPerDei ?? '-' }, 25 | { name: 'DEUS Price', value: formatDollarAmount(parseFloat(deusPrice), 2) ?? '-' }, 26 | { 27 | name: 'Pool(V3)', 28 | value: truncateAddress(CollateralPool[SupportedChainId.FANTOM]) ?? '-', 29 | link: CollateralPool[SupportedChainId.FANTOM], 30 | }, 31 | ], 32 | [usdcBackingPerDei, deusPrice] 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Icons/Wallet.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from 'styled-components' 3 | 4 | export const Wallet = ({ size = 12, ...rest }: { size?: number; [x: string]: any }) => { 5 | const theme = useTheme() 6 | 7 | return ( 8 | 9 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // jest.config.js 2 | module.exports = { 3 | collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts', '!**/node_modules/**'], 4 | moduleNameMapper: { 5 | // Handle CSS imports (with CSS modules) 6 | // https://jestjs.io/docs/webpack#mocking-css-modules 7 | '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', 8 | 9 | // Handle CSS imports (without CSS modules) 10 | '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', 11 | 12 | // Handle image imports 13 | // https://jestjs.io/docs/webpack#handling-static-assets 14 | '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, 15 | 16 | // Handle module aliases 17 | '^@/components/(.*)$': '/components/$1', 18 | }, 19 | // Add more setup options before each test is run 20 | // setupFilesAfterEnv: ['/jest.setup.js'], 21 | testPathIgnorePatterns: ['/node_modules/', '/.next/'], 22 | testEnvironment: 'jsdom', 23 | transform: { 24 | // Use babel-jest to transpile tests with the next/babel preset 25 | // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object 26 | '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], 27 | }, 28 | transformIgnorePatterns: ['/node_modules/', '^.+\\.module\\.(css|sass|scss)$'], 29 | setupFilesAfterEnv: ['/jest.setup.ts'], 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Row/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from 'rebass/styled-components' 2 | import styled from 'styled-components' 3 | 4 | export const Row = styled(Box)<{ 5 | width?: string 6 | align?: string 7 | justify?: string 8 | padding?: string 9 | border?: string 10 | borderRadius?: string 11 | }>` 12 | width: ${({ width }) => width ?? '100%'}; 13 | display: flex; 14 | padding: 0; 15 | align-items: ${({ align }) => align ?? 'center'}; 16 | justify-content: ${({ justify }) => justify ?? 'flex-start'}; 17 | padding: ${({ padding }) => padding}; 18 | border: ${({ border }) => border}; 19 | border-radius: ${({ borderRadius }) => borderRadius}; 20 | ` 21 | export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>` 22 | flex-wrap: wrap; 23 | margin: ${({ gap }) => gap && `-${gap}`}; 24 | justify-content: ${({ justify }) => justify && justify}; 25 | & > * { 26 | margin: ${({ gap }) => gap} !important; 27 | } 28 | ` 29 | export const RowBetween = styled(Row)` 30 | justify-content: space-between; 31 | ` 32 | export const RowCenter = styled(Row)` 33 | justify-content: center; 34 | ` 35 | export const RowStart = styled(Row)` 36 | justify-content: flex-start; 37 | ` 38 | export const RowEnd = styled(Row)` 39 | justify-content: flex-end; 40 | ` 41 | export const RowFixed = styled(Row)<{ gap?: string; justify?: string }>` 42 | width: fit-content; 43 | margin: ${({ gap }) => gap && `-${gap}`}; 44 | ` 45 | -------------------------------------------------------------------------------- /src/utils/array.ts: -------------------------------------------------------------------------------- 1 | const CONSERVATIVE_BLOCK_GAS_LIMIT = 10_000_000 // conservative, hard-coded estimate of the current block gas limit 2 | export const DEFAULT_GAS_REQUIRED = 200_000 // the default value for calls that don't specify gasRequired 3 | 4 | // chunks array into chunks 5 | // evenly distributes items among the chunks 6 | export function chunkArray(items: T[], gasLimit = CONSERVATIVE_BLOCK_GAS_LIMIT * 10): T[][] { 7 | const chunks: T[][] = [] 8 | let currentChunk: T[] = [] 9 | let currentChunkCumulativeGas = 0 10 | 11 | for (let i = 0; i < items.length; i++) { 12 | const item = items[i] 13 | 14 | // calculate the gas required by the current item 15 | const gasRequired = (item as { gasRequired?: number })?.gasRequired ?? DEFAULT_GAS_REQUIRED 16 | 17 | // if the current chunk is empty, or the current item wouldn't push it over the gas limit, 18 | // append the current item and increment the cumulative gas 19 | if (currentChunk.length === 0 || currentChunkCumulativeGas + gasRequired < gasLimit) { 20 | currentChunk.push(item) 21 | currentChunkCumulativeGas += gasRequired 22 | } else { 23 | // otherwise, push the current chunk and create a new chunk 24 | chunks.push(currentChunk) 25 | currentChunk = [item] 26 | currentChunkCumulativeGas = gasRequired 27 | } 28 | } 29 | if (currentChunk.length > 0) chunks.push(currentChunk) 30 | 31 | return chunks 32 | } 33 | -------------------------------------------------------------------------------- /public/static/images/networks/mainnet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/lib/muon/client/base.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance } from 'axios' 2 | import { getAddress } from '@ethersproject/address' 3 | 4 | import { MuonResponse } from '../types' 5 | import { Type } from '../error' 6 | 7 | interface IConstructor { 8 | baseURL: string 9 | nSign: number 10 | APP_ID: string 11 | APP_METHOD: string 12 | } 13 | 14 | export class MuonClient { 15 | private _api: AxiosInstance 16 | public APP_ID: string 17 | public APP_METHOD: string 18 | public nSign: number 19 | 20 | constructor({ baseURL, nSign, APP_ID, APP_METHOD }: IConstructor) { 21 | this._api = axios.create({ 22 | baseURL, 23 | timeout: 20000, 24 | }) 25 | this.nSign = nSign 26 | this.APP_ID = APP_ID 27 | this.APP_METHOD = APP_METHOD 28 | } 29 | 30 | public _getChecksumAddress(contract: string) { 31 | try { 32 | return getAddress(contract) 33 | } catch (err) { 34 | return false 35 | } 36 | } 37 | 38 | public async _makeRequest(requestParams: any): Promise> { 39 | const response = await this._api({ 40 | method: 'POST', 41 | data: { 42 | app: this.APP_ID, 43 | method: this.APP_METHOD, 44 | nSign: this.nSign, 45 | params: requestParams, 46 | }, 47 | }) 48 | 49 | if (response.status !== 200) { 50 | return new Error('Unable to reach the Muon Network.') 51 | } 52 | return response.data 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/Web3ReactManager/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useWeb3React } from '@web3-react/core' 3 | 4 | import { network } from '../../connectors' 5 | import { useEagerConnect, useInactiveListener } from 'hooks/useWeb3' 6 | import { NETWORK_CONTEXT_NAME } from 'constants/misc' 7 | 8 | export default function Web3ReactManager({ children }: { children: JSX.Element }) { 9 | const { active } = useWeb3React() 10 | const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React(NETWORK_CONTEXT_NAME) 11 | 12 | // try to eagerly connect to an injected provider, if it exists and has granted access already 13 | const triedEager = useEagerConnect() 14 | 15 | useEffect(() => { 16 | if (triedEager && !networkActive && !networkError && !active) { 17 | activateNetwork(network) 18 | } 19 | }, [triedEager, networkActive, networkError, activateNetwork, active]) 20 | 21 | // when there's no account connected, react to logins (broadly speaking) on the injected provider, if it exists 22 | useInactiveListener(!triedEager) 23 | 24 | if (!triedEager) { 25 | return null 26 | } 27 | 28 | // if the account context isn't active, and there's an error on the network context, it's an irrecoverable error 29 | if (!active && networkError) { 30 | console.log('Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device.') 31 | } 32 | 33 | return children 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Icons/Copy.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Copy({ size = 12, ...rest }: { size?: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 16 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /public/static/images/footer/Github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/constants/chains.ts: -------------------------------------------------------------------------------- 1 | // We allow the user to connect with these chains, so we can force them to change to Fantom. 2 | // E.g. if the user's chain is not in the list, web3react will deny connection and then we can't change to Fantom. 3 | export enum SupportedChainId { 4 | MAINNET = 1, 5 | ROPSTEN = 3, 6 | RINKEBY = 4, 7 | GOERLI = 5, 8 | KOVAN = 42, 9 | 10 | TELOS = 40, 11 | XDAI = 100, 12 | FUSE = 122, 13 | CELO = 42220, 14 | 15 | BSC = 56, 16 | BSC_TESTNET = 97, 17 | 18 | OKEX_TESTNET = 65, 19 | OKEX = 66, 20 | 21 | HECO = 128, 22 | HECO_TESTNET = 256, 23 | 24 | POLYGON = 137, 25 | POLYGON_TESTNET = 80001, 26 | 27 | FANTOM = 250, 28 | FANTOM_TESTNET = 4002, 29 | 30 | MOONRIVER = 1285, 31 | MOONBEAM_TESTNET = 1287, 32 | 33 | ARBITRUM = 42161, 34 | ARBITRUM_TESTNET = 79377087078960, 35 | 36 | AVALANCHE_TESTNET = 43113, 37 | AVALANCHE = 43114, 38 | 39 | HARMONY = 1666600000, 40 | HARMONY_TESTNET = 1666700000, 41 | 42 | PALM = 11297108109, 43 | PALM_TESTNET = 11297108099, 44 | } 45 | 46 | export const SUPPORTED_CHAIN_IDS: SupportedChainId[] = Object.values(SupportedChainId).filter( 47 | (id) => typeof id === 'number' 48 | ) as SupportedChainId[] 49 | 50 | export const SolidlyChains = [SupportedChainId.FANTOM, SupportedChainId.ARBITRUM] 51 | 52 | export const FALLBACK_CHAIN_ID = SupportedChainId.FANTOM 53 | 54 | export const NETWORK_URLS: { [chainId: number]: string } = { 55 | [SupportedChainId.FANTOM]: 'https://rpc.ftm.tools', 56 | } 57 | -------------------------------------------------------------------------------- /public/static/images/tokens/lqdr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaVersion": 2020, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "jsx": true 8 | } 9 | }, 10 | "extends": [ 11 | "next", 12 | "plugin:react/recommended", 13 | "plugin:@typescript-eslint/recommended", 14 | "plugin:react-hooks/recommended", 15 | "plugin:prettier/recommended", 16 | "prettier" 17 | ], 18 | "plugins": ["unused-imports"], 19 | "rules": { 20 | "unused-imports/no-unused-imports": "error", 21 | "@typescript-eslint/explicit-function-return-type": "off", 22 | "@typescript-eslint/no-explicit-any": "off", 23 | "@typescript-eslint/ban-ts-comment": "off", 24 | "@typescript-eslint/ban-ts-ignore": "off", 25 | "@typescript-eslint/explicit-module-boundary-types": "off", 26 | "react/react-in-jsx-scope": "off", 27 | "object-shorthand": ["error", "always"], 28 | "prettier/prettier": [ 29 | "error", { 30 | "endOfLine": "auto" 31 | } 32 | ], 33 | "no-restricted-imports": [ 34 | "error", 35 | { 36 | "paths": [ 37 | { 38 | "name": "lodash", 39 | "message": "Please import from 'lodash/module' directly to support tree-shaking." 40 | }, 41 | { 42 | "name": "ethers", 43 | "message": "Please import from '@ethersproject/module' directly to support tree-shaking." 44 | } 45 | ] 46 | } 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/StableCoin/Navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Wrapper = styled.div` 5 | display: flex; 6 | flex-flow: row nowrap; 7 | justify-content: flex-start; 8 | gap: 20px; 9 | margin-left: 30px; 10 | ` 11 | 12 | const Item = styled.div<{ 13 | selected: boolean 14 | }>` 15 | font-size: 15px; 16 | transition: all 0.3s ease; 17 | border-bottom: 1px solid ${({ selected, theme }) => (selected ? theme.text1 : 'transparent')}; 18 | color: ${({ selected, theme }) => (selected ? theme.text1 : theme.text3)}; 19 | &:hover { 20 | cursor: pointer; 21 | } 22 | ` 23 | 24 | export enum NavigationTypes { 25 | MINT = 'MINT', 26 | SWAP = 'SWAP', 27 | REDEEM = 'REDEEM', 28 | } 29 | 30 | const NavigationLabels = { 31 | [NavigationTypes.MINT]: 'Mint', 32 | [NavigationTypes.SWAP]: 'Swap', 33 | [NavigationTypes.REDEEM]: 'Redeem', 34 | } 35 | 36 | export default function Navigation({ 37 | selected, 38 | setSelected, 39 | }: { 40 | selected: string 41 | setSelected: (value: NavigationTypes) => void 42 | }) { 43 | return ( 44 | 45 | {(Object.keys(NavigationTypes) as Array).map((key, index) => { 46 | const label = NavigationLabels[key] 47 | return ( 48 | setSelected(NavigationTypes[key])} key={index}> 49 | {label} 50 | 51 | ) 52 | })} 53 | 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /src/components/Web3Network/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react' 2 | import styled from 'styled-components' 3 | import Image from 'next/image' 4 | 5 | import useWeb3React from 'hooks/useWeb3' 6 | import { ChainInfo } from 'constants/chainInfo' 7 | 8 | import { NavButton } from 'components/Button' 9 | import { SolidlyChains } from 'constants/chains' 10 | 11 | const Button = styled(NavButton)` 12 | background: ${({ theme }) => theme.bg1}; 13 | justify-content: space-between; 14 | gap: 5px; 15 | 16 | &:focus, 17 | &:hover { 18 | cursor: default; 19 | border: 1px solid ${({ theme }) => theme.text3}; 20 | } 21 | 22 | ${({ theme }) => theme.mediaWidth.upToMedium` 23 | & > * { 24 | &:nth-child(2) { 25 | display: none; 26 | } 27 | } 28 | `}; 29 | ` 30 | 31 | const Text = styled.p` 32 | width: fit-content; 33 | /* overflow: hidden; */ 34 | text-overflow: ellipsis; 35 | white-space: nowrap; 36 | font-weight: bold; 37 | ` 38 | 39 | export default function Web3Network() { 40 | const { account, chainId } = useWeb3React() 41 | 42 | const Chain = useMemo(() => { 43 | return chainId && chainId in ChainInfo ? ChainInfo[chainId] : null 44 | }, [chainId]) 45 | 46 | if (!account || !chainId || !Chain || !SolidlyChains.includes(chainId)) { 47 | return null 48 | } 49 | 50 | return ( 51 | 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /src/components/analytics/GoogleAnalyticsProvider.tsx: -------------------------------------------------------------------------------- 1 | import ReactGA from 'react-ga4' 2 | import { GaOptions, InitOptions, UaEventOptions } from 'react-ga4/types/ga4' 3 | 4 | /** 5 | * Google Analytics Provider containing all methods used throughout app to log events to Google Analytics. 6 | */ 7 | export default class GoogleAnalyticsProvider { 8 | public sendEvent(event: string | UaEventOptions, params?: any) { 9 | ReactGA.event(event, params) 10 | } 11 | 12 | public initialize( 13 | GA_MEASUREMENT_ID: InitOptions[] | string, 14 | options?: { 15 | legacyDimensionMetric?: boolean 16 | nonce?: string 17 | testMode?: boolean 18 | gaOptions?: GaOptions | any 19 | gtagOptions?: any 20 | } 21 | ) { 22 | ReactGA.initialize(GA_MEASUREMENT_ID, options) 23 | } 24 | 25 | public set(fieldsObject: any) { 26 | ReactGA.set(fieldsObject) 27 | } 28 | 29 | public outboundLink( 30 | { 31 | label, 32 | }: { 33 | label: string 34 | }, 35 | hitCallback: () => unknown 36 | ) { 37 | ReactGA.outboundLink({ label }, hitCallback) 38 | } 39 | 40 | public pageview(path?: string, _?: string[], title?: string) { 41 | ReactGA.pageview(path, _, title) 42 | } 43 | 44 | public ga(...args: any[]) { 45 | ReactGA.ga(...args) 46 | } 47 | 48 | public gaCommandSendTiming(timingCategory: any, timingVar: any, timingValue: any, timingLabel: any) { 49 | ReactGA._gaCommandSendTiming(timingCategory, timingVar, timingValue, timingLabel) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # vscode 45 | .vscode/ 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Optional REPL history 54 | .node_repl_history 55 | 56 | # Output of 'npm pack' 57 | *.tgz 58 | 59 | # Yarn Integrity file 60 | .yarn-integrity 61 | 62 | # dotenv environment variables file 63 | .env 64 | .env.development 65 | 66 | # parcel-bundler cache (https://parceljs.org/) 67 | .cache 68 | 69 | # next.js build output 70 | .next 71 | out/ 72 | 73 | # nuxt.js build output 74 | .nuxt 75 | 76 | # vuepress build output 77 | .vuepress/dist 78 | 79 | # Serverless directories 80 | .serverless 81 | 82 | # FuseBox cache 83 | .fusebox/ 84 | 85 | .DS_Store 86 | -------------------------------------------------------------------------------- /src/state/user/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from '@reduxjs/toolkit' 2 | 3 | import { updateUserSlippageTolerance, updateMatchesDarkMode, updateUserDarkMode } from './actions' 4 | 5 | const currentTimestamp = () => new Date().getTime() 6 | 7 | export interface UserState { 8 | matchesDarkMode: boolean // whether the dark mode media query matches 9 | userDarkMode: boolean | null // the user's choice for dark mode or light mode 10 | 11 | // user defined slippage tolerance in percentages (userSlipperageTolerance of 80 means 80%) 12 | // TODO upgrade to a strongly typed version of this, similar to (but not exactly) like Uniswap's Percent type 13 | userSlippageTolerance: number | 'auto' 14 | 15 | timestamp: number 16 | } 17 | 18 | export const initialState: UserState = { 19 | matchesDarkMode: false, 20 | userDarkMode: true, 21 | userSlippageTolerance: 'auto', 22 | timestamp: currentTimestamp(), 23 | } 24 | 25 | export default createReducer(initialState, (builder) => 26 | builder 27 | .addCase(updateUserDarkMode, (state, action) => { 28 | state.userDarkMode = action.payload.userDarkMode 29 | state.timestamp = currentTimestamp() 30 | }) 31 | .addCase(updateMatchesDarkMode, (state, action) => { 32 | state.matchesDarkMode = action.payload.matchesDarkMode 33 | state.timestamp = currentTimestamp() 34 | }) 35 | .addCase(updateUserSlippageTolerance, (state, action) => { 36 | state.userSlippageTolerance = action.payload.userSlippageTolerance 37 | state.timestamp = currentTimestamp() 38 | }) 39 | ) 40 | -------------------------------------------------------------------------------- /src/components/InfoHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { Info } from 'components/Icons' 4 | import { Close as CloseIcon } from 'components/Icons' 5 | 6 | const Wrapper = styled.div<{ bg?: string }>` 7 | width: 100%; 8 | display: flex; 9 | justify-content: center; 10 | background: ${({ theme, bg }) => (bg ? (bg === 'gray' ? theme.text3 : bg) : theme.primary6)}; 11 | ` 12 | 13 | const Value = styled.div` 14 | font-weight: 600; 15 | font-size: 12px; 16 | line-height: 16px; 17 | margin: 8px 24px; 18 | ` 19 | 20 | const CloseIconWrapper = styled.button` 21 | position: absolute; 22 | padding: 5px; 23 | right: 25px; 24 | cursor: pointer; 25 | 26 | ${({ theme }) => theme.mediaWidth.upToSmall` 27 | right: 6px; 28 | `} 29 | ` 30 | 31 | const InfoIcon = styled(Info)` 32 | color: ${({ theme }) => theme.white}; 33 | margin-top: 6px; 34 | margin-right: -15px; 35 | cursor: default !important; 36 | 37 | ${({ theme }) => theme.mediaWidth.upToSmall` 38 | margin-left: 6px; 39 | `} 40 | ` 41 | 42 | export default function InfoHeader({ 43 | text, 44 | onClose, 45 | bg, 46 | hasInfoIcon, 47 | }: { 48 | text: string 49 | onClose: (status: boolean) => void 50 | bg?: string 51 | hasInfoIcon?: boolean 52 | }) { 53 | return ( 54 | 55 | {hasInfoIcon && } 56 | {text} 57 | onClose(false)}> 58 | 59 | 60 | 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /src/utils/time.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs' 2 | import utc from 'dayjs/plugin/utc' 3 | 4 | dayjs.extend(utc) 5 | 6 | export enum RoundMode { 7 | ROUND_DOWN, 8 | ROUND_UP, 9 | } 10 | 11 | export function getDurationSeconds(targetDate: Date, ROUND_MODE: RoundMode): number { 12 | const now = dayjs.utc().unix() 13 | const then = dayjs.utc(targetDate).unix() 14 | const result = then - now 15 | return ROUND_MODE === RoundMode.ROUND_DOWN ? Math.floor(result) : Math.ceil(result) 16 | } 17 | 18 | export function getRemainingTime(timeStamp: number): { 19 | diff: number 20 | day: number 21 | hours: number 22 | minutes: number 23 | seconds: number 24 | } { 25 | const now = dayjs().utc() 26 | const endTime = dayjs.utc(timeStamp) 27 | const diff = endTime.diff(now) 28 | 29 | const day = endTime.diff(now, 'day') 30 | const hours = dayjs.utc(diff).hour() 31 | const minutes = dayjs.utc(diff).minute() 32 | const seconds = dayjs.utc(diff).second() 33 | 34 | return { diff, day, hours, minutes, seconds } 35 | } 36 | 37 | export function getTimeLength(timeLength: number): { 38 | hours: string 39 | minutes: string 40 | seconds: string 41 | fullLength: string 42 | } { 43 | const hours = dayjs.utc(timeLength).hour() + ' hr' 44 | const minutes = dayjs.utc(timeLength).minute() + ' min' 45 | const seconds = dayjs.utc(timeLength).second() + ' sec' 46 | 47 | let fullLength = '' 48 | if (hours[0] !== '0') fullLength += hours 49 | if (minutes[0] !== '0') fullLength += minutes 50 | if (seconds[0] !== '0') fullLength += seconds 51 | 52 | return { hours, minutes, seconds, fullLength } 53 | } 54 | -------------------------------------------------------------------------------- /public/static/images/pages/clqdr/ic_clqdr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/Popups/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | import { useActivePopups } from 'state/application/hooks' 4 | import useWindowSize from 'hooks/useWindowSize' 5 | 6 | import PopupItem from './PopupItem' 7 | import { Z_INDEX } from 'theme' 8 | 9 | const Container = styled.div` 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: flex-end; 13 | position: fixed; 14 | height: auto; 15 | top: 75px; 16 | z-index: ${Z_INDEX.popover}; 17 | ` 18 | 19 | const ContainerLarge = styled(Container)` 20 | right: 30px; 21 | width: 300px; 22 | ` 23 | 24 | const ContainerSmall = styled(Container)` 25 | margin-left: 50%; 26 | transform: translateX(-50%); 27 | width: 90vw; 28 | ` 29 | 30 | export default function Popups() { 31 | const activePopups = useActivePopups() 32 | const { width } = useWindowSize() 33 | 34 | return ( 35 | <> 36 | {typeof width == 'number' && width >= 500 ? ( 37 | 38 | {activePopups.map((item) => { 39 | return ( 40 | 41 | ) 42 | })} 43 | 44 | ) : ( 45 | 46 | {activePopups // reverse so new items up front 47 | .slice(0) 48 | .reverse() 49 | .map((item) => ( 50 | 51 | ))} 52 | 53 | )} 54 | 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /src/components/ReviewModal/ThemeSelector.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react' 2 | import styled from 'styled-components' 3 | 4 | import ThemeIconDark from 'components/Icons/ThemeIconDark' 5 | import ThemeIconLight from 'components/Icons/ThemeIconLight' 6 | 7 | const Wrapper = styled.div` 8 | display: flex; 9 | align-items: center; 10 | justify-content: space-between; 11 | padding: 10px 20px; 12 | margin-top: -10px; 13 | ` 14 | 15 | const RightElement = styled.div` 16 | margin-right: 25px; 17 | cursor: pointer; 18 | 19 | &:hover { 20 | filter: brightness(0.75); 21 | } 22 | ` 23 | 24 | const HorizontalLine = styled.div` 25 | height: 1px; 26 | background: ${({ theme }) => theme.border2}; 27 | ` 28 | 29 | const Title = styled.div` 30 | font-weight: 400; 31 | color: ${({ theme }) => theme.text2}; 32 | ${({ theme }) => theme.mediaWidth.upToMedium` 33 | font-size:12px; 34 | `} 35 | ` 36 | 37 | export default function ThemeSelector({ 38 | title, 39 | amount, 40 | setAmount, 41 | }: { 42 | title: string 43 | amount: boolean 44 | setAmount: (value: boolean) => void 45 | }) { 46 | return useMemo(() => { 47 | return ( 48 | <> 49 | 50 | {title} 51 | { 53 | setAmount(!amount) 54 | }} 55 | > 56 | {amount ? : } 57 | 58 | 59 | 60 | 61 | ) 62 | }, [title, amount, setAmount]) 63 | } 64 | -------------------------------------------------------------------------------- /src/constants/wallet.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector' 2 | 3 | import { injected, walletconnect, walletlink } from '../connectors' 4 | 5 | interface WalletInfo { 6 | readonly connector?: AbstractConnector 7 | readonly name: string 8 | readonly iconURL: StaticImageData 9 | readonly description: string 10 | readonly color: string 11 | readonly href?: string 12 | readonly primary?: true 13 | readonly mobile?: true 14 | readonly mobileOnly?: true 15 | } 16 | 17 | export const SUPPORTED_WALLETS: { [key: string]: WalletInfo } = { 18 | INJECTED: { 19 | connector: injected, 20 | name: 'Injected', 21 | iconURL: require('/public/static/images/wallets/injected.svg'), 22 | description: 'Injected web3 provider.', 23 | color: '#010101', 24 | primary: true, 25 | }, 26 | METAMASK: { 27 | connector: injected, 28 | name: 'MetaMask', 29 | iconURL: require('/public/static/images/wallets/metamask.png'), 30 | description: 'Easy-to-use browser extension.', 31 | color: '#E8831D', 32 | }, 33 | WALLET_CONNECT: { 34 | connector: walletconnect, 35 | name: 'WalletConnect', 36 | iconURL: require('/public/static/images/wallets/walletConnect.png'), 37 | description: 'Connect to Trust Wallet, Rainbow Wallet and more...', 38 | color: '#4196FC', 39 | mobile: true, 40 | }, 41 | WALLET_LINK: { 42 | connector: walletlink, 43 | name: 'Coinbase Wallet', 44 | iconURL: require('/public/static/images/wallets/coinbaseWalletIcon.png'), 45 | description: 'Use Coinbase Wallet app on mobile device', 46 | color: '#315CF5', 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /public/static/images/LegacyDeiLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/constants/stakings.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '@sushiswap/core-sdk' 2 | import { BDEI_TOKEN, DEI_BDEI_LP_TOKEN } from 'constants/tokens' 3 | 4 | export type StakingType = { 5 | name: string 6 | pid: number 7 | token: Token 8 | provideLink?: string 9 | } 10 | 11 | export type vDeusStakingType = { 12 | id: number 13 | name: string 14 | pid: number 15 | apr: number 16 | lockDuration: number 17 | lpToken: string 18 | provideLink?: string 19 | } 20 | 21 | export const StakingPools: StakingType[] = [ 22 | { 23 | name: 'bDEI', 24 | pid: 0, 25 | token: BDEI_TOKEN, 26 | provideLink: '/bdei', 27 | }, 28 | { 29 | name: 'DEI-bDEI', 30 | pid: 1, 31 | token: DEI_BDEI_LP_TOKEN, 32 | provideLink: '/bdei', 33 | }, 34 | ] 35 | 36 | export type UserDeposit = { 37 | nftId: number 38 | poolId: number 39 | amount: number 40 | depositTimestamp: number 41 | isWithdrawn: boolean 42 | isExited: boolean 43 | } 44 | 45 | export const vDeusStakingPools: vDeusStakingType[] = [ 46 | { 47 | id: 0, 48 | name: '3 Months', 49 | pid: 0, 50 | apr: 10, 51 | lpToken: '0x24651a470D08009832d62d702d1387962A2E5d60', 52 | lockDuration: 180, 53 | provideLink: '/redeem', 54 | }, 55 | { 56 | id: 1, 57 | name: '6 Months', 58 | pid: 1, 59 | lpToken: '0x65875f75d5CDEf890ea97ADC43E216D3f0c2b2D8', 60 | apr: 20, 61 | lockDuration: 360, 62 | provideLink: '/redeem', 63 | }, 64 | { 65 | id: 2, 66 | name: '1 Year', 67 | pid: 2, 68 | lpToken: '0xCf18eCa0EaC101eb47828BFd460D1922000213db', 69 | apr: 40, 70 | lockDuration: 720, 71 | provideLink: '/redeem', 72 | }, 73 | ] 74 | -------------------------------------------------------------------------------- /public/static/images/tokens/deus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/static/images/tokens/usdc.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/Icons/Redeem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Redeem({ size = 8, ...rest }: { size?: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /src/hooks/useCurrencyLogo.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | 3 | import NotFound from '/public/static/images/fallback/not_found.png' 4 | import DEI_LOGO from '/public/static/images/tokens/dei.svg' 5 | import DEUS_LOGO from '/public/static/images/tokens/deus.svg' 6 | import USDC_LOGO from '/public/static/images/tokens/usdc.svg' 7 | import BDEI_LOGO from '/public/static/images/tokens/bdei.svg' 8 | import LQDR_ICON from '/public/static/images/tokens/lqdr.svg' 9 | import CLQDR_ICON from '/public/static/images/tokens/clqdr.svg' 10 | 11 | const LogoMap: { [contractOrSymbol: string]: string } = { 12 | // symbols 13 | FTM: 'https://assets.spooky.fi/tokens/FTM.png', 14 | DEI: DEI_LOGO, 15 | DEUS: DEUS_LOGO, 16 | // contracts 17 | // make sure these values are checksummed! https://ethsum.netlify.app/ 18 | '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83': 'https://assets.spooky.fi/tokens/wFTM.png', // wFTM 19 | '0x04068DA6C83AFCFA0e13ba15A6696662335D5B75': USDC_LOGO, 20 | '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8': USDC_LOGO, 21 | '0xDE1E704dae0B4051e80DAbB26ab6ad6c12262DA0': DEI_LOGO, 22 | '0xDE5ed76E7c05eC5e4572CfC88d1ACEA165109E44': DEUS_LOGO, 23 | '0x05f6ea7F80BDC07f6E0728BbBBAbebEA4E142eE8': BDEI_LOGO, 24 | '0x10b620b2dbAC4Faa7D7FFD71Da486f5D44cd86f9': LQDR_ICON, 25 | '0x814c66594a22404e101FEcfECac1012D8d75C156': CLQDR_ICON, 26 | usdc: USDC_LOGO, 27 | deus: DEUS_LOGO, 28 | } 29 | 30 | export default function useCurrencyLogo(contractOrSymbol?: string): string { 31 | return useMemo(() => { 32 | try { 33 | if (contractOrSymbol && contractOrSymbol in LogoMap) { 34 | return LogoMap[contractOrSymbol] 35 | } 36 | return `https://assets.spooky.fi/tokens/${contractOrSymbol}.png` 37 | } catch (err) { 38 | return NotFound.src 39 | } 40 | }, [contractOrSymbol]) 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Icons/Error.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Error({ size = 12, ...rest }: { size?: number; [x: string]: any }) { 4 | return ( 5 | 6 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Icons/Mint.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Mint({ size, ...rest }: { size: number; [x: string]: any }) { 4 | return ( 5 | 6 | 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/components/InvalidContext/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | 3 | import { useWalletModalToggle } from 'state/application/hooks' 4 | import { useSupportedChainId } from 'hooks/useSupportedChainId' 5 | import useRpcChangerCallback from 'hooks/useRpcChangerCallback' 6 | import useWeb3React from 'hooks/useWeb3' 7 | 8 | import { PrimaryButton } from 'components/Button' 9 | import { SupportedChainId } from 'constants/chains' 10 | 11 | export enum ContextError { 12 | ACCOUNT, 13 | CHAIN_ID, 14 | VALID, 15 | } 16 | 17 | export function useInvalidContext() { 18 | const { chainId, account } = useWeb3React() 19 | const isSupportedChainId = useSupportedChainId() 20 | return useMemo( 21 | () => 22 | !account || !chainId ? ContextError.ACCOUNT : !isSupportedChainId ? ContextError.CHAIN_ID : ContextError.VALID, 23 | [account, chainId, isSupportedChainId] 24 | ) 25 | } 26 | 27 | export function InvalidContext({ connectText }: { connectText?: string }) { 28 | const invalidContext = useInvalidContext() 29 | const toggleWalletModal = useWalletModalToggle() 30 | const rpcChangerCallback = useRpcChangerCallback() 31 | 32 | return useMemo(() => { 33 | if (invalidContext === ContextError.ACCOUNT) { 34 | return ( 35 | <> 36 |
{connectText ?? 'Connect your Wallet'}
37 | Connect Wallet 38 | 39 | ) 40 | } 41 | if (invalidContext === ContextError.CHAIN_ID) { 42 | return ( 43 | <> 44 |
You are not connected to the Fantom Opera Network.
45 | rpcChangerCallback(SupportedChainId.FANTOM)}>Switch to Fantom 46 | 47 | ) 48 | } 49 | return null 50 | }, [invalidContext, connectText, rpcChangerCallback, toggleWalletModal]) 51 | } 52 | -------------------------------------------------------------------------------- /src/components/ReviewModal/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | import { OptionButton } from 'components/Button' 4 | 5 | export const DefaultOptionButton = styled(OptionButton)` 6 | display: inline-flex; 7 | margin: 1px; 8 | margin-right: 10px; 9 | 10 | ${({ theme }) => theme.mediaWidth.upToSmall` 11 | margin-right: 5px; 12 | white-space: normal; 13 | `} 14 | ` 15 | 16 | export const CustomOption = styled(DefaultOptionButton)` 17 | justify-content: flex-end; 18 | width: 85px; 19 | margin-right: 0px; 20 | padding: 0 5px; 21 | border-radius: 8px; 22 | ` 23 | 24 | export const InputAmount = styled.input.attrs({ type: 'number' })<{ active?: boolean }>` 25 | color: ${({ theme }) => theme.text1}; 26 | border: 0; 27 | outline: none; 28 | width: 100%; 29 | margin-right: 2px; 30 | margin-left: 2px; 31 | font-size: 0.95rem; 32 | background: transparent; 33 | ${({ active, theme }) => 34 | active && 35 | ` 36 | color: ${theme.text2}; 37 | `} 38 | ` 39 | 40 | export const AmountsWrapper = styled.div<{ hasCustom?: boolean }>` 41 | display: flex; 42 | flex-wrap: nowrap; 43 | justify-content: space-between; 44 | margin-top: 16px; 45 | ` 46 | 47 | export const AmountsInnerWrapper = styled.div<{ hasCustom?: boolean }>` 48 | ${({ hasCustom, theme }) => 49 | !hasCustom && 50 | theme.mediaWidth.upToSmall` 51 | width: 100%; 52 | display: flex; 53 | flex-wrap: nowrap; 54 | justify-content: space-between; 55 | `} 56 | ` 57 | 58 | export const QuestionMarkWrap = styled.div` 59 | margin-left: 6px; 60 | display: inline; 61 | background: transparent; 62 | ` 63 | 64 | export const Title = styled.div` 65 | font-weight: 400; 66 | color: ${({ theme }) => theme.text2}; 67 | display: flex; 68 | direction: row; 69 | justify-content: space-between; 70 | 71 | ${({ theme }) => theme.mediaWidth.upToMedium` 72 | font-size: 12px; 73 | `} 74 | ` 75 | -------------------------------------------------------------------------------- /src/hooks/useRpcChangerCallback.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | 3 | import useWeb3React from './useWeb3' 4 | import { ChainInfo } from 'constants/chainInfo' 5 | import { SupportedChainId } from 'constants/chains' 6 | 7 | export default function useRpcChangerCallback() { 8 | const { account, chainId, library } = useWeb3React() 9 | 10 | return useCallback( 11 | async (targetChainId: SupportedChainId) => { 12 | if (!chainId) return false 13 | if (!targetChainId || !ChainInfo[targetChainId]) return false 14 | if (targetChainId === chainId) return true 15 | if (!window.ethereum) return false 16 | 17 | try { 18 | await library?.send('wallet_switchEthereumChain', [{ chainId: ChainInfo[targetChainId].chainId }]) 19 | return true 20 | } catch (switchError) { 21 | // This error code indicates that the chain has not been added to MetaMask. 22 | if (switchError.code === 4902) { 23 | try { 24 | const params = { 25 | chainId: ChainInfo[targetChainId].chainId, 26 | chainName: ChainInfo[targetChainId].chainName, 27 | nativeCurrency: ChainInfo[targetChainId].nativeCurrency, 28 | rpcUrls: [ChainInfo[targetChainId].rpcUrl], 29 | } 30 | await library?.send('wallet_addEthereumChain', [params, account]) 31 | return true 32 | } catch (addError) { 33 | console.log('Something went wrong trying to add a new network RPC: ') 34 | console.error(addError) 35 | return false 36 | } 37 | } 38 | // handle other "switch" errors 39 | console.log('Unknown error occured when trying to change the network RPC: ') 40 | console.error(switchError) 41 | return false 42 | } 43 | // eslint-disable-next-line react-hooks/exhaustive-deps 44 | }, 45 | [chainId, library, account] 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /public/static/images/footer/Discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { Provider as ReduxProvider } from 'react-redux' 2 | import { Web3ReactProvider } from '@web3-react/core' 3 | import { ModalProvider } from 'styled-react-modal' 4 | import dynamic from 'next/dynamic' 5 | import type { AppProps } from 'next/app' 6 | import { Toaster } from 'react-hot-toast' 7 | 8 | import Web3ReactManager from '../components/Web3ReactManager' 9 | import ThemeProvider, { ThemedGlobalStyle } from '../theme' 10 | import Popups from '../components/Popups' 11 | import Layout from '../components/Layout' 12 | import { ModalBackground } from '../components/Modal' 13 | import { useAnalyticsReporter } from '../components/analytics' 14 | import LiveChat from 'components/LiveChat' 15 | 16 | import store from '../state' 17 | import { getLibrary } from '../utils/library' 18 | 19 | const Updaters = dynamic(() => import('../state/updaters'), { ssr: false }) 20 | const Web3ProviderNetwork = dynamic(() => import('../components/Web3ProviderNetwork'), { 21 | ssr: false, 22 | }) 23 | 24 | if (typeof window !== 'undefined' && !!window.ethereum) { 25 | window.ethereum.autoRefreshOnNetworkChange = false 26 | } 27 | 28 | export default function MyApp({ Component, pageProps }: AppProps) { 29 | useAnalyticsReporter() 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /src/theme/styled.d.ts: -------------------------------------------------------------------------------- 1 | import { ThemedCssFunction } from 'styled-components/macro' 2 | import { SupportedThemes } from 'theme' 3 | 4 | export type Color = string 5 | export interface Colors { 6 | themeName: SupportedThemes 7 | 8 | // base 9 | white: Color 10 | black: Color 11 | 12 | // text 13 | text1: Color 14 | text2: Color 15 | text3: Color 16 | text4: Color 17 | 18 | // backgrounds 19 | bg0: Color 20 | bg1: Color 21 | bg2: Color 22 | bg3: Color 23 | bg4: Color 24 | bg5: Color 25 | 26 | // borders 27 | border1: Color 28 | border2: Color 29 | border3: Color 30 | 31 | specialBG1: Color 32 | specialBG2: Color 33 | 34 | //blues 35 | primary1: Color 36 | primary2: Color 37 | primary3: Color 38 | primary4: Color 39 | primary5: Color 40 | primary6: Color 41 | primary7: Color 42 | 43 | primaryText1: Color 44 | 45 | // pinks 46 | secondary1: Color 47 | secondary2: Color 48 | 49 | // other 50 | red1: Color 51 | red2: Color 52 | red3: Color 53 | green1: Color 54 | yellow1: Color 55 | yellow2: Color 56 | yellow3: Color 57 | yellow4: Color 58 | blue1: Color 59 | blue2: Color 60 | darkPink: Color 61 | 62 | error: Color 63 | success: Color 64 | warning: Color 65 | 66 | deusColor: Color 67 | deusColorReverse: Color 68 | deiColor: Color 69 | cLqdrColor: Color 70 | deiPrimaryColor: Color 71 | deiSecondaryColor: Color 72 | } 73 | 74 | export type Shadow = string 75 | export interface Shadows { 76 | shadow1: Shadow 77 | boxShadow1: Shadow 78 | boxShadow2: Shadow 79 | } 80 | 81 | declare module 'styled-components' { 82 | export interface DefaultTheme extends Colors, Shadows { 83 | grids: Grids 84 | 85 | // media queries 86 | mediaWidth: { 87 | upToExtraSmall: ThemedCssFunction 88 | upToSmall: ThemedCssFunction 89 | upToMedium: ThemedCssFunction 90 | upToLarge: ThemedCssFunction 91 | upToExtraLarge: ThemedCssFunction 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/components/Pagination/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import ReactPaginate from 'react-paginate' 4 | 5 | const Wrapper = styled.div` 6 | margin: 0.8rem auto; 7 | font-size: 0.8rem; 8 | .pagination { 9 | display: flex; 10 | justify-content: center; 11 | list-style-type: none; 12 | align-items: flex-end; 13 | margin: 0; 14 | padding: 0; 15 | overflow: hidden; 16 | & > li { 17 | float: left; 18 | } 19 | & > li a { 20 | display: block; 21 | text-align: bottom; 22 | padding: 1rem; 23 | text-decoration: none; 24 | :hover { 25 | cursor: pointer; 26 | } 27 | ${({ theme }) => theme.mediaWidth.upToSmall` 28 | padding: 1rem 0.8rem; 29 | `} 30 | } 31 | } 32 | .break { 33 | pointer-events: none; 34 | } 35 | .active { 36 | & > * { 37 | font-size: 1rem; 38 | font-weight: 700; 39 | background: ${({ theme }) => theme.bg1}; 40 | color: ${({ theme }) => theme.darkPink}; 41 | } 42 | } 43 | ${({ theme }) => theme.mediaWidth.upToSmall` 44 | font-size: 0.7rem; 45 | `} 46 | ` 47 | 48 | export default function Pagination({ 49 | pageCount, 50 | onPageChange, 51 | count, 52 | }: { 53 | pageCount: number 54 | onPageChange: ({ selected }: { selected: number }) => void 55 | count: number 56 | }) { 57 | return ( 58 | 59 | '} 62 | breakLabel={''} 63 | breakClassName={'break'} 64 | pageCount={pageCount} 65 | marginPagesDisplayed={0} // how much to show at the beginning and end (using 2) => Previous 1, 2, .. , 9, 10 Next 66 | pageRangeDisplayed={4} // how much to show left and right from the current page (using 2) => Previous 1, 2 .. 9 10 (11) 12 13 ... 23 24 Next 67 | onPageChange={onPageChange} 68 | containerClassName={'pagination'} 69 | activeClassName={'active'} 70 | /> 71 | 72 | ) 73 | } 74 | -------------------------------------------------------------------------------- /src/components/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { useInjectedAddress } from 'hooks/useInjectedAddress' 5 | 6 | import NavBar from './NavBar' 7 | import Warning from './Warning' 8 | import Footer from 'components/Disclaimer' 9 | 10 | const Wrapper = styled.div` 11 | display: flex; 12 | height: 100%; 13 | flex-flow: column nowrap; 14 | ` 15 | 16 | const Content = styled.div` 17 | position: relative; 18 | min-height: calc(970px - 55px - 67px); 19 | overflow: scroll; 20 | padding-bottom: 50px; 21 | 22 | ${({ theme }) => theme.mediaWidth.upToSmall` 23 | padding-bottom: 30px; 24 | height:100%; 25 | `} 26 | 27 | @media screen and (min-height: 1040px) { 28 | height: calc(100vh - 55px - 60px); 29 | } 30 | ` 31 | 32 | export default function Layout({ children }: { children: React.ReactNode }) { 33 | const hasInjected = useInjectedAddress() 34 | 35 | return ( 36 | 37 | 38 | {hasInjected && ( 39 | {`❌ You are in "READ-ONLY" mode. Please do not confirm any transactions! ❌ `} 40 | )} 41 | {/* 42 | <> 43 | The DEUS team is actively monitoring the{' '} 44 | 48 | USDC situation 49 | 50 | . We started to actively diversify USDC backing of DEI into multiple stables. For more info, read the 51 | announcement on{' '} 52 | 56 | Discord 57 | 58 | . 59 | 60 | */} 61 | {children} 62 |