├── static.json ├── .prettierignore ├── src ├── state │ ├── actions.ts │ ├── swap │ │ ├── constants.ts │ │ ├── types.ts │ │ └── actions.ts │ ├── mint │ │ ├── actions.ts │ │ └── reducer.ts │ ├── global │ │ └── actions.ts │ ├── multicall │ │ └── chunkArray.ts │ ├── block │ │ ├── index.ts │ │ └── hooks.ts │ ├── user │ │ ├── actions.ts │ │ └── hooks │ │ │ ├── useUserAddedTokens.ts │ │ │ └── helpers.ts │ ├── transactions │ │ └── actions.ts │ ├── lists │ │ └── actions.ts │ └── types.ts ├── components │ ├── ErrorBoundary │ │ ├── index.ts │ │ └── ErrorBoundary.tsx │ ├── Skeleton │ │ ├── index.tsx │ │ ├── types.ts │ │ └── Skeleton.tsx │ ├── WalletModal │ │ ├── index.tsx │ │ ├── types.ts │ │ ├── config.tsx │ │ ├── wallet-list.tsx │ │ ├── WalletCard.tsx │ │ └── TransactionRow.tsx │ ├── Alert │ │ ├── index.tsx │ │ └── types.ts │ ├── Toast │ │ ├── index.tsx │ │ ├── types.ts │ │ ├── DescriptionWithTx.tsx │ │ └── ToastContainer.tsx │ ├── ui │ │ ├── animated-background.tsx │ │ ├── label.tsx │ │ ├── custom-hero.tsx │ │ ├── section-header.tsx │ │ ├── separator.tsx │ │ ├── close-icon.tsx │ │ ├── switch.tsx │ │ ├── tooltip.tsx │ │ └── scroll-area.tsx │ ├── Layout │ │ ├── Column.tsx │ │ └── Row.tsx │ ├── main-navigation │ │ └── hamburger.tsx │ ├── footer │ │ └── footer.tsx │ ├── Loader │ │ └── CircleLoader.tsx │ ├── socials.tsx │ ├── NotFound.tsx │ └── swap │ │ ├── NumericalInput.tsx │ │ └── sorting.ts ├── config │ ├── constants │ │ ├── endpoints.ts │ │ ├── networks.ts │ │ ├── swapWarningTokens.ts │ │ ├── lists.ts │ │ ├── tokenLists │ │ │ └── scads-unsupported.tokenlist.json │ │ ├── types.ts │ │ └── contracts.ts │ ├── abi │ │ ├── erc20.ts │ │ └── erc20_bytes32.json │ ├── localization │ │ └── languages.ts │ └── index.ts ├── utils │ ├── calls │ │ ├── index.ts │ │ ├── pulse.ts │ │ ├── scadsMint.ts │ │ ├── usdPrice.ts │ │ └── caratMint.ts │ ├── getTokenLogoURL.ts │ ├── isZero.ts │ ├── providers.ts │ ├── getCurrencyLogoURL.ts │ ├── truncateHash.ts │ ├── getLocalStorageItemKeys.ts │ ├── slugify.ts │ ├── ENS │ │ └── parseENSAddress.ts │ ├── bigNumber.ts │ ├── getGasPrice.ts │ ├── types.ts │ ├── maxAmountSpend.ts │ ├── clearUserStates.ts │ ├── wrappedCurrency.ts │ ├── uriToHttp.ts │ ├── getRpcUrl.ts │ ├── sentry.ts │ ├── trades.ts │ ├── addressHelpers.ts │ ├── wagmi.ts │ ├── contenthashToUri.ts │ └── getTokenList.ts ├── contexts │ ├── ToastsContext │ │ ├── index.tsx │ │ ├── types.ts │ │ └── Listener.tsx │ ├── Localization │ │ ├── index.tsx │ │ ├── useTranslation.ts │ │ ├── types.ts │ │ └── helpers.ts │ ├── ToasterProvider.tsx │ └── RefreshContext.tsx ├── pages │ ├── 404.tsx │ ├── index.tsx │ ├── _components │ │ ├── wallets.tsx │ │ ├── stats │ │ │ └── stats.tsx │ │ ├── roadmap │ │ │ ├── roadmap-item.tsx │ │ │ └── roadmap.tsx │ │ └── outro.tsx │ ├── whitepaper │ │ └── _components │ │ │ └── contact.tsx │ ├── tokenomics │ │ └── _components │ │ │ └── tokenomics-card.tsx │ ├── globals.css │ └── faq │ │ └── index.tsx ├── lib │ └── utils.ts ├── hooks │ ├── useParsedQueryString.ts │ ├── useUserAgent.ts │ ├── useRefresh.ts │ ├── useToast.ts │ ├── useSentryUser.ts │ ├── useTheme.ts │ ├── useCurrentBlockTimestamp.ts │ ├── usePreviousValue.ts │ ├── useActiveWeb3React.ts │ ├── useLastUpdated.ts │ ├── useProviderOrSigner.ts │ ├── useOutsideClick.tsx │ ├── useEagerConnect.ts │ ├── useTokenAllowance.ts │ ├── useTotalSupply.ts │ ├── useTransactionDeadline.ts │ ├── useInterval.ts │ ├── useHttpLocations.ts │ ├── useDebounce.ts │ ├── useScadsMint.tsx │ ├── ENS │ │ ├── useENS.ts │ │ ├── useENSContentHash.ts │ │ ├── useENSAddress.ts │ │ └── useENSName.ts │ ├── useIsWindowVisible.ts │ ├── useAccountEventListener.ts │ ├── useCaratMint.tsx │ ├── useFetchListCallback.ts │ └── useAuth.ts ├── react-app-env.d.ts ├── __tests__ │ ├── config │ │ ├── contracts.test.ts │ │ └── tokens.test.ts │ └── utils │ │ ├── addressHelpers.test.ts │ │ ├── truncateHash.test.ts │ │ ├── uriToHttp.test.ts │ │ └── prices.test.ts ├── index.tsx └── Providers.tsx ├── cypress ├── .eslintrc ├── support │ ├── commands.d.ts │ └── index.js ├── tsconfig.json └── integration │ └── home │ └── home.test.ts ├── public ├── logo.webp ├── favicon.ico ├── robots.txt ├── images │ ├── formula.png │ ├── whitepaper │ │ ├── poh.webp │ │ ├── pulse.webp │ │ ├── scads.webp │ │ └── twine.webp │ ├── binance.svg │ └── wallet-connect.svg └── manifest.json ├── packages ├── uikit │ ├── src │ │ ├── setupTests.js │ │ ├── types │ │ │ └── index.d.ts │ │ ├── components │ │ │ ├── Link │ │ │ │ ├── index.tsx │ │ │ │ ├── types.ts │ │ │ │ └── Link.tsx │ │ │ ├── Heading │ │ │ │ ├── index.tsx │ │ │ │ ├── types.ts │ │ │ │ ├── index.stories.tsx │ │ │ │ └── Heading.tsx │ │ │ ├── Text │ │ │ │ ├── index.tsx │ │ │ │ ├── TooltipText.tsx │ │ │ │ ├── types.ts │ │ │ │ └── Text.tsx │ │ │ ├── Box │ │ │ │ ├── index.tsx │ │ │ │ ├── Flex.tsx │ │ │ │ ├── Box.tsx │ │ │ │ ├── types.ts │ │ │ │ └── index.stories.tsx │ │ │ ├── Stepper │ │ │ │ ├── index.ts │ │ │ │ ├── types.ts │ │ │ │ └── Stepper.tsx │ │ │ ├── Button │ │ │ │ ├── index.tsx │ │ │ │ ├── IconButton.tsx │ │ │ │ ├── theme.ts │ │ │ │ ├── Button.tsx │ │ │ │ └── types.ts │ │ │ └── Svg │ │ │ │ ├── types.ts │ │ │ │ ├── Icons │ │ │ │ ├── Block.tsx │ │ │ │ ├── Info.tsx │ │ │ │ ├── Error.tsx │ │ │ │ ├── CheckmarkCircle.tsx │ │ │ │ ├── Close.tsx │ │ │ │ ├── OpenNew.tsx │ │ │ │ ├── Refresh.tsx │ │ │ │ └── WalletConnect.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── Svg.tsx │ │ ├── util │ │ │ ├── getExternalLinkProps.ts │ │ │ └── getThemeValue.ts │ │ ├── styled.d.ts │ │ ├── theme │ │ │ ├── dark.ts │ │ │ ├── light.ts │ │ │ ├── index.ts │ │ │ ├── base.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ ├── __tests__ │ │ │ └── components │ │ │ │ ├── flex.test.tsx │ │ │ │ ├── text.test.tsx │ │ │ │ ├── heading.test.tsx │ │ │ │ └── svg.test.tsx │ │ └── testHelpers.tsx │ ├── babel.config.js │ ├── .storybook │ │ ├── preview-head.html │ │ ├── main.js │ │ └── preview.js │ ├── svgTransform.js │ ├── jest.config.js │ ├── rollup.config.js │ ├── tsconfig.json │ ├── README.md │ └── package.json ├── wagmi │ ├── src │ │ ├── index.ts │ │ ├── useWeb3React.ts │ │ ├── hooks │ │ │ └── useSignMessage.ts │ │ └── provider.tsx │ ├── tsconfig.json │ └── package.json ├── tsconfig │ ├── package.json │ ├── react-library.json │ ├── nextjs.json │ └── base.json └── eslint-config │ ├── README.md │ ├── package.json │ └── lib │ └── index.js ├── .prettierrc ├── postcss.config.js ├── crowdin.yml ├── sentry.properties ├── netlify.toml ├── README.md ├── .env.test ├── cypress.json ├── next-env.d.ts ├── .commitlintrc.json ├── tsconfig.json ├── doc └── Tokens.md ├── .env ├── .env.development ├── .env.production ├── jest.config.js ├── .gitignore ├── sentry.server.config.js ├── sentry.edge.config.js ├── tailwind.config.ts ├── sentry.client.config.js └── .eslintrc /static.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "build/" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | 4 | .next -------------------------------------------------------------------------------- /src/state/actions.ts: -------------------------------------------------------------------------------- 1 | export { setBlock } from './block' 2 | -------------------------------------------------------------------------------- /cypress/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended"] 3 | } 4 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ErrorBoundary' -------------------------------------------------------------------------------- /public/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/logo.webp -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/images/formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/images/formula.png -------------------------------------------------------------------------------- /src/config/constants/endpoints.ts: -------------------------------------------------------------------------------- 1 | export const API_PROFILE = process.env.NEXT_PUBLIC_API_PROFILE 2 | -------------------------------------------------------------------------------- /packages/uikit/src/setupTests.js: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | import "jest-styled-components"; 3 | -------------------------------------------------------------------------------- /src/utils/calls/index.ts: -------------------------------------------------------------------------------- 1 | export * from './scadsMint' 2 | export * from './caratMint' 3 | export * from './pulse' 4 | -------------------------------------------------------------------------------- /public/images/whitepaper/poh.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/images/whitepaper/poh.webp -------------------------------------------------------------------------------- /public/images/whitepaper/pulse.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/images/whitepaper/pulse.webp -------------------------------------------------------------------------------- /public/images/whitepaper/scads.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/images/whitepaper/scads.webp -------------------------------------------------------------------------------- /public/images/whitepaper/twine.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scads-io/frontend/HEAD/public/images/whitepaper/twine.webp -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "semi": false, 4 | "singleQuote": true, 5 | "printWidth": 120 6 | } 7 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } -------------------------------------------------------------------------------- /src/contexts/ToastsContext/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Provider' 2 | export { default as ToastListener } from './Listener' 3 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /src/config/localization/translations.json 3 | translation: /public/locales/%locale%.json 4 | -------------------------------------------------------------------------------- /packages/uikit/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const content: string; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/Skeleton/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Skeleton } from "./Skeleton" 2 | export type { SkeletonProps } from "./types" -------------------------------------------------------------------------------- /src/contexts/Localization/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Provider' 2 | export { default as useTranslation } from './useTranslation' 3 | -------------------------------------------------------------------------------- /cypress/support/commands.d.ts: -------------------------------------------------------------------------------- 1 | export const TEST_ADDRESS_NEVER_USE: string 2 | 3 | export const TEST_ADDRESS_NEVER_USE_SHORTENED: string -------------------------------------------------------------------------------- /packages/uikit/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@babel/preset-env"], 3 | plugins: ["styled-components"], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Link/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Link } from "./Link"; 2 | export type { LinkProps } from "./types"; 3 | -------------------------------------------------------------------------------- /packages/wagmi/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './provider' 2 | export * from './useWeb3React' 3 | export * from './hooks/useSignMessage' 4 | -------------------------------------------------------------------------------- /packages/uikit/.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/wagmi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig/react-library.json", 3 | "compilerOptions": { 4 | "jsx": "react", 5 | "target": "ES2015" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/WalletModal/index.tsx: -------------------------------------------------------------------------------- 1 | export { connectorLocalStorageKey } from "./config" 2 | export { ConnectorNames } from "./types" 3 | export type { Login } from "./types" 4 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NotFound from '../components/NotFound' 3 | 4 | const NotFoundPage = () => 5 | 6 | export default NotFoundPage -------------------------------------------------------------------------------- /packages/uikit/svgTransform.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | process() { 3 | return "module.exports = {};"; 4 | }, 5 | getCacheKey() { 6 | return "svgTransform"; 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/state/swap/constants.ts: -------------------------------------------------------------------------------- 1 | // BNB 2 | export const DEFAULT_INPUT_CURRENCY = 'BNB' 3 | // BUSD 4 | export const DEFAULT_OUTPUT_CURRENCY = '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' 5 | -------------------------------------------------------------------------------- /sentry.properties: -------------------------------------------------------------------------------- 1 | defaults.url=https://sentry.io/ 2 | defaults.org=scads 3 | defaults.project=scads 4 | cli.executable=../../../.npm/_npx/a8388072043b4cbc/node_modules/@sentry/cli/bin/sentry-cli -------------------------------------------------------------------------------- /packages/uikit/src/components/Heading/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Heading } from "./Heading"; 2 | export type { HeadingProps, Scales as HeadingScales, Tags as HeadingTags } from "./types"; 3 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Text/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Text } from "./Text"; 2 | export { default as TooltipText } from "./TooltipText"; 3 | export type { TextProps } from "./types"; 4 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Box/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Box } from "./Box"; 2 | export { default as Flex } from "./Flex"; 3 | export type { BoxProps, FlexProps, GridProps } from "./types"; 4 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Stepper/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Stepper } from "./Stepper"; 2 | export { Step } from "./Step"; 3 | export type { Status as StepStatus, StepProps } from "./types"; 4 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[plugins]] 2 | package = "@netlify/plugin-nextjs" 3 | 4 | [build] 5 | publish = ".next" 6 | [context.deploy-preview] 7 | ignore = "if [[ $HEAD == *feature/* ]]; then exit 1; else exit 0; fi" -------------------------------------------------------------------------------- /src/components/Alert/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Alert } from "./Alert" 2 | export { variants as alertVariants } from "./types" 3 | export type { AlertProps, Variants as AlertVariants } from "./types" 4 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/getTokenLogoURL.ts: -------------------------------------------------------------------------------- 1 | const getTokenLogoURL = (address: string) => 2 | `https://assets.trustwalletapp.com/blockchains/smartchain/assets/${address}/logo.png` 3 | 4 | export default getTokenLogoURL 5 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es5", 5 | "lib": ["es5", "dom"], 6 | "types": ["cypress"] 7 | }, 8 | "include": ["**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scads Frontend 2 | 3 | This project contains the main features of the Scads application. 4 | 5 | If you want to contribute, please refer to the [contributing guidelines](./CONTRIBUTING.md) of this project. 6 | -------------------------------------------------------------------------------- /src/contexts/ToasterProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Toaster } from "react-hot-toast" 3 | 4 | const ToasterProvider = () => { 5 | return 6 | } 7 | 8 | export default ToasterProvider 9 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_CHAIN_ID = "56" 2 | 3 | NEXT_PUBLIC_NODE_1 = "https://bsc-dataseed1.ninicoin.io" 4 | NEXT_PUBLIC_NODE_2 = "https://bsc-dataseed1.ninicoin.io" 5 | NEXT_PUBLIC_NODE_3 = "https://bsc-dataseed1.ninicoin.io" 6 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "pluginsFile": false, 4 | "fixturesFolder": false, 5 | "supportFile": "cypress/support/index.js", 6 | "video": false, 7 | "defaultCommandTimeout": 10000 8 | } 9 | -------------------------------------------------------------------------------- /packages/uikit/src/util/getExternalLinkProps.ts: -------------------------------------------------------------------------------- 1 | const getExternalLinkProps = (): { target: string; rel: string } => ({ 2 | target: "_blank", 3 | rel: "noreferrer noopener", 4 | }) 5 | 6 | export default getExternalLinkProps 7 | -------------------------------------------------------------------------------- /src/utils/isZero.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns true if the string value is zero in hex 3 | * @param hexNumberString 4 | */ 5 | export default function isZero(hexNumberString: string) { 6 | return /^0x0*$/.test(hexNumberString) 7 | } 8 | -------------------------------------------------------------------------------- /packages/wagmi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@scads-io/wagmi", 3 | "version": "1.0.0", 4 | "description": "wagmi adaptor for scads", 5 | "main": "src/index.ts", 6 | "devDependencies": { 7 | "tsconfig": "*" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/hooks/useParsedQueryString.ts: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router' 2 | import { ParsedUrlQuery } from 'querystring' 3 | 4 | export default function useParsedQueryString(): ParsedUrlQuery { 5 | const { query } = useRouter() 6 | return query 7 | } -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Link/types.ts: -------------------------------------------------------------------------------- 1 | import { AnchorHTMLAttributes } from "react"; 2 | import { TextProps } from "../Text"; 3 | 4 | export interface LinkProps extends TextProps, AnchorHTMLAttributes { 5 | external?: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/providers.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers' 2 | import getRpcUrl from 'utils/getRpcUrl' 3 | 4 | const RPC_URL = getRpcUrl() 5 | 6 | export const simpleRpcProvider = new ethers.providers.StaticJsonRpcProvider(RPC_URL) 7 | 8 | export default null 9 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Button } from "./Button"; 2 | export { default as IconButton } from "./IconButton"; 3 | export type { ButtonProps, BaseButtonProps, Scale as ButtonScale, Variant as ButtonVariant } from "./types"; 4 | -------------------------------------------------------------------------------- /src/hooks/useUserAgent.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | 3 | const useUserAgent = () => { 4 | useEffect(() => { 5 | document.documentElement.setAttribute('data-useragent', navigator.userAgent) 6 | }, []) 7 | } 8 | 9 | export default useUserAgent 10 | -------------------------------------------------------------------------------- /packages/uikit/src/styled.d.ts: -------------------------------------------------------------------------------- 1 | import "styled-components"; 2 | import { ScadsTheme } from "./theme"; 3 | 4 | declare module "styled-components" { 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | export interface DefaultTheme extends ScadsTheme {} 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/getCurrencyLogoURL.ts: -------------------------------------------------------------------------------- 1 | const getCurrencyLogoURL = (symbol: string) => { 2 | if (symbol === 'Scads') { 3 | return 'https://svgshare.com/i/zMp.svg' 4 | } 5 | return 'https://svgshare.com/i/rur.svg' 6 | } 7 | 8 | 9 | export default getCurrencyLogoURL 10 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "main": "index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "files": [ 10 | "base.json", 11 | "nextjs.json" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Toast/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as ToastContainer } from './ToastContainer' 2 | export { types as toastTypes } from './types' 3 | export { default as ToastDescriptionWithTx } from './DescriptionWithTx' 4 | export type { ToastContainerProps, Toast, Types as ToastTypes } from './types' 5 | -------------------------------------------------------------------------------- /src/utils/truncateHash.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Truncate a transaction or address hash 3 | */ 4 | const truncateHash = (address: string, startLength = 4, endLength = 4) => { 5 | return `${address.substring(0, startLength)}...${address.substring(address.length - endLength)}` 6 | } 7 | 8 | export default truncateHash 9 | -------------------------------------------------------------------------------- /packages/uikit/src/theme/dark.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from "styled-components"; 2 | import base from "./base"; 3 | import { darkColors } from "./colors"; 4 | 5 | const darkTheme: DefaultTheme = { 6 | ...base, 7 | isDark: true, 8 | colors: darkColors, 9 | }; 10 | 11 | export default darkTheme; 12 | -------------------------------------------------------------------------------- /src/config/constants/networks.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@scads/sdk' 2 | 3 | const NETWORK_URLS: { [chainId in ChainId]: string } = { 4 | [ChainId.MAINNET]: 'https://bsc-dataseed1.defibit.io', 5 | [ChainId.TESTNET]: 'https://data-seed-prebsc-1-s1.binance.org:8545', 6 | } 7 | 8 | export default NETWORK_URLS 9 | -------------------------------------------------------------------------------- /packages/uikit/src/theme/light.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from "styled-components"; 2 | import base from "./base"; 3 | import { lightColors } from "./colors"; 4 | 5 | const lightTheme: DefaultTheme = { 6 | ...base, 7 | isDark: false, 8 | colors: lightColors, 9 | }; 10 | 11 | export default lightTheme; 12 | -------------------------------------------------------------------------------- /packages/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx", 7 | "lib": ["ES2015", "DOM"], 8 | "module": "ESNext", 9 | "target": "es6" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/hooks/useRefresh.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { FastRefresh, SlowRefresh } from 'contexts/RefreshContext' 3 | 4 | export const useFastFresh = () => { 5 | return useContext(FastRefresh.Context) 6 | } 7 | export const useSlowFresh = () => { 8 | return useContext(SlowRefresh.Context) 9 | } 10 | -------------------------------------------------------------------------------- /packages/eslint-config/README.md: -------------------------------------------------------------------------------- 1 | # eslint-config 2 | 3 | Scads Eslint config with: 4 | 5 | - Airbnb config 6 | - Typescript 7 | - Prettier 8 | 9 | ## Usage 10 | 11 | ``` 12 | npx install-peerdeps --dev @scads-io/eslint-config 13 | ``` 14 | 15 | Add `"extends": "@scads-io/eslint-config"` to your eslint config file. 16 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Box/Flex.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { flexbox } from "styled-system"; 3 | import Box from "./Box"; 4 | import { FlexProps } from "./types"; 5 | 6 | const Flex = styled(Box)` 7 | display: flex; 8 | ${flexbox} 9 | `; 10 | 11 | export default Flex; 12 | -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"], 3 | "rules": { 4 | "subject-case": [2, "always", "sentence-case"], 5 | "type-enum": [ 6 | 2, 7 | "always", 8 | ["build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"] 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/uikit/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testPathIgnorePatterns: ["/node_modules/", "/dist/", "/.storybook/"], 4 | setupFilesAfterEnv: ["/src/setupTests.js"], 5 | transform: { 6 | "\\.(js|jsx)?$": "babel-jest", 7 | "^.+\\.svg$": "/svgTransform.js", 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Text/TooltipText.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import Text from "./Text"; 3 | 4 | const TooltipText = styled(Text)` 5 | text-decoration: ${({ theme }) => `underline dotted ${theme.colors.textSubtle}`}; 6 | text-underline-offset: 0.1em; 7 | `; 8 | 9 | export default TooltipText; 10 | -------------------------------------------------------------------------------- /packages/uikit/src/util/getThemeValue.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import get from "lodash/get" 3 | import { DefaultTheme } from "styled-components" 4 | 5 | const getThemeValue = 6 | (path: string, fallback?: string | number) => 7 | (theme: DefaultTheme): string => 8 | get(theme, path, fallback) 9 | 10 | export default getThemeValue 11 | -------------------------------------------------------------------------------- /src/utils/getLocalStorageItemKeys.ts: -------------------------------------------------------------------------------- 1 | const getLocalStorageItemKeys = (prefix: string) => { 2 | const result = [] 3 | for (let i = 0; i < localStorage.length; i++) { 4 | if (localStorage.key(i).startsWith(prefix)) { 5 | result.push(localStorage.key(i)) 6 | } 7 | } 8 | return result 9 | } 10 | 11 | export default getLocalStorageItemKeys 12 | -------------------------------------------------------------------------------- /packages/uikit/src/index.ts: -------------------------------------------------------------------------------- 1 | // Components 2 | export * from "./components/Box"; 3 | export * from "./components/Button"; 4 | export * from "./components/Heading"; 5 | export * from "./components/Link"; 6 | export * from "./components/Stepper"; 7 | export * from "./components/Svg"; 8 | export * from "./components/Text"; 9 | 10 | // Theme 11 | export * from "./theme"; 12 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Box/Box.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { background, border, layout, position, space } from "styled-system"; 3 | import { BoxProps } from "./types"; 4 | 5 | const Box = styled.div` 6 | ${background} 7 | ${border} 8 | ${layout} 9 | ${position} 10 | ${space} 11 | `; 12 | 13 | export default Box; 14 | -------------------------------------------------------------------------------- /packages/uikit/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript"; 2 | import url from "@rollup/plugin-url"; 3 | import pkg from "./package.json"; 4 | 5 | export default { 6 | input: "src/index.ts", 7 | output: [ 8 | { file: pkg.main, format: "cjs" }, 9 | { file: pkg.module, format: "es" }, 10 | ], 11 | plugins: [url(), typescript()], 12 | }; 13 | -------------------------------------------------------------------------------- /src/state/mint/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | 3 | export enum Field { 4 | CURRENCY_A = 'CURRENCY_A', 5 | CURRENCY_B = 'CURRENCY_B', 6 | } 7 | 8 | export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint') 9 | export const resetMintState = createAction('mint/resetMintState') 10 | -------------------------------------------------------------------------------- /src/config/constants/swapWarningTokens.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '@scads/sdk' 2 | import tokens from 'config/constants/tokens' 3 | 4 | const { bondly, safemoon } = tokens 5 | 6 | interface WarningTokenList { 7 | [key: string]: Token 8 | } 9 | 10 | const SwapWarningTokens = { 11 | safemoon, 12 | bondly, 13 | } 14 | 15 | export default SwapWarningTokens 16 | -------------------------------------------------------------------------------- /src/hooks/useToast.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { ToastsContext } from 'contexts/ToastsContext' 3 | 4 | const useToast = () => { 5 | const toastContext = useContext(ToastsContext) 6 | 7 | if (toastContext === undefined) { 8 | throw new Error('Toasts context undefined') 9 | } 10 | 11 | return toastContext 12 | } 13 | 14 | export default useToast 15 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface Window { 4 | ethereum?: { 5 | isMetaMask?: true 6 | request?: (...args: any[]) => Promise 7 | } 8 | BinanceChain?: { 9 | bnbSign?: (address: string, message: string) => Promise<{ publicKey: string; signature: string }> 10 | } 11 | } 12 | 13 | type SerializedBigNumber = string 14 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This file is processed and loaded automatically before your test files. 3 | // 4 | // You can read more here: 5 | // https://on.cypress.io/configuration 6 | // *********************************************************** 7 | 8 | // Import commands.ts using ES2015 syntax: 9 | import './commands' 10 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Text/types.ts: -------------------------------------------------------------------------------- 1 | import { LayoutProps, SpaceProps, TypographyProps } from "styled-system"; 2 | 3 | export interface TextProps extends SpaceProps, TypographyProps, LayoutProps { 4 | color?: string; 5 | fontSize?: string; 6 | bold?: boolean; 7 | small?: boolean; 8 | ellipsis?: boolean; 9 | textTransform?: "uppercase" | "lowercase" | "capitalize"; 10 | } 11 | -------------------------------------------------------------------------------- /src/config/constants/lists.ts: -------------------------------------------------------------------------------- 1 | export const UNSUPPORTED_LIST_URLS: string[] = [] 2 | 3 | // lower index == higher priority for token import 4 | export const DEFAULT_LIST_OF_LISTS: string[] = [ 5 | ...UNSUPPORTED_LIST_URLS, // need to load unsupported tokens as well 6 | ] 7 | 8 | // default lists to be 'active' aka searched across 9 | export const DEFAULT_ACTIVE_LIST_URLS: string[] = [] 10 | -------------------------------------------------------------------------------- /src/config/constants/tokenLists/scads-unsupported.tokenlist.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Scads Unsupported List", 3 | "timestamp": "2021-01-05T20:47:02.923Z", 4 | "version": { 5 | "major": 1, 6 | "minor": 0, 7 | "patch": 0 8 | }, 9 | "tags": {}, 10 | "logoURI": "ipfs://QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir", 11 | "keywords": ["scads", "unsupported"], 12 | "tokens": [] 13 | } 14 | -------------------------------------------------------------------------------- /src/hooks/useSentryUser.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/react' 2 | import { useEffect } from 'react' 3 | import useActiveWeb3React from './useActiveWeb3React' 4 | 5 | function useSentryUser() { 6 | const { account } = useActiveWeb3React() 7 | useEffect(() => { 8 | if (account) { 9 | Sentry.setUser({ account }) 10 | } 11 | }, [account]) 12 | } 13 | 14 | export default useSentryUser 15 | -------------------------------------------------------------------------------- /src/config/abi/erc20.ts: -------------------------------------------------------------------------------- 1 | import { Interface } from '@ethersproject/abi' 2 | import ERC20_ABI from './erc20.json' 3 | import ERC20_BYTES32_ABI from './erc20_bytes32.json' 4 | 5 | const ERC20_INTERFACE = new Interface(ERC20_ABI) 6 | 7 | const ERC20_BYTES32_INTERFACE = new Interface(ERC20_BYTES32_ABI) 8 | 9 | export default ERC20_INTERFACE 10 | export { ERC20_ABI, ERC20_BYTES32_INTERFACE, ERC20_BYTES32_ABI } 11 | -------------------------------------------------------------------------------- /src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { ThemeContext as StyledThemeContext } from 'styled-components' 3 | import { useThemeManager } from 'state/user/hooks' 4 | 5 | const useTheme = () => { 6 | const [isDark, toggleTheme] = useThemeManager() 7 | const theme = useContext(StyledThemeContext) 8 | return { isDark, theme, toggleTheme } 9 | } 10 | 11 | export default useTheme 12 | -------------------------------------------------------------------------------- /src/utils/slugify.ts: -------------------------------------------------------------------------------- 1 | const slugify = (str: string) => { 2 | return str 3 | .toLowerCase() 4 | .replace(/\s/g, '-') // Replace white space 5 | .replace(/[^a-zA-Z0-9-_]/g, '-') // Replace non alphanum-_ chars 6 | .replace(/-{2,}/g, '-') // Remove extra - 7 | .replace(/-$/g, '') // Remove - at the start 8 | .replace(/^-/g, '') // Remove - at the end 9 | } 10 | 11 | export default slugify 12 | -------------------------------------------------------------------------------- /packages/uikit/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 3 | addons: [ 4 | { 5 | name: "@storybook/addon-essentials", 6 | options: { 7 | backgrounds: false, 8 | }, 9 | }, 10 | "@storybook/addon-links", 11 | "@storybook/addon-a11y", 12 | "themeprovider-storybook/register", 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /src/contexts/Localization/useTranslation.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { LanguageContext } from './Provider' 3 | 4 | const useTranslation = () => { 5 | const languageContext = useContext(LanguageContext) 6 | 7 | if (languageContext === undefined) { 8 | throw new Error('Language context is undefined') 9 | } 10 | 11 | return languageContext 12 | } 13 | 14 | export default useTranslation 15 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Button/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import Button from "./Button"; 3 | import { BaseButtonProps, PolymorphicComponent } from "./types"; 4 | 5 | const IconButton: PolymorphicComponent = styled(Button)` 6 | padding: 0; 7 | width: ${({ scale }) => (scale === "sm" ? "32px" : "48px")}; 8 | `; 9 | 10 | export default IconButton; 11 | -------------------------------------------------------------------------------- /src/contexts/ToastsContext/types.ts: -------------------------------------------------------------------------------- 1 | import { Toast } from 'components/Toast' 2 | 3 | type ToastSignature = (title: Toast['title'], description?: Toast['description']) => void 4 | export interface ToastContextApi { 5 | toasts: Toast[] 6 | clear: () => void 7 | remove: (id: string) => void 8 | toastError: ToastSignature 9 | toastInfo: ToastSignature 10 | toastSuccess: ToastSignature 11 | toastWarning: ToastSignature 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/ENS/parseENSAddress.ts: -------------------------------------------------------------------------------- 1 | const ENS_NAME_REGEX = /^(([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+)eth(\/.*)?$/ 2 | 3 | export function parseENSAddress(ensAddress: string): { ensName: string; ensPath: string | undefined } | undefined { 4 | const match = ensAddress.match(ENS_NAME_REGEX) 5 | if (!match) return undefined 6 | return { ensName: `${match[1].toLowerCase()}eth`, ensPath: match[4] } 7 | } 8 | 9 | export default parseENSAddress 10 | -------------------------------------------------------------------------------- /src/__tests__/config/contracts.test.ts: -------------------------------------------------------------------------------- 1 | import map from 'lodash/map' 2 | import filter from 'lodash/filter' 3 | import contracts from 'config/constants/contracts' 4 | 5 | describe('Config contracts', () => { 6 | it.each(map(contracts, (contract, key) => [key, contract]))('Contract %s has a unique address', (key, contract) => { 7 | const duplicates = filter(contracts, (c) => contract[56] === c[56]) 8 | expect(duplicates).toHaveLength(1) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /src/utils/calls/pulse.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_GAS_LIMIT } from 'config' 2 | import getGasPrice from 'utils/getGasPrice' 3 | 4 | const options = { 5 | gasLimit: DEFAULT_GAS_LIMIT, 6 | } 7 | 8 | export const circulatePulse = async (pulseContract, stableCoinAddress) => { 9 | const gasPrice = getGasPrice() 10 | const tx = await pulseContract.circulate(stableCoinAddress, { ...options, gasPrice }) 11 | const receipt = await tx.wait() 12 | return receipt.status 13 | } -------------------------------------------------------------------------------- /src/hooks/useCurrentBlockTimestamp.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers' 2 | import { useSingleCallResult } from '../state/multicall/hooks' 3 | import { useMulticallContract } from './useContract' 4 | 5 | // gets the current timestamp from the blockchain 6 | export default function useCurrentBlockTimestamp(): BigNumber | undefined { 7 | const multicall = useMulticallContract() 8 | return useSingleCallResult(multicall, 'getCurrentBlockTimestamp')?.result?.[0] 9 | } 10 | -------------------------------------------------------------------------------- /packages/tsconfig/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "declaration": false, 8 | "declarationMap": false, 9 | "incremental": true, 10 | "jsx": "preserve", 11 | "lib": ["dom", "dom.iterable", "esnext"], 12 | "module": "esnext", 13 | "noEmit": true, 14 | "resolveJsonModule": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Alert/types.ts: -------------------------------------------------------------------------------- 1 | import { MouseEvent, ReactNode } from "react" 2 | 3 | export const variants = { 4 | INFO: "info", 5 | DANGER: "danger", 6 | SUCCESS: "success", 7 | WARNING: "warning", 8 | } as const 9 | 10 | export type Variants = typeof variants[keyof typeof variants] 11 | 12 | export interface AlertProps { 13 | variant?: Variants 14 | title: string 15 | children?: ReactNode 16 | onClick?: (evt: MouseEvent) => void 17 | } 18 | -------------------------------------------------------------------------------- /src/contexts/ToastsContext/Listener.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ToastContainer } from 'components/Toast' 3 | // eslint-disable-next-line import/no-cycle 4 | import useToast from 'hooks/useToast' 5 | 6 | const ToastListener = () => { 7 | const { toasts, remove } = useToast() 8 | 9 | const handleRemove = (id: string) => remove(id) 10 | 11 | return 12 | } 13 | 14 | export default ToastListener 15 | -------------------------------------------------------------------------------- /src/hooks/usePreviousValue.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | 3 | /** 4 | * Returns the previous value of the given value 5 | * 6 | * @see https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state 7 | */ 8 | const usePreviousValue = (value: any) => { 9 | const ref = useRef() 10 | 11 | useEffect(() => { 12 | ref.current = value 13 | }, [value]) 14 | 15 | return ref.current 16 | } 17 | 18 | export default usePreviousValue 19 | -------------------------------------------------------------------------------- /src/state/global/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | import { ChainId } from '@scads/sdk' 3 | 4 | // fired once when the app reloads but before the app renders 5 | // allows any updates to be applied to store data loaded from localStorage 6 | export const updateVersion = createAction('global/updateVersion') 7 | 8 | export const resetUserState = createAction<{ chainId: ChainId }>('global/resetUserState') 9 | 10 | export default updateVersion 11 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Heading/types.ts: -------------------------------------------------------------------------------- 1 | export const tags = { 2 | H1: "h1", 3 | H2: "h2", 4 | H3: "h3", 5 | H4: "h4", 6 | H5: "h5", 7 | H6: "h6", 8 | }; 9 | 10 | export const scales = { 11 | MD: "md", 12 | LG: "lg", 13 | XL: "xl", 14 | XXL: "xxl", 15 | } as const; 16 | 17 | export type Tags = typeof tags[keyof typeof tags]; 18 | export type Scales = typeof scales[keyof typeof scales]; 19 | 20 | export interface HeadingProps { 21 | as?: Tags; 22 | scale?: Scales; 23 | } 24 | -------------------------------------------------------------------------------- /packages/wagmi/src/useWeb3React.ts: -------------------------------------------------------------------------------- 1 | import { useAccount, useNetwork } from 'wagmi' 2 | 3 | export function useWeb3React() { 4 | const { chain } = useNetwork() 5 | const { address, connector, isConnected, isConnecting } = useAccount() 6 | 7 | return { 8 | chainId: chain?.id, 9 | account: isConnected ? address : null, // TODO: migrate using `isConnected` instead of account to check wallet auth 10 | isConnected, 11 | isConnecting, 12 | chain, 13 | connector, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/WalletModal/types.ts: -------------------------------------------------------------------------------- 1 | import { FC } from "react" 2 | import { SvgProps } from "@scads-io/uikit" 3 | 4 | export enum ConnectorNames { 5 | MetaMask = "metaMask", 6 | Injected = "injected", 7 | WalletConnect = "walletConnect", 8 | BSC = "bsc", 9 | } 10 | 11 | export type Login = (connectorId: ConnectorNames) => void 12 | 13 | export interface Config { 14 | title: string 15 | icon: FC 16 | connectorId: ConnectorNames 17 | priority: number 18 | href?: string 19 | } 20 | -------------------------------------------------------------------------------- /packages/uikit/src/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { Colors, MediaQueries, Radii, Shadows, Spacing, ZIndices } from "./types"; 2 | 3 | export interface ScadsTheme { 4 | isDark: boolean; 5 | colors: Colors; 6 | mediaQueries: MediaQueries; 7 | spacing: Spacing; 8 | shadows: Shadows; 9 | radii: Radii; 10 | zIndices: ZIndices; 11 | } 12 | 13 | export { darkColors, lightColors } from "./colors"; 14 | export { default as dark } from "./dark"; 15 | export { default as light } from "./light"; 16 | export * from "./types"; 17 | -------------------------------------------------------------------------------- /src/contexts/Localization/types.ts: -------------------------------------------------------------------------------- 1 | import { ReactText } from 'react' 2 | import { Language } from 'config/localization/languages' 3 | 4 | export type ContextData = { 5 | [key: string]: ReactText 6 | } 7 | 8 | export interface ProviderState { 9 | isFetching: boolean 10 | currentLanguage: Language 11 | } 12 | 13 | export interface ContextApi extends ProviderState { 14 | setLanguage: (language: Language) => void 15 | t: Translate 16 | } 17 | 18 | export type Translate = (key: string, data?: ContextData) => string 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./packages/tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "baseUrl": "src", 5 | "strictNullChecks": false, 6 | "noImplicitAny": false, 7 | "noFallthroughCasesInSwitch": true, 8 | "downlevelIteration": true, 9 | "target": "es5", 10 | "paths": { 11 | "@scads-io/wagmi": ["../packages/wagmi/src/index.ts"], 12 | } 13 | }, 14 | "exclude": ["node_modules", "cypress", "/apps/*"], 15 | "include": ["next-env.d.ts", "./src/**/*.ts", "./src/**/*.tsx"] 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/bigNumber.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js' 2 | import { ethers } from 'ethers' 3 | 4 | export const BIG_ZERO = new BigNumber(0) 5 | export const BIG_ONE = new BigNumber(1) 6 | export const BIG_NINE = new BigNumber(9) 7 | export const BIG_TEN = new BigNumber(10) 8 | 9 | export const ethersToSerializedBigNumber = (ethersBn: ethers.BigNumber): SerializedBigNumber => 10 | ethersToBigNumber(ethersBn).toJSON() 11 | 12 | export const ethersToBigNumber = (ethersBn: ethers.BigNumber): BigNumber => new BigNumber(ethersBn.toString()) 13 | -------------------------------------------------------------------------------- /doc/Tokens.md: -------------------------------------------------------------------------------- 1 | # Tokens management 2 | 3 | All the tokens are in `/config/constans/tokens.md`. They are instances of tthe `Token` class defined in the SDK. 4 | Before adding a new **farm** or **pool** you need to make sure the Tokens are in this file. 5 | To add a Token to the exchange lists: 6 | 7 | - For the default list: `/config/constant/tokenLists/scads-default.tokenlist.json` 8 | - For other lists, check the token-lists project in the `scads-toolkit` repo 9 | - To blacklist a token: `/config/constant/tokenLists/scads-unsupported.tokenlist.json` 10 | -------------------------------------------------------------------------------- /src/components/Skeleton/types.ts: -------------------------------------------------------------------------------- 1 | import { LayoutProps, SpaceProps } from "styled-system" 2 | 3 | export const animation = { 4 | WAVES: "waves", 5 | PULSE: "pulse", 6 | } as const 7 | 8 | export const variant = { 9 | RECT: "rect", 10 | CIRCLE: "circle", 11 | } as const 12 | 13 | export type Animation = typeof animation[keyof typeof animation] 14 | export type Variant = typeof variant[keyof typeof variant] 15 | 16 | export interface SkeletonProps extends SpaceProps, LayoutProps { 17 | animation?: Animation 18 | variant?: Variant 19 | } 20 | -------------------------------------------------------------------------------- /cypress/integration/home/home.test.ts: -------------------------------------------------------------------------------- 1 | import { TEST_ADDRESS_NEVER_USE, TEST_ADDRESS_NEVER_USE_SHORTENED } from '../../support/commands' 2 | 3 | describe('Home Page', () => { 4 | beforeEach(() => cy.visit('/')) 5 | it('loads home page', () => { 6 | cy.get('#homepage-hero') 7 | }) 8 | 9 | it('connected wallet is displayed properly', () => { 10 | cy.get('nav') 11 | .first() 12 | .find('div') 13 | .last() 14 | .get(`[title=${TEST_ADDRESS_NEVER_USE}]`) 15 | .contains(TEST_ADDRESS_NEVER_USE_SHORTENED) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/state/swap/types.ts: -------------------------------------------------------------------------------- 1 | export type PairDataNormalized = { 2 | time: number 3 | token0Id: string 4 | token1Id: string 5 | reserve0: number 6 | reserve1: number 7 | }[] 8 | 9 | export type DerivedPairDataNormalized = { 10 | time: number 11 | token0Id: string 12 | token1Id: string 13 | token0DerivedBNB: number 14 | token1DerivedBNB: number 15 | }[] 16 | 17 | export type PairPricesNormalized = { 18 | time: Date 19 | value: number 20 | }[] 21 | 22 | export enum PairDataTimeWindowEnum { 23 | DAY, 24 | WEEK, 25 | MONTH, 26 | YEAR, 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/getGasPrice.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@scads/sdk' 2 | import store from 'state' 3 | import { GAS_PRICE_GWEI } from 'state/user/hooks/helpers' 4 | 5 | /** 6 | * Function to return gasPrice outwith a react component 7 | */ 8 | const getGasPrice = (): string => { 9 | const chainId = process.env.NEXT_PUBLIC_CHAIN_ID 10 | const state = store.getState() 11 | const userGas = state.user.gasPrice || GAS_PRICE_GWEI.default 12 | return chainId === ChainId.MAINNET.toString() ? userGas : GAS_PRICE_GWEI.testnet 13 | } 14 | 15 | export default getGasPrice 16 | -------------------------------------------------------------------------------- /src/config/constants/types.ts: -------------------------------------------------------------------------------- 1 | export type TranslatableText = 2 | | string 3 | | { 4 | key: string 5 | data?: { 6 | [key: string]: string | number 7 | } 8 | } 9 | export interface Address { 10 | 97?: string 11 | 56: string 12 | } 13 | 14 | export interface SerializedToken { 15 | chainId: number 16 | address: string 17 | decimals: number 18 | symbol?: string 19 | name?: string 20 | projectLink?: string 21 | } 22 | 23 | export type Images = { 24 | lg: string 25 | md: string 26 | sm: string 27 | ipfs?: string 28 | } 29 | -------------------------------------------------------------------------------- /src/config/localization/languages.ts: -------------------------------------------------------------------------------- 1 | export interface Language { 2 | code: string 3 | language: string 4 | locale: string 5 | } 6 | 7 | export const EN: Language = { locale: 'en-US', language: 'English', code: 'en' } 8 | export const RU: Language = { locale: 'ru-RU', language: 'Русский', code: 'ru' } 9 | /* export const UK: Language = { locale: 'uk-UA', language: 'Українська', code: 'uk' } */ 10 | 11 | export const languages = { 12 | 'en-US': EN, 13 | 'ru-RU': RU, 14 | /* 'uk-UA': UK, */ 15 | } 16 | 17 | export const languageList = Object.values(languages) 18 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | import ethers, { Contract, ContractFunction } from 'ethers' 2 | 3 | export type MultiCallResponse = T | null 4 | 5 | // Profile contract 6 | // [userId, points, teamId, tokenId, collectionAddress isActive] 7 | export type GetUserProfileResponse = [ 8 | ethers.BigNumber, 9 | ethers.BigNumber, 10 | ethers.BigNumber, 11 | string, 12 | ethers.BigNumber, 13 | boolean, 14 | ] 15 | 16 | export interface ScadsProfileContract extends Contract { 17 | getUserProfile: ContractFunction 18 | hasRegistered: ContractFunction 19 | } 20 | -------------------------------------------------------------------------------- /src/state/multicall/chunkArray.ts: -------------------------------------------------------------------------------- 1 | // chunks array into chunks of maximum size 2 | // evenly distributes items among the chunks 3 | export default function chunkArray(items: T[], maxChunkSize: number): T[][] { 4 | if (maxChunkSize < 1) throw new Error('maxChunkSize must be gte 1') 5 | if (items.length <= maxChunkSize) return [items] 6 | 7 | const numChunks: number = Math.ceil(items.length / maxChunkSize) 8 | const chunkSize = Math.ceil(items.length / numChunks) 9 | 10 | return [...Array(numChunks).keys()].map((ix) => items.slice(ix * chunkSize, ix * chunkSize + chunkSize)) 11 | } 12 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Stepper/types.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from "styled-components"; 2 | 3 | export interface ThemedProps { 4 | theme: DefaultTheme; 5 | } 6 | 7 | export type Status = "past" | "current" | "future"; 8 | 9 | export interface StatusProps extends ThemedProps { 10 | theme: DefaultTheme; 11 | status?: Status; 12 | $isFirstStep?: boolean; 13 | $isLastStep?: boolean; 14 | $isFirstPart?: boolean; 15 | } 16 | 17 | export interface StepProps { 18 | index: number; 19 | statusFirstPart: Status; 20 | statusSecondPart?: Status; 21 | numberOfSteps?: number; 22 | } 23 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/types.ts: -------------------------------------------------------------------------------- 1 | import { SVGAttributes } from "react"; 2 | import { DefaultTheme } from "styled-components"; 3 | import { SpaceProps } from "styled-system"; 4 | import { Colors } from "../../theme"; 5 | 6 | export interface SvgProps extends SVGAttributes, SpaceProps { 7 | theme?: DefaultTheme; 8 | spin?: boolean; 9 | } 10 | 11 | export type IconComponentType = { 12 | iconName: string; 13 | isActive?: boolean; 14 | height?: string; 15 | width?: string; 16 | activeColor?: string; 17 | activeBackgroundColor?: keyof Colors; 18 | } & SvgProps; 19 | -------------------------------------------------------------------------------- /src/components/ui/animated-background.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { motion } from 'framer-motion' 3 | import { cn } from 'lib/utils' 4 | 5 | const AnimatedBackground = ({ color }: { color: string }) => { 6 | return ( 7 | 16 | ) 17 | } 18 | 19 | export default AnimatedBackground 20 | -------------------------------------------------------------------------------- /src/hooks/useActiveWeb3React.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@scads/sdk' 2 | import { useWeb3React } from '@scads-io/wagmi' 3 | import { useProvider } from 'wagmi' 4 | 5 | /** 6 | * Provides a web3 provider with or without user's signer 7 | * Recreate web3 instance only if the provider change 8 | */ 9 | const useActiveWeb3React = () => { 10 | const web3React = useWeb3React() 11 | const chainId = ChainId.MAINNET 12 | const provider = useProvider({ chainId }) 13 | 14 | return { 15 | provider, 16 | ...web3React, 17 | chainId, 18 | } 19 | } 20 | 21 | export default useActiveWeb3React 22 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Box/types.ts: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from "react"; 2 | import { 3 | BackgroundProps, 4 | BorderProps, 5 | FlexboxProps, 6 | LayoutProps, 7 | PositionProps, 8 | SpaceProps, 9 | GridProps as _GridProps, 10 | } from "styled-system"; 11 | 12 | export interface BoxProps 13 | extends BackgroundProps, 14 | BorderProps, 15 | LayoutProps, 16 | PositionProps, 17 | SpaceProps, 18 | HTMLAttributes {} 19 | 20 | export interface FlexProps extends BoxProps, FlexboxProps {} 21 | 22 | export interface GridProps extends FlexProps, _GridProps {} 23 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | CI=false 2 | NEXT_PUBLIC_CHAIN_ID = "56" 3 | # NEXT_PUBLIC_CHAIN_ID = "97" 4 | 5 | # 10+ nodes balanced, US/EU 6 | NEXT_PUBLIC_NODE_1 = "https://bsc-dataseed1.binance.org/" 7 | # NEXT_PUBLIC_NODE_1 = "https://data-seed-prebsc-2-s1.binance.org:8545/" 8 | 9 | # 10+ nodes balanced, US/EU 10 | NEXT_PUBLIC_NODE_2 = "https://bsc-dataseed2.binance.org/" 11 | # NEXT_PUBLIC_NODE_2 = "https://data-seed-prebsc-1-s2.binance.org:8545/" 12 | 13 | # 10+ nodes balanced in each region, global 14 | NEXT_PUBLIC_NODE_3 = "https://bsc-dataseed3.binance.org/" 15 | # NEXT_PUBLIC_NODE_3 = "https://data-seed-prebsc-1-s3.binance.org:8545/" -------------------------------------------------------------------------------- /src/contexts/Localization/helpers.ts: -------------------------------------------------------------------------------- 1 | import { EN } from 'config/localization/languages' 2 | 3 | const publicUrl = process.env.PUBLIC_URL || '' 4 | 5 | export const LS_KEY = 'scads_language' 6 | 7 | export const fetchLocale = async (locale) => { 8 | const response = await fetch(`${publicUrl}/locales/${locale}.json`) 9 | const data = await response.json() 10 | return data 11 | } 12 | 13 | export const getLanguageCodeFromLS = () => { 14 | try { 15 | const codeFromStorage = localStorage.getItem(LS_KEY) 16 | 17 | return codeFromStorage || EN.locale 18 | } catch { 19 | return EN.locale 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/Block.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "inlineSources": false, 11 | "isolatedModules": true, 12 | "moduleResolution": "node", 13 | "noUnusedLocals": false, 14 | "noUnusedParameters": false, 15 | "preserveWatchOutput": true, 16 | "skipLibCheck": true, 17 | "strict": true 18 | }, 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /src/config/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 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Scads", 3 | "name": "ScadsSwap", 4 | "description": "Discover SCADS: The New Secured, Self-Sustained Algorithm!", 5 | "homepage_url": "https://scads.io/", 6 | "start_url": ".", 7 | "display": "standalone", 8 | "theme_color": "#1FC7D4", 9 | "background_color": "#ffffff", 10 | "orientation": "portrait", 11 | "icons": [ 12 | { 13 | "src": "favicon.ico", 14 | "sizes": "64x64 32x32 24x24 16x16", 15 | "type": "image/x-icon" 16 | }, 17 | { 18 | "src": "logo.webp", 19 | "type": "image/png", 20 | "sizes": "192x192" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/uikit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "lib": ["dom", "DOM.Iterable", "ESNext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": false, 16 | "jsx": "react" 17 | }, 18 | "include": ["src"], 19 | "exclude": ["node_modules", "src/**/*.stories.tsx", "src/__tests__/**/*"] 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Hero from "./_components/hero" 3 | import TextBlock from "./_components/text-block" 4 | import Wallets from "./_components/wallets" 5 | import Roadmap from "./_components/roadmap/roadmap" 6 | import Stats from "./_components/stats/stats" 7 | import Tutorial from "./_components/tutorial/tutorial" 8 | import Outro from "./_components/outro" 9 | 10 | export default function Home() { 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_CHAIN_ID = "56" 2 | # NEXT_PUBLIC_CHAIN_ID = "97" 3 | NEXT_PUBLIC_GTAG = "GTM-PXLD3XW" 4 | 5 | # 10+ nodes balanced, US/EU 6 | NEXT_PUBLIC_NODE_1 = "https://bsc-dataseed.binance.org/" 7 | # NEXT_PUBLIC_NODE_1 = "https://data-seed-prebsc-2-s1.binance.org:8545/" 8 | 9 | # 10+ nodes balanced, US/EU 10 | NEXT_PUBLIC_NODE_2 = "https://bsc-dataseed1.binance.org/" 11 | # NEXT_PUBLIC_NODE_2 = "https://data-seed-prebsc-1-s2.binance.org:8545/" 12 | 13 | # 10+ nodes balanced in each region, global 14 | NEXT_PUBLIC_NODE_3 = "https://bsc-dataseed3.binance.org/" 15 | # NEXT_PUBLIC_NODE_3 = "https://data-seed-prebsc-1-s3.binance.org:8545/ -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_CHAIN_ID = "56" 2 | # NEXT_PUBLIC_CHAIN_ID = "97" 3 | NEXT_PUBLIC_GTAG = "GTM-TLF66T4" 4 | 5 | # 10+ nodes balanced, US/EU 6 | NEXT_PUBLIC_NODE_1 = "https://bsc-dataseed.binance.org/" 7 | # NEXT_PUBLIC_NODE_1 = "https://data-seed-prebsc-2-s1.binance.org:8545/" 8 | 9 | # 10+ nodes balanced, US/EU 10 | NEXT_PUBLIC_NODE_2 = "https://bsc-dataseed1.binance.org/" 11 | # NEXT_PUBLIC_NODE_2 = "https://data-seed-prebsc-1-s2.binance.org:8545/" 12 | 13 | # 10+ nodes balanced in each region, global 14 | NEXT_PUBLIC_NODE_3 = "https://bsc-dataseed3.binance.org/" 15 | # NEXT_PUBLIC_NODE_3 = "https://data-seed-prebsc-1-s3.binance.org:8545/" -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/Info.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /packages/uikit/src/__tests__/components/flex.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { renderWithTheme } from "../../testHelpers"; 3 | import Flex from "../../components/Box/Flex"; 4 | 5 | it("renders correctly", () => { 6 | const { asFragment } = renderWithTheme(flex); 7 | expect(asFragment()).toMatchInlineSnapshot(` 8 | 9 | .c0 { 10 | display: -webkit-box; 11 | display: -webkit-flex; 12 | display: -ms-flexbox; 13 | display: flex; 14 | } 15 | 16 |
19 | flex 20 |
21 |
22 | `); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/Error.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /packages/uikit/src/__tests__/components/text.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { renderWithTheme } from "../../testHelpers"; 3 | import Text from "../../components/Text/Text"; 4 | 5 | it("renders correctly", () => { 6 | const { asFragment } = renderWithTheme(scads); 7 | expect(asFragment()).toMatchInlineSnapshot(` 8 | 9 | .c0 { 10 | color: #280D5F; 11 | font-size: 16px; 12 | font-weight: 400; 13 | line-height: 1.5; 14 | } 15 | 16 |
20 | scads 21 |
22 |
23 | `); 24 | }); 25 | -------------------------------------------------------------------------------- /src/hooks/useLastUpdated.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from 'react' 2 | import usePreviousValue from './usePreviousValue' 3 | 4 | /** 5 | * A helper hook to keep track of the time between events 6 | * Can also be used to force an effect to re-run 7 | */ 8 | const useLastUpdated = () => { 9 | const [lastUpdated, setStateLastUpdated] = useState(Date.now()) 10 | const previousLastUpdated = usePreviousValue(lastUpdated) 11 | 12 | const setLastUpdated = useCallback(() => { 13 | setStateLastUpdated(Date.now()) 14 | }, [setStateLastUpdated]) 15 | 16 | return { lastUpdated, previousLastUpdated, setLastUpdated } 17 | } 18 | 19 | export default useLastUpdated 20 | -------------------------------------------------------------------------------- /src/hooks/useProviderOrSigner.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import { useWeb3React } from '@scads-io/wagmi' 3 | import { useAccount, useProvider, useSigner } from 'wagmi' 4 | 5 | export const useProviderOrSigner = (withSignerIfPossible = true) => { 6 | const web3React = useWeb3React() 7 | const { chainId } = web3React 8 | const provider = useProvider({ chainId }) 9 | const { address, isConnected } = useAccount() 10 | const { data: signer } = useSigner() 11 | 12 | return useMemo( 13 | () => (withSignerIfPossible && address && isConnected && signer ? signer : provider), 14 | [address, isConnected, provider, signer, withSignerIfPossible], 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/state/block/index.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit' 2 | import { BlockState } from '../types' 3 | 4 | const initialState: BlockState = { currentBlock: 0, initialBlock: 0 } 5 | 6 | export const blockSlice = createSlice({ 7 | name: 'Block', 8 | initialState, 9 | reducers: { 10 | setBlock: (state, action: PayloadAction) => { 11 | if (state.initialBlock === 0) { 12 | state.initialBlock = action.payload 13 | } 14 | 15 | state.currentBlock = action.payload 16 | }, 17 | }, 18 | }) 19 | 20 | // Actions 21 | export const { setBlock } = blockSlice.actions 22 | 23 | export default blockSlice.reducer 24 | -------------------------------------------------------------------------------- /src/hooks/useOutsideClick.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | 3 | export const useOutsideClick = (ref: React.RefObject, callback: Function) => { 4 | useEffect(() => { 5 | const listener = (event: any) => { 6 | if (!ref.current || ref.current.contains(event.target)) { 7 | return 8 | } 9 | callback(event) 10 | } 11 | 12 | document.addEventListener('mousedown', listener) 13 | document.addEventListener('touchstart', listener) 14 | 15 | return () => { 16 | document.removeEventListener('mousedown', listener) 17 | document.removeEventListener('touchstart', listener) 18 | } 19 | }, [ref, callback]) 20 | } 21 | -------------------------------------------------------------------------------- /src/state/user/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | import { SerializedToken } from 'config/constants/types' 3 | 4 | export const updateUserSingleHopOnly = createAction<{ userSingleHopOnly: boolean }>('user/updateUserSingleHopOnly') 5 | export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number }>( 6 | 'user/updateUserSlippageTolerance', 7 | ) 8 | export const addSerializedToken = createAction<{ serializedToken: SerializedToken }>('user/addSerializedToken') 9 | export const removeSerializedToken = createAction<{ chainId: number; address: string }>('user/removeSerializedToken') 10 | 11 | export const toggleTheme = createAction('user/toggleTheme') -------------------------------------------------------------------------------- /src/utils/calls/scadsMint.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_GAS_LIMIT } from 'config' 2 | import getGasPrice from 'utils/getGasPrice' 3 | 4 | const options = { 5 | gasLimit: DEFAULT_GAS_LIMIT, 6 | } 7 | 8 | export const scadsMint = async (scadsContract, amount, token) => { 9 | const gasPrice = getGasPrice() 10 | const tx = await scadsContract.scadsMint(amount, token) 11 | const receipt = await tx.wait() 12 | return receipt.status 13 | } 14 | 15 | export const scadsSell = async (scadsContract, amount, token) => { 16 | const gasPrice = getGasPrice() 17 | const tx = await scadsContract.scadsSell(amount, token, { ...options, gasPrice }) 18 | const receipt = await tx.wait() 19 | return receipt.status 20 | } 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const nextJest = require('next/jest') 3 | 4 | // Providing the path to your Next.js app which will enable loading next.config.js and .env files 5 | const createJestConfig = nextJest({ dir: './' }) 6 | 7 | // Any custom config you want to pass to Jest 8 | const customJestConfig = { 9 | testPathIgnorePatterns: ['/cypress/', '/src/__tests__/config'], 10 | moduleDirectories: ['node_modules', 'src'], 11 | testTimeout: 20000, 12 | } 13 | 14 | // createJestConfig is exported in this way to ensure that next/jest can load the Next.js configuration, which is async 15 | module.exports = createJestConfig(customJestConfig) -------------------------------------------------------------------------------- /src/components/Toast/types.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | 3 | export const types = { 4 | SUCCESS: 'success', 5 | DANGER: 'danger', 6 | WARNING: 'warning', 7 | INFO: 'info', 8 | } 9 | 10 | export type Types = typeof types[keyof typeof types] 11 | 12 | export interface Toast { 13 | id: string 14 | type: Types 15 | title: string 16 | description?: ReactNode 17 | } 18 | 19 | export interface ToastContainerProps { 20 | toasts: Toast[] 21 | stackSpacing?: number 22 | ttl?: number 23 | onRemove: (id: string) => void 24 | } 25 | 26 | export interface ToastProps { 27 | toast: Toast 28 | onRemove: ToastContainerProps['onRemove'] 29 | ttl: number 30 | style: Partial 31 | } 32 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/CheckmarkCircle.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /src/utils/maxAmountSpend.ts: -------------------------------------------------------------------------------- 1 | import { CurrencyAmount, ETHER, JSBI } from '@scads/sdk' 2 | import { MIN_BNB } from '../config/constants' 3 | 4 | /** 5 | * Given some token amount, return the max that can be spent of it 6 | * @param currencyAmount to return max of 7 | */ 8 | export function maxAmountSpend(currencyAmount?: CurrencyAmount): CurrencyAmount | undefined { 9 | if (!currencyAmount) return undefined 10 | if (currencyAmount.currency === ETHER) { 11 | if (JSBI.greaterThan(currencyAmount.raw, MIN_BNB)) { 12 | return CurrencyAmount.ether(JSBI.subtract(currencyAmount.raw, MIN_BNB)) 13 | } 14 | return CurrencyAmount.ether(JSBI.BigInt(0)) 15 | } 16 | return currencyAmount 17 | } 18 | 19 | export default maxAmountSpend 20 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as BlockIcon } from "./Icons/Block"; 2 | export { default as CheckmarkCircleIcon } from "./Icons/CheckmarkCircle"; 3 | export { default as CloseIcon } from "./Icons/Close"; 4 | export { default as ErrorIcon } from "./Icons/Error"; 5 | export { default as InfoIcon } from "./Icons/Info"; 6 | export { default as MetamaskIcon } from "./Icons/Metamask"; 7 | export { default as OpenNewIcon } from "./Icons/OpenNew"; 8 | export { default as RefreshIcon } from "./Icons/Refresh"; 9 | export { default as WalletConnect } from "./Icons/WalletConnect"; 10 | export { default as TrustWallet } from "./Icons/TrustWallet"; 11 | 12 | export { default as Svg } from "./Svg"; 13 | export type { SvgProps } from "./types"; 14 | -------------------------------------------------------------------------------- /src/hooks/useEagerConnect.ts: -------------------------------------------------------------------------------- 1 | import { useClient, useConnect } from 'wagmi' 2 | import { useEffect } from 'react' 3 | 4 | const SAFE_ID = 'safe' 5 | 6 | const useEagerConnect = () => { 7 | const client = useClient() 8 | const { connectAsync, connectors } = useConnect() 9 | useEffect(() => { 10 | const connectorInstance = connectors.find((c) => c.id === SAFE_ID && c.ready) 11 | if ( 12 | connectorInstance && 13 | // @ts-ignore 14 | !window.cy 15 | ) { 16 | connectAsync({ connector: connectorInstance }).catch(() => { 17 | client.autoConnect() 18 | }) 19 | } else { 20 | client.autoConnect() 21 | } 22 | }, [client, connectAsync, connectors]) 23 | } 24 | 25 | export default useEagerConnect 26 | -------------------------------------------------------------------------------- /src/state/user/hooks/useUserAddedTokens.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import { ChainId, Token } from '@scads/sdk' 3 | import { useSelector } from 'react-redux' 4 | import useActiveWeb3React from 'hooks/useActiveWeb3React' 5 | import { AppState } from '../../index' 6 | import { deserializeToken } from './helpers' 7 | 8 | export default function useUserAddedTokens(): Token[] { 9 | const { chainId } = useActiveWeb3React() 10 | const serializedTokensMap = useSelector(({ user: { tokens } }) => tokens) 11 | 12 | return useMemo(() => { 13 | if (!chainId) return [] 14 | return Object.values(serializedTokensMap?.[chainId as ChainId] ?? {}).map(deserializeToken) 15 | }, [serializedTokensMap, chainId]) 16 | } 17 | -------------------------------------------------------------------------------- /src/hooks/useTokenAllowance.ts: -------------------------------------------------------------------------------- 1 | import { Token, TokenAmount } from '@scads/sdk' 2 | import { useMemo } from 'react' 3 | 4 | import { useTokenContract } from './useContract' 5 | import { useSingleCallResult } from '../state/multicall/hooks' 6 | 7 | function useTokenAllowance(token?: Token, owner?: string, spender?: string): TokenAmount | undefined { 8 | const contract = useTokenContract(token?.address, false) 9 | const inputs = useMemo(() => [owner, spender], [owner, spender]) 10 | const allowance = useSingleCallResult(contract, 'allowance', inputs).result 11 | return useMemo( 12 | () => (token && allowance ? new TokenAmount(token, allowance.toString()) : undefined), 13 | [token, allowance], 14 | ) 15 | } 16 | 17 | export default useTokenAllowance 18 | -------------------------------------------------------------------------------- /src/hooks/useTotalSupply.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import { Token, TokenAmount } from '@scads/sdk' 3 | import { useTokenContract } from './useContract' 4 | import { useSingleCallResult } from '../state/multicall/hooks' 5 | 6 | // returns undefined if input token is undefined, or fails to get token contract, 7 | // or contract total supply cannot be fetched 8 | function useTotalSupply(token?: Token): TokenAmount | undefined { 9 | const contract = useTokenContract(token?.address, false) 10 | 11 | const totalSupply: BigNumber = useSingleCallResult(contract, 'totalSupply')?.result?.[0] 12 | 13 | return token && totalSupply ? new TokenAmount(token, totalSupply.toString()) : undefined 14 | } 15 | 16 | export default useTotalSupply 17 | -------------------------------------------------------------------------------- /src/hooks/useTransactionDeadline.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers' 2 | import { useMemo } from 'react' 3 | import { useSelector } from 'react-redux' 4 | import { AppState } from '../state' 5 | import useCurrentBlockTimestamp from './useCurrentBlockTimestamp' 6 | 7 | // combines the block timestamp with the user setting to give the deadline that should be used for any submitted transaction 8 | export default function useTransactionDeadline(): BigNumber | undefined { 9 | const ttl = useSelector((state) => state.user.userDeadline) 10 | const blockTimestamp = useCurrentBlockTimestamp() 11 | return useMemo(() => { 12 | if (blockTimestamp && ttl) return blockTimestamp.add(ttl) 13 | return undefined 14 | }, [blockTimestamp, ttl]) 15 | } 16 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Link/Link.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | import getExternalLinkProps from "../../util/getExternalLinkProps"; 4 | import Text from "../Text/Text"; 5 | import { LinkProps } from "./types"; 6 | 7 | const StyledLink = styled(Text)` 8 | display: flex; 9 | align-items: center; 10 | width: fit-content; 11 | &:hover { 12 | text-decoration: underline; 13 | } 14 | `; 15 | 16 | const Link: React.FC = ({ external, ...props }) => { 17 | const internalProps = external ? getExternalLinkProps() : {}; 18 | return ; 19 | }; 20 | 21 | Link.defaultProps = { 22 | color: "primary", 23 | }; 24 | 25 | export default Link; 26 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/Close.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as LabelPrimitive from '@radix-ui/react-label' 3 | import { cva, type VariantProps } from 'class-variance-authority' 4 | 5 | import { cn } from 'lib/utils' 6 | 7 | const labelVariants = cva('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70') 8 | 9 | const Label = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef & VariantProps 12 | >(({ className, ...props }, ref) => ( 13 | 14 | )) 15 | Label.displayName = LabelPrimitive.Root.displayName 16 | 17 | export { Label } 18 | -------------------------------------------------------------------------------- /src/hooks/useInterval.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | 3 | export default function useInterval(callback: () => void, delay: null | number, leading = true) { 4 | const savedCallback = useRef<() => void>() 5 | 6 | // Remember the latest callback. 7 | useEffect(() => { 8 | savedCallback.current = callback 9 | }, [callback]) 10 | 11 | // Set up the interval. 12 | useEffect(() => { 13 | function tick() { 14 | const { current } = savedCallback 15 | if (current) { 16 | current() 17 | } 18 | } 19 | 20 | if (delay !== null) { 21 | if (leading) tick() 22 | const id = setInterval(tick, delay) 23 | return () => clearInterval(id) 24 | } 25 | return undefined 26 | }, [delay, leading]) 27 | } 28 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Stepper/Stepper.tsx: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import React from "react"; 3 | import styled from "styled-components"; 4 | import { ThemedProps } from "./types"; 5 | 6 | const StepperWrapper = styled.div` 7 | display: flex; 8 | flex-direction: column; 9 | width: fit-content; 10 | `; 11 | 12 | const Stepper: React.FC = ({ children }) => { 13 | const numberOfSteps = React.Children.count(children); 14 | return ( 15 | 16 | {React.Children.map(children, (child) => { 17 | if (React.isValidElement(child)) { 18 | return React.cloneElement(child, { numberOfSteps }); 19 | } 20 | return child; 21 | })} 22 | 23 | ); 24 | }; 25 | 26 | export default Stepper; 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | cypress/videos 11 | cypress/screenshots 12 | cypress/fixtures/example.json 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | .idea/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | .eslintcache 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # debug 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | debug.log* 37 | 38 | package-lock.json 39 | /yarn.lock 40 | public/output.css 41 | 42 | # Sentry 43 | .sentryclirc 44 | /packages/uikit/dist 45 | /packages/uikit/node_modules 46 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/OpenNew.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /src/components/ui/custom-hero.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { motion } from 'framer-motion' 3 | import { cn } from 'lib/utils' 4 | 5 | const CustomHero = ({ title, className }: { title: string; className?: string }) => { 6 | return ( 7 | <> 8 | 19 | {title} 20 | 21 | 22 | ) 23 | } 24 | 25 | export default CustomHero 26 | -------------------------------------------------------------------------------- /src/hooks/useHttpLocations.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import contenthashToUri from 'utils/contenthashToUri' 3 | import { parseENSAddress } from 'utils/ENS/parseENSAddress' 4 | import uriToHttp from 'utils/uriToHttp' 5 | import useENSContentHash from './ENS/useENSContentHash' 6 | 7 | export default function useHttpLocations(uri: string | undefined): string[] { 8 | const ens = useMemo(() => (uri ? parseENSAddress(uri) : undefined), [uri]) 9 | const resolvedContentHash = useENSContentHash(ens?.ensName) 10 | return useMemo(() => { 11 | if (ens) { 12 | return resolvedContentHash.contenthash ? uriToHttp(contenthashToUri(resolvedContentHash.contenthash)) : [] 13 | } 14 | return uri ? uriToHttp(uri) : [] 15 | }, [ens, resolvedContentHash.contenthash, uri]) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Layout/Column.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 | width: 100%; 10 | align-items: center; 11 | ` 12 | 13 | export const AutoColumn = styled.div<{ 14 | gap?: 'sm' | 'md' | 'lg' | string 15 | justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between' 16 | }>` 17 | display: grid; 18 | grid-auto-rows: auto; 19 | grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap}; 20 | justify-items: ${({ justify }) => justify && justify}; 21 | padding-top: 5px; 22 | ` 23 | 24 | export default Column 25 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/Refresh.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Icon; 14 | -------------------------------------------------------------------------------- /src/components/ui/section-header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { motion } from 'framer-motion' 3 | import { useTranslation } from 'contexts/Localization' 4 | 5 | 6 | const SectionHeader = ({ title, y }: { title: string; y: number }) => { 7 | const { t } = useTranslation() 8 | 9 | return ( 10 | 20 | {t(title)} 21 | 22 | ) 23 | } 24 | 25 | export default SectionHeader 26 | -------------------------------------------------------------------------------- /src/pages/_components/wallets.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { HoverEffect } from 'components/ui/card-hover-effect' 3 | import { cardItems } from 'constants/content' 4 | import SectionHeader from 'components/ui/section-header' 5 | 6 | const Wallets = () => { 7 | return ( 8 |
9 | 10 |
11 | 12 |
13 |
14 |
15 | ) 16 | } 17 | 18 | export default Wallets 19 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | // modified from https://usehooks.com/useDebounce/ 4 | export default function useDebounce(value: T, delay: number): T { 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 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Svg.tsx: -------------------------------------------------------------------------------- 1 | import styled, { css, keyframes } from "styled-components"; 2 | import { space } from "styled-system"; 3 | import { SvgProps } from "./types"; 4 | 5 | const rotate = keyframes` 6 | from { 7 | transform: rotate(0deg); 8 | } 9 | to { 10 | transform: rotate(360deg); 11 | } 12 | `; 13 | 14 | const spinStyle = css` 15 | animation: ${rotate} 2s linear infinite; 16 | `; 17 | 18 | const Svg = styled.svg` 19 | align-self: center; // Safari fix 20 | fill: ${({ theme }) => theme.isDark ? "white" : "black"}; 21 | flex-shrink: 0; 22 | ${({ spin }) => spin && spinStyle} 23 | ${space} 24 | `; 25 | 26 | Svg.defaultProps = { 27 | color: "text", 28 | width: "20px", 29 | xmlns: "http://www.w3.org/2000/svg", 30 | spin: false, 31 | }; 32 | 33 | export default Svg; 34 | -------------------------------------------------------------------------------- /packages/uikit/README.md: -------------------------------------------------------------------------------- 1 | # Scads UIkit 2 | 3 | Scads UIkit is a set of React components and hooks used to build pages on Scads's apps. It also contains a theme file for dark and light mode. 4 | 5 | ## Install 6 | 7 | `yarn add @scads/uikit` 8 | 9 | ## Setup 10 | 11 | ### Theme 12 | 13 | Before using Scads UIkit, you need to provide the theme file to styled-component. 14 | 15 | ``` 16 | import { ThemeProvider } from 'styled-components' 17 | import { light, dark } from '@scads/uikit' 18 | ... 19 | ... 20 | ``` 21 | 22 | ### Reset 23 | 24 | A reset CSS is available as a global styled component. 25 | 26 | ``` 27 | import { ResetCSS } from '@scads/uikit' 28 | ... 29 | 30 | ``` 31 | 32 | ### Types 33 | 34 | This project is built with Typescript and export all the relevant types. 35 | -------------------------------------------------------------------------------- /sentry.server.config.js: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the server. 2 | // The config you add here will be used whenever the server handles a request. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from '@sentry/nextjs' 6 | 7 | const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN 8 | 9 | Sentry.init({ 10 | dsn: SENTRY_DSN || 'https://ed98e16b9d704c22bef92d24bdd5f3b7@o1092725.ingest.sentry.io/6111410', 11 | // Adjust this value in production, or use tracesSampler for greater control 12 | tracesSampleRate: 0.1, 13 | // ... 14 | // Note: if you want to override the automatic release value, do not set a 15 | // `release` value here - use the environment variable `SENTRY_RELEASE`, so 16 | // that it will also get attached to your source maps 17 | }) -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as SeparatorPrimitive from '@radix-ui/react-separator' 3 | 4 | import { cn } from 'lib/utils' 5 | 6 | const Separator = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( 10 | 21 | )) 22 | Separator.displayName = SeparatorPrimitive.Root.displayName 23 | 24 | export { Separator } 25 | -------------------------------------------------------------------------------- /src/utils/clearUserStates.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/react' 2 | import { Dispatch } from '@reduxjs/toolkit' 3 | import { resetUserState } from 'state/global/actions' 4 | import { connectorLocalStorageKey } from 'components/WalletModal' 5 | import getLocalStorageItemKeys from './getLocalStorageItemKeys' 6 | 7 | export const clearUserStates = (dispatch: Dispatch, chainId: number, isDeactive = false) => { 8 | dispatch(resetUserState({ chainId })) 9 | Sentry.configureScope((scope) => scope.setUser(null)) 10 | // Only clear localStorage when user disconnect,switch address no need clear it. 11 | if (isDeactive) { 12 | window?.localStorage?.removeItem(connectorLocalStorageKey) 13 | } 14 | const lsOrderKeys = getLocalStorageItemKeys('gorders_') 15 | lsOrderKeys.forEach((lsOrderKey) => window?.localStorage?.removeItem(lsOrderKey)) 16 | } 17 | -------------------------------------------------------------------------------- /src/hooks/useScadsMint.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | import { scadsMint, scadsSell } from 'utils/calls' 3 | import { useScadsContract } from 'hooks/useContract' 4 | import { utils } from 'ethers' 5 | 6 | const useScadsMint = () => { 7 | const scadsContract = useScadsContract() 8 | 9 | const handleMint = useCallback( 10 | async (amount: string, token: string) => { 11 | const txHash = await scadsMint(scadsContract, utils.parseEther(amount), token) 12 | }, 13 | [scadsContract], 14 | ) 15 | 16 | const handleSell = useCallback( 17 | async (amount: string, token: string) => { 18 | const txHash = await scadsSell(scadsContract, utils.parseEther(amount), token) 19 | }, 20 | [scadsContract], 21 | ) 22 | 23 | return { scadsMint: handleMint, scadsSell: handleSell } 24 | } 25 | 26 | export default useScadsMint 27 | -------------------------------------------------------------------------------- /packages/wagmi/src/hooks/useSignMessage.ts: -------------------------------------------------------------------------------- 1 | import { SignMessageArgs } from '@wagmi/core' 2 | import { useCallback } from 'react' 3 | import { useAccount, useSignMessage as useSignMessageWagmi } from 'wagmi' 4 | 5 | export function useSignMessage() { 6 | const { address, connector } = useAccount() 7 | const { signMessageAsync: sign } = useSignMessageWagmi() 8 | 9 | return { 10 | signMessageAsync: useCallback( 11 | async (args: SignMessageArgs) => { 12 | if (connector?.id === 'bsc' && window.BinanceChain && address) { 13 | const res = await window.BinanceChain.bnbSign?.(address, args.message as string) 14 | if (res) { 15 | return res.signature 16 | } 17 | return null 18 | } 19 | return sign(args) 20 | }, 21 | [address, connector?.id, sign], 22 | ), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/hooks/ENS/useENS.ts: -------------------------------------------------------------------------------- 1 | import { isAddress } from '../../utils' 2 | import useENSAddress from './useENSAddress' 3 | import useENSName from './useENSName' 4 | 5 | /** 6 | * Given a name or address, does a lookup to resolve to an address and name 7 | * @param nameOrAddress ENS name or address 8 | */ 9 | export default function useENS(nameOrAddress?: string | null): { 10 | loading: boolean 11 | address: string | null 12 | name: string | null 13 | } { 14 | const validated = isAddress(nameOrAddress) 15 | const reverseLookup = useENSName(validated || undefined) 16 | const lookup = useENSAddress(nameOrAddress) 17 | 18 | return { 19 | loading: reverseLookup.loading || lookup.loading, 20 | address: validated || lookup.address, 21 | name: reverseLookup.ENSName ? reverseLookup.ENSName : !validated && lookup.address ? nameOrAddress || null : null, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/wrappedCurrency.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, Currency, CurrencyAmount, ETHER, Token, TokenAmount, WETH } from '@scads/sdk' 2 | 3 | export function wrappedCurrency(currency: Currency | undefined, chainId: ChainId | undefined): Token | undefined { 4 | return chainId && currency === ETHER ? WETH[chainId] : currency instanceof Token ? currency : undefined 5 | } 6 | 7 | export function wrappedCurrencyAmount( 8 | currencyAmount: CurrencyAmount | undefined, 9 | chainId: ChainId | undefined, 10 | ): TokenAmount | undefined { 11 | const token = currencyAmount && chainId ? wrappedCurrency(currencyAmount.currency, chainId) : undefined 12 | return token && currencyAmount ? new TokenAmount(token, currencyAmount.raw) : undefined 13 | } 14 | 15 | export function unwrappedToken(token: Token): Currency { 16 | if (token.equals(WETH[token.chainId])) return ETHER 17 | return token 18 | } 19 | -------------------------------------------------------------------------------- /sentry.edge.config.js: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the server. 2 | // The config you add here will be used whenever the server handles a request. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import { init } from '@sentry/nextjs' 6 | 7 | const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN 8 | const ENV = process.env.VERCEL_ENV || process.env.NODE_ENV 9 | 10 | init({ 11 | dsn: SENTRY_DSN, 12 | environment: ENV === 'production' ? 'production' : 'development', 13 | // Adjust this value in production, or use tracesSampler for greater control 14 | tracesSampleRate: 0, 15 | // ... 16 | // Note: if you want to override the automatic release value, do not set a 17 | // `release` value here - use the environment variable `SENTRY_RELEASE`, so 18 | // that it will also get attached to your source maps 19 | }) -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, useMemo } from 'react' 2 | import { BLOCKED_ADDRESSES } from './config/constants' 3 | import useActiveWeb3React from './hooks/useActiveWeb3React' 4 | import ListsUpdater from './state/lists/updater' 5 | import MulticallUpdater from './state/multicall/updater' 6 | import TransactionUpdater from './state/transactions/updater' 7 | 8 | export function Updaters() { 9 | return ( 10 | <> 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | export function Blocklist({ children }: { children: ReactNode }) { 19 | const { account } = useActiveWeb3React() 20 | const blocked: boolean = useMemo(() => Boolean(account && BLOCKED_ADDRESSES.indexOf(account) !== -1), [account]) 21 | if (blocked) { 22 | return
Blocked address
23 | } 24 | return <>{children} 25 | } -------------------------------------------------------------------------------- /src/components/ui/close-icon.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { motion } from 'framer-motion' 4 | 5 | export const CloseIcon = () => { 6 | return ( 7 | 31 | 32 | 33 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/_components/stats/stats.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Separator } from 'components/ui/separator' 3 | import Treasury from './treasury' 4 | import GeneralStats from './general-stats' 5 | 6 | 7 | const Stats = () => { 8 | return ( 9 |
10 | 11 | 12 | 13 |
14 |
15 | ) 16 | } 17 | 18 | export default Stats 19 | -------------------------------------------------------------------------------- /packages/uikit/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withThemesProvider } from "themeprovider-storybook"; 3 | import light from "../src/theme/light"; 4 | import dark from "../src/theme/dark"; 5 | import ResetCSS from "../src/ResetCSS"; 6 | import { ModalProvider } from "../src/widgets/Modal"; 7 | 8 | const globalDecorator = (StoryFn) => ( 9 | 10 | 11 | 12 | 13 | ); 14 | 15 | export const parameters = { 16 | actions: { argTypesRegex: "^on[A-Z].*" }, 17 | layout: "fullscreen", 18 | }; 19 | 20 | const themes = [ 21 | { 22 | name: "Light", 23 | backgroundColor: light.colors.background, 24 | ...light, 25 | }, 26 | { 27 | name: "Dark", 28 | backgroundColor: dark.colors.background, 29 | ...dark, 30 | }, 31 | ]; 32 | 33 | export const decorators = [globalDecorator, withThemesProvider(themes)]; 34 | -------------------------------------------------------------------------------- /src/utils/uriToHttp.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-case-declarations */ 2 | /** 3 | * Given a URI that may be ipfs, ipns, http, or https protocol, return the fetch-able http(s) URLs for the same content 4 | * @param uri to convert to fetch-able http url 5 | */ 6 | export default function uriToHttp(uri: string): string[] { 7 | const protocol = uri.split(':')[0].toLowerCase() 8 | switch (protocol) { 9 | case 'https': 10 | return [uri] 11 | case 'http': 12 | return [`https${uri.substr(4)}`, uri] 13 | case 'ipfs': 14 | const hash = uri.match(/^ipfs:(\/\/)?(.*)$/i)?.[2] 15 | return [`https://cloudflare-ipfs.com/ipfs/${hash}/`, `https://ipfs.io/ipfs/${hash}/`] 16 | case 'ipns': 17 | const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2] 18 | return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`] 19 | default: 20 | return [] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/__tests__/utils/addressHelpers.test.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from 'utils/addressHelpers' 2 | 3 | describe('getAddress', () => { 4 | const address = { 5 | 56: '0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', 6 | 97: '0xa35062141Fa33BCA92Ce69FeD37D0E8908868AAe', 7 | } 8 | 9 | it(`get address for mainnet (chainId 56)`, () => { 10 | process.env.NEXT_PUBLIC_CHAIN_ID = '56' 11 | const expected = address[56] 12 | expect(getAddress(address)).toEqual(expected) 13 | }) 14 | it(`get address for testnet (chainId 97)`, () => { 15 | process.env.NEXT_PUBLIC_CHAIN_ID = '97' 16 | const expected = address[97] 17 | expect(getAddress(address)).toEqual(expected) 18 | }) 19 | it(`get address for any other network (chainId 31337)`, () => { 20 | process.env.NEXT_PUBLIC_CHAIN_ID = '31337' 21 | const expected = address[56] 22 | expect(getAddress(address)).toEqual(expected) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/uikit/src/__tests__/components/heading.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { renderWithTheme } from "../../testHelpers"; 3 | import Heading from "../../components/Heading/Heading"; 4 | 5 | it("renders correctly", () => { 6 | const { asFragment } = renderWithTheme(Title); 7 | expect(asFragment()).toMatchInlineSnapshot(` 8 | 9 | .c0 { 10 | color: #280D5F; 11 | font-size: 16px; 12 | font-weight: 600; 13 | line-height: 1.5; 14 | } 15 | 16 | .c1 { 17 | font-size: 20px; 18 | font-weight: 600; 19 | line-height: 1.1; 20 | } 21 | 22 | @media screen and (min-width:968px) { 23 | .c1 { 24 | font-size: 20px; 25 | } 26 | } 27 | 28 |

32 | Title 33 |

34 |
35 | `); 36 | }); 37 | -------------------------------------------------------------------------------- /src/pages/_components/roadmap/roadmap-item.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTranslation } from 'contexts/Localization' 3 | import { Calendar } from 'lucide-react' 4 | 5 | interface RoadmapItemProps { 6 | date: string 7 | l1: string 8 | l2?: string 9 | l3?: string 10 | } 11 | 12 | const RoadmapItem: React.FC = ({ date, l1, l2, l3 }) => { 13 | const { t } = useTranslation() 14 | 15 | return ( 16 |
  • 17 | 18 | 19 |
      20 |
    • {t(l1)}
    • 21 | {l2 &&
    • {t(l2)}
    • } 22 | {l3 &&
    • {t(l3)}
    • } 23 |
    24 |
  • 25 | ) 26 | } 27 | 28 | export default RoadmapItem 29 | -------------------------------------------------------------------------------- /src/utils/getRpcUrl.ts: -------------------------------------------------------------------------------- 1 | import sample from 'lodash/sample' 2 | 3 | if ( 4 | process.env.NODE_ENV !== 'production' && 5 | (!process.env.NEXT_PUBLIC_NODE_1 || !process.env.NEXT_PUBLIC_NODE_2 || !process.env.NEXT_PUBLIC_NODE_3) 6 | ) { 7 | throw Error('One base RPC URL is undefined') 8 | } 9 | 10 | // Array of available nodes to connect to 11 | export const nodes = [process.env.NEXT_PUBLIC_NODE_1, process.env.NEXT_PUBLIC_NODE_2, process.env.NEXT_PUBLIC_NODE_3] 12 | 13 | const getNodeUrl = () => { 14 | // Use custom node if available (both for development and production) 15 | // However on the testnet it wouldn't work, so if on testnet - comment out the NEXT_PUBLIC_NODE_PRODUCTION from env file 16 | if (process.env.NEXT_PUBLIC_NODE_PRODUCTION) { 17 | return process.env.NEXT_PUBLIC_NODE_PRODUCTION 18 | } 19 | 20 | console.log(sample(nodes)); 21 | 22 | return sample(nodes) 23 | } 24 | 25 | export default getNodeUrl 26 | -------------------------------------------------------------------------------- /src/components/WalletModal/config.tsx: -------------------------------------------------------------------------------- 1 | import { MetamaskIcon, WalletConnect, TrustWallet } from "@scads-io/uikit" 2 | 3 | import { Config, ConnectorNames } from "./types" 4 | 5 | const connectors: Config[] = [ 6 | { 7 | title: "Metamask", 8 | icon: MetamaskIcon, 9 | connectorId: ConnectorNames.MetaMask, 10 | priority: 1, 11 | href: "https://metamask.app.link/dapp/scads.io/" 12 | }, 13 | { 14 | title: "WalletConnect", 15 | icon: WalletConnect, 16 | connectorId: ConnectorNames.WalletConnect, 17 | priority: 2, 18 | }, 19 | { 20 | title: "Trust Wallet", 21 | icon: TrustWallet, 22 | connectorId: ConnectorNames.Injected, 23 | priority: 3, 24 | href: "https://link.trustwallet.com/open_url?coin_id=20000714&url=https://scads.io/", 25 | }, 26 | ] 27 | 28 | export default connectors 29 | export const connectorLocalStorageKey = "connectorIdv2" 30 | export const walletLocalStorageKey = "wallet" 31 | -------------------------------------------------------------------------------- /src/hooks/useIsWindowVisible.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | function isWindowVisible() { 4 | if (!(typeof document !== 'undefined' && 'visibilityState' in document)) { 5 | return true 6 | } 7 | 8 | return document.visibilityState === 'visible' 9 | } 10 | 11 | /** 12 | * Returns whether the window is currently visible to the user. 13 | */ 14 | export default function useIsWindowVisible() { 15 | const [isVisible, setIsVisible] = useState(isWindowVisible()) 16 | 17 | useEffect(() => { 18 | if (!('visibilityState' in document)) return undefined 19 | 20 | const handleVisibilityChange = () => { 21 | setIsVisible(isWindowVisible()) 22 | } 23 | 24 | document.addEventListener('visibilitychange', handleVisibilityChange) 25 | return () => { 26 | document.removeEventListener('visibilitychange', handleVisibilityChange) 27 | } 28 | }, [setIsVisible]) 29 | 30 | return isVisible 31 | } 32 | -------------------------------------------------------------------------------- /packages/eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@scads-io/eslint-config", 3 | "version": "1.2.1", 4 | "description": "Eslint config for Scads", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib" 8 | ], 9 | "repository": "https://github.com/scads-io/toolkit/tree/main/packages/eslint-config", 10 | "license": "MIT", 11 | "author": "RabbitDoge", 12 | "private": false, 13 | "dependencies": { 14 | "@typescript-eslint/eslint-plugin": "^4.7.0", 15 | "@typescript-eslint/parser": "^4.7.0", 16 | "eslint-config-airbnb": "^18.2.1", 17 | "eslint-config-prettier": "^6.15.0", 18 | "eslint-plugin-import": "^2.22.1", 19 | "eslint-plugin-jsx-a11y": "^6.4.1", 20 | "eslint-plugin-react": "^7.21.5", 21 | "eslint-plugin-react-hooks": "^4.0.0" 22 | }, 23 | "peerDependencies": { 24 | "eslint": "^7.2.0", 25 | "prettier": "^3.2.5" 26 | }, 27 | "publishConfig": { 28 | "access": "public" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/calls/usdPrice.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | export const useBtcUSDPrice = () => { 4 | const [price, setPrice] = useState(1) 5 | useEffect(() => { 6 | fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd') 7 | .then((res) => res.json()) 8 | .then((data) => { 9 | setPrice(data.bitcoin.usd) 10 | }) 11 | .catch((err) => { 12 | console.error(err) 13 | }) 14 | }, []) 15 | 16 | return price 17 | } 18 | 19 | export const useEthUSDPrice = () => { 20 | const [price, setPrice] = useState(1) 21 | useEffect(() => { 22 | fetch('https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd') 23 | .then((res) => res.json()) 24 | .then((data) => { 25 | setPrice(data.ethereum.usd) 26 | }) 27 | .catch((err) => { 28 | console.error(err) 29 | }) 30 | }, []) 31 | 32 | return price 33 | } 34 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@scads/sdk' 2 | import BigNumber from 'bignumber.js' 3 | import { BIG_TEN } from 'utils/bigNumber' 4 | 5 | BigNumber.config({ 6 | EXPONENTIAL_AT: 1000, 7 | DECIMAL_PLACES: 80, 8 | }) 9 | 10 | export const BSC_BLOCK_TIME = 3 11 | 12 | export const BASE_BSC_SCAN_URLS = { 13 | [ChainId.MAINNET]: 'https://bscscan.com', 14 | [ChainId.TESTNET]: 'https://testnet.bscscan.com', 15 | } 16 | 17 | export const SCADS_PER_BLOCK = 0.0001 18 | export const BLOCKS_PER_YEAR = (60 / BSC_BLOCK_TIME) * 60 * 24 * 365 // 10512000 19 | export const SCADS_PER_YEAR = SCADS_PER_BLOCK * BLOCKS_PER_YEAR 20 | export const BASE_URL = 'https://scads.io' 21 | export const BASE_ADD_LIQUIDITY_URL = `${BASE_URL}/add` 22 | export const BASE_BSC_SCAN_URL = BASE_BSC_SCAN_URLS[ChainId.MAINNET] 23 | export const DEFAULT_TOKEN_DECIMAL = BIG_TEN.pow(18) 24 | export const DEFAULT_GAS_LIMIT = 4000000 25 | export const IPFS_GATEWAY = 'https://ipfs.io/ipfs' 26 | -------------------------------------------------------------------------------- /src/utils/sentry.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/react' 2 | 3 | const assignError = (maybeError: any) => { 4 | if (typeof maybeError === 'string') { 5 | return new Error(maybeError) 6 | } 7 | if (typeof maybeError === 'object') { 8 | const error = new Error(maybeError?.message ?? String(maybeError)) 9 | if (maybeError?.stack) { 10 | error.stack = maybeError.stack 11 | } 12 | if (maybeError?.code) { 13 | error.name = maybeError.code 14 | } 15 | return error 16 | } 17 | return maybeError 18 | } 19 | 20 | export const isUserRejected = (err) => { 21 | // provider user rejected error code 22 | return typeof err === 'object' && 'code' in err && err.code === 4001 23 | } 24 | 25 | export const logError = (error: Error | unknown) => { 26 | if (error instanceof Error) { 27 | Sentry.captureException(error) 28 | } else { 29 | Sentry.captureException(assignError(error), error) 30 | } 31 | console.error(error) 32 | } 33 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Heading/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Heading from "./Heading"; 3 | 4 | export default { 5 | title: "Components/Heading", 6 | component: Heading, 7 | argTypes: {}, 8 | }; 9 | 10 | export const Sizes: React.FC = () => { 11 | return ( 12 |
    13 | Default 14 | Size md 15 | Size lg 16 | Size xl 17 | Size xxl 18 |
    19 | ); 20 | }; 21 | 22 | export const tags: React.FC = () => { 23 | return ( 24 |
    25 | Default 26 | Tag h1 27 | Tag h2 28 | Tag h3 29 | Tag h4 30 | Tag h5 31 | Tag h6 32 |
    33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/components/main-navigation/hamburger.tsx: -------------------------------------------------------------------------------- 1 | import React, { Dispatch, SetStateAction } from "react" 2 | import { cn } from "lib/utils" 3 | 4 | interface HamburgerProps { 5 | isOpen: boolean 6 | setIsOpen: Dispatch> 7 | } 8 | 9 | const Hamburger: React.FC = ({ isOpen, setIsOpen }) => { 10 | const hamburgerLine = "h-0.5 w-6 my-1 transition ease transform duration-300 bg-white" 11 | 12 | return ( 13 | 27 | ) 28 | } 29 | 30 | export default Hamburger 31 | -------------------------------------------------------------------------------- /src/__tests__/utils/truncateHash.test.ts: -------------------------------------------------------------------------------- 1 | import truncateHash from 'utils/truncateHash' 2 | 3 | describe('truncateHash', () => { 4 | it.each([ 5 | ['aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb', 'aaaa...bbbb', 4, 4], 6 | ['aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb', 'aaaaa...bbbb', 5, 4], 7 | ['aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb', 'a...bbbbb', 1, 5], 8 | ['aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb', 'aaaaaa...b', 6, 1], 9 | ['aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb', 'aaaaaaaa...bbbbbbbb', 8, 8], 10 | ])('truncates "%s" to "%s" correctly', (address, expected, startLength, endLength) => { 11 | expect(truncateHash(address, startLength, endLength)).toEqual(expected) 12 | }) 13 | 14 | it('formats address with default lengths of 4', () => { 15 | expect(truncateHash('a1a2a3a4a5a6a7a8n1n2n3n4n5n6n7n8')).toEqual('a1a2...n7n8') 16 | expect(truncateHash('cacacacacacacacaxzxzxzxzxzxzxzxz', 5)).toEqual('cacac...xzxz') 17 | expect(truncateHash('cacacacacacacacaxzxzxzxzxzxzxzxz', undefined, 9)).toEqual('caca...zxzxzxzxz') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /public/images/binance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/state/block/hooks.ts: -------------------------------------------------------------------------------- 1 | import useInterval from 'hooks/useInterval' 2 | import useIsWindowVisible from 'hooks/useIsWindowVisible' 3 | import { useSelector } from 'react-redux' 4 | import { useAppDispatch } from 'state' 5 | import { simpleRpcProvider } from 'utils/providers' 6 | import { setBlock } from '.' 7 | import { State } from '../types' 8 | 9 | export const usePollBlockNumber = (refreshTime = 6000) => { 10 | const dispatch = useAppDispatch() 11 | const isWindowVisible = useIsWindowVisible() 12 | 13 | useInterval( 14 | () => { 15 | const fetchBlock = async () => { 16 | const blockNumber = await simpleRpcProvider.getBlockNumber() 17 | dispatch(setBlock(blockNumber)) 18 | } 19 | 20 | fetchBlock() 21 | }, 22 | isWindowVisible ? refreshTime : null, 23 | true, 24 | ) 25 | } 26 | 27 | export const useBlock = () => { 28 | return useSelector((state: State) => state.block) 29 | } 30 | 31 | export const useInitialBlock = () => { 32 | return useSelector((state: State) => state.block.initialBlock) 33 | } 34 | -------------------------------------------------------------------------------- /src/hooks/useAccountEventListener.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useAppDispatch } from '../state' 3 | import { clearUserStates } from '../utils/clearUserStates' 4 | import useActiveWeb3React from './useActiveWeb3React' 5 | 6 | export const useAccountEventListener = () => { 7 | const { account, chainId, connector } = useActiveWeb3React() 8 | const dispatch = useAppDispatch() 9 | 10 | useEffect(() => { 11 | if (account && connector) { 12 | const handleUpdateEvent = () => { 13 | clearUserStates(dispatch, chainId) 14 | } 15 | 16 | const handleDeactiveEvent = () => { 17 | clearUserStates(dispatch, chainId, true) 18 | } 19 | 20 | connector.addListener('disconnect', handleDeactiveEvent) 21 | connector.addListener('change', handleUpdateEvent) 22 | 23 | return () => { 24 | connector.removeListener('disconnect', handleDeactiveEvent) 25 | connector.removeListener('change', handleUpdateEvent) 26 | } 27 | } 28 | return undefined 29 | }, [account, chainId, dispatch, connector]) 30 | } 31 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Heading/Heading.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import Text from "../Text/Text"; 3 | import { tags, scales, HeadingProps } from "./types"; 4 | import { KnownTarget } from "styled-components/dist/types"; 5 | 6 | const style = { 7 | [scales.MD]: { 8 | fontSize: "20px", 9 | fontSizeLg: "20px", 10 | }, 11 | [scales.LG]: { 12 | fontSize: "24px", 13 | fontSizeLg: "24px", 14 | }, 15 | [scales.XL]: { 16 | fontSize: "32px", 17 | fontSizeLg: "40px", 18 | }, 19 | [scales.XXL]: { 20 | fontSize: "48px", 21 | fontSizeLg: "64px", 22 | }, 23 | }; 24 | 25 | const Heading = styled(Text).attrs({ bold: true })` 26 | font-size: ${({ scale }) => style[scale || scales.MD].fontSize}; 27 | font-weight: 600; 28 | line-height: 1.1; 29 | 30 | ${({ theme }) => theme.mediaQueries.lg} { 31 | font-size: ${({ scale }) => style[scale || scales.MD].fontSizeLg}; 32 | } 33 | `; 34 | 35 | Heading.defaultProps = { 36 | as: tags.H2 as (KnownTarget & string) | undefined, 37 | }; 38 | 39 | export default Heading; 40 | -------------------------------------------------------------------------------- /src/components/ui/switch.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as SwitchPrimitives from '@radix-ui/react-switch' 3 | 4 | import { cn } from 'lib/utils' 5 | 6 | const Switch = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, ...props }, ref) => ( 10 | 18 | 23 | 24 | )) 25 | Switch.displayName = SwitchPrimitives.Root.displayName 26 | 27 | export { Switch } 28 | -------------------------------------------------------------------------------- /src/state/transactions/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | import { ChainId } from '@scads/sdk' 3 | 4 | export interface SerializableTransactionReceipt { 5 | to: string 6 | from: string 7 | contractAddress: string 8 | transactionIndex: number 9 | blockHash: string 10 | transactionHash: string 11 | blockNumber: number 12 | status?: number 13 | } 14 | 15 | export const addTransaction = createAction<{ 16 | chainId: ChainId 17 | hash: string 18 | from: string 19 | approval?: { tokenAddress: string; spender: string } 20 | claim?: { recipient: string } 21 | summary?: string 22 | }>('transactions/addTransaction') 23 | export const clearAllTransactions = createAction<{ chainId: ChainId }>('transactions/clearAllTransactions') 24 | export const finalizeTransaction = createAction<{ 25 | chainId: ChainId 26 | hash: string 27 | receipt: SerializableTransactionReceipt 28 | }>('transactions/finalizeTransaction') 29 | export const checkedTransaction = createAction<{ 30 | chainId: ChainId 31 | hash: string 32 | blockNumber: number 33 | }>('transactions/checkedTransaction') 34 | -------------------------------------------------------------------------------- /public/images/wallet-connect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/utils/trades.ts: -------------------------------------------------------------------------------- 1 | import { Trade, Percent, currencyEquals } from '@scads/sdk' 2 | import { ZERO_PERCENT, ONE_HUNDRED_PERCENT } from '../config/constants/index' 3 | 4 | // returns whether tradeB is better than tradeA by at least a threshold percentage amount 5 | export function isTradeBetter( 6 | tradeA: Trade | undefined | null, 7 | tradeB: Trade | undefined | null, 8 | minimumDelta: Percent = ZERO_PERCENT, 9 | ): boolean | undefined { 10 | if (tradeA && !tradeB) return false 11 | if (tradeB && !tradeA) return true 12 | if (!tradeA || !tradeB) return undefined 13 | 14 | if ( 15 | tradeA.tradeType !== tradeB.tradeType || 16 | !currencyEquals(tradeA.inputAmount.currency, tradeB.inputAmount.currency) || 17 | !currencyEquals(tradeB.outputAmount.currency, tradeB.outputAmount.currency) 18 | ) { 19 | throw new Error('Trades are not comparable') 20 | } 21 | 22 | if (minimumDelta.equalTo(ZERO_PERCENT)) { 23 | return tradeA.executionPrice.lessThan(tradeB.executionPrice) 24 | } 25 | return tradeA.executionPrice.raw.multiply(minimumDelta.add(ONE_HUNDRED_PERCENT)).lessThan(tradeB.executionPrice) 26 | } 27 | 28 | export default isTradeBetter 29 | -------------------------------------------------------------------------------- /src/contexts/RefreshContext.tsx: -------------------------------------------------------------------------------- 1 | import useInterval from 'hooks/useInterval' 2 | import useIsWindowVisible from 'hooks/useIsWindowVisible' 3 | import React, { useState } from 'react' 4 | 5 | const FAST_INTERVAL = 10000 6 | const SLOW_INTERVAL = 60000 7 | 8 | const createRefreshContext = (interval: number) => { 9 | const RefreshContext = React.createContext(0) 10 | return { 11 | Context: RefreshContext, 12 | Provider: ({ children }) => { 13 | const [count, setCount] = useState(0) 14 | 15 | useInterval( 16 | () => { 17 | setCount((c) => c + 1) 18 | }, 19 | useIsWindowVisible ? interval : null, 20 | false, 21 | ) 22 | 23 | return {children} 24 | }, 25 | } 26 | } 27 | 28 | export const SlowRefresh = createRefreshContext(SLOW_INTERVAL) 29 | export const FastRefresh = createRefreshContext(FAST_INTERVAL) 30 | 31 | const RefreshContextProvider = ({ children }) => { 32 | return ( 33 | 34 | {children} 35 | 36 | ) 37 | } 38 | 39 | export { RefreshContextProvider } 40 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Text/Text.tsx: -------------------------------------------------------------------------------- 1 | import styled, { DefaultTheme } from "styled-components"; 2 | import { space, typography, layout } from "styled-system"; 3 | import getThemeValue from "../../util/getThemeValue"; 4 | import { TextProps } from "./types"; 5 | 6 | interface ThemedProps extends TextProps { 7 | theme: DefaultTheme; 8 | } 9 | 10 | const getColor = ({ color, theme }: ThemedProps) => { 11 | return getThemeValue(`colors.${color}`, color)(theme); 12 | }; 13 | 14 | const getFontSize = ({ fontSize, small }: TextProps) => { 15 | return small ? "14px" : fontSize || "16px"; 16 | }; 17 | 18 | const Text = styled.div` 19 | color: ${getColor}; 20 | font-size: ${getFontSize}; 21 | font-weight: ${({ bold }) => (bold ? 600 : 400)}; 22 | line-height: 1.5; 23 | ${({ textTransform }) => textTransform && `text-transform: ${textTransform};`} 24 | ${({ ellipsis }) => 25 | ellipsis && 26 | `white-space: nowrap; 27 | overflow: hidden; 28 | text-overflow: ellipsis;`} 29 | 30 | ${space} 31 | ${typography} 32 | ${layout} 33 | `; 34 | 35 | Text.defaultProps = { 36 | color: "text", 37 | small: false, 38 | ellipsis: false, 39 | }; 40 | 41 | export default Text; 42 | -------------------------------------------------------------------------------- /src/state/user/hooks/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '@scads/sdk' 2 | import { SerializedToken } from 'config/constants/types' 3 | import { parseUnits } from 'ethers/lib/utils' 4 | 5 | export function serializeToken(token: Token): SerializedToken { 6 | return { 7 | chainId: token.chainId, 8 | address: token.address, 9 | decimals: token.decimals, 10 | symbol: token.symbol, 11 | name: token.name, 12 | projectLink: token.projectLink, 13 | } 14 | } 15 | 16 | export function deserializeToken(serializedToken: SerializedToken): Token { 17 | return new Token( 18 | serializedToken.chainId, 19 | serializedToken.address, 20 | serializedToken.decimals, 21 | serializedToken.symbol, 22 | serializedToken.name, 23 | serializedToken.projectLink, 24 | ) 25 | } 26 | 27 | export enum GAS_PRICE { 28 | default = '5', 29 | fast = '6', 30 | instant = '7', 31 | testnet = '10', 32 | } 33 | 34 | export const GAS_PRICE_GWEI = { 35 | default: parseUnits(GAS_PRICE.default, 'gwei').toString(), 36 | fast: parseUnits(GAS_PRICE.fast, 'gwei').toString(), 37 | instant: parseUnits(GAS_PRICE.instant, 'gwei').toString(), 38 | testnet: parseUnits(GAS_PRICE.testnet, 'gwei').toString(), 39 | } 40 | -------------------------------------------------------------------------------- /src/__tests__/utils/uriToHttp.test.ts: -------------------------------------------------------------------------------- 1 | import uriToHttp from 'utils/uriToHttp' 2 | 3 | describe('uriToHttp', () => { 4 | it('returns .eth.link for ens names', () => { 5 | expect(uriToHttp('t2crtokens.eth')).toEqual([]) 6 | }) 7 | it('returns https first for http', () => { 8 | expect(uriToHttp('http://test.com')).toEqual(['https://test.com', 'http://test.com']) 9 | }) 10 | it('returns https for https', () => { 11 | expect(uriToHttp('https://test.com')).toEqual(['https://test.com']) 12 | }) 13 | it('returns ipfs gateways for ipfs:// urls', () => { 14 | expect(uriToHttp('ipfs://QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ')).toEqual([ 15 | 'https://cloudflare-ipfs.com/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/', 16 | 'https://ipfs.io/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/', 17 | ]) 18 | }) 19 | it('returns ipns gateways for ipns:// urls', () => { 20 | expect(uriToHttp('ipns://app.uniswap.org')).toEqual([ 21 | 'https://cloudflare-ipfs.com/ipns/app.uniswap.org/', 22 | 'https://ipfs.io/ipns/app.uniswap.org/', 23 | ]) 24 | }) 25 | it('returns empty array for invalid scheme', () => { 26 | expect(uriToHttp('blah:test')).toEqual([]) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/components/Layout/Row.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Box } from '@scads-io/uikit' 3 | 4 | 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 | 22 | export const RowBetween = styled(Row)` 23 | justify-content: space-between; 24 | ` 25 | 26 | export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>` 27 | flex-wrap: wrap; 28 | margin: ${({ gap }) => gap && `-${gap}`}; 29 | justify-content: ${({ justify }) => justify && justify}; 30 | 31 | & > * { 32 | margin: ${({ gap }) => gap} !important; 33 | } 34 | ` 35 | 36 | export const RowFixed = styled(Row)<{ gap?: string; justify?: string }>` 37 | width: fit-content; 38 | margin: ${({ gap }) => gap && `-${gap}`}; 39 | ` 40 | 41 | export default Row 42 | -------------------------------------------------------------------------------- /src/state/swap/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@reduxjs/toolkit' 2 | import { DerivedPairDataNormalized, PairDataNormalized, PairDataTimeWindowEnum } from './types' 3 | 4 | export enum Field { 5 | INPUT = 'INPUT', 6 | OUTPUT = 'OUTPUT', 7 | } 8 | 9 | export const selectCurrency = createAction<{ field: Field; currencyId: string }>('swap/selectCurrency') 10 | export const switchCurrencies = createAction('swap/switchCurrencies') 11 | export const typeInput = createAction<{ field: Field; typedValue: string }>('swap/typeInput') 12 | export const replaceSwapState = createAction<{ 13 | field: Field 14 | typedValue: string 15 | inputCurrencyId?: string 16 | outputCurrencyId?: string 17 | recipient: string | null 18 | }>('swap/replaceSwapState') 19 | export const setRecipient = createAction<{ recipient: string | null }>('swap/setRecipient') 20 | export const updatePairData = 21 | createAction<{ pairData: PairDataNormalized; pairId: string; timeWindow: PairDataTimeWindowEnum }>( 22 | 'swap/updatePairData', 23 | ) 24 | export const updateDerivedPairData = 25 | createAction<{ pairData: DerivedPairDataNormalized; pairId: string; timeWindow: PairDataTimeWindowEnum }>( 26 | 'swap/updateDerivedPairData', 27 | ) 28 | -------------------------------------------------------------------------------- /src/utils/addressHelpers.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@scads/sdk' 2 | import addresses from 'config/constants/contracts' 3 | import { Address } from 'config/constants/types' 4 | 5 | export const getAddress = (address: Address): string => { 6 | const chainId = process.env.NEXT_PUBLIC_CHAIN_ID 7 | return address[chainId] ? address[chainId] : address[ChainId.MAINNET] 8 | } 9 | 10 | export const getMasterChefAddress = () => { 11 | return getAddress(addresses.masterChef) 12 | } 13 | export const getMulticallAddress = () => { 14 | return getAddress(addresses.multiCall) 15 | } 16 | export const getScadsProfileAddress = () => { 17 | return getAddress(addresses.scadsProfile) 18 | } 19 | export const getTreasuryAddress = () => { 20 | return getAddress(addresses.treasury) 21 | } 22 | export const getScadsAddress = () => { 23 | return getAddress(addresses.scads) 24 | } 25 | export const getCaratAddress = () => { 26 | return getAddress(addresses.carat) 27 | } 28 | export const getPulseAddress = () => { 29 | return getAddress(addresses.pulse) 30 | } 31 | export const getScadsPoolAddress = () => { 32 | return getAddress(addresses.scadsPool) 33 | } 34 | export const getCaratPoolAddress = () => { 35 | return getAddress(addresses.caratPool) 36 | } 37 | -------------------------------------------------------------------------------- /src/state/lists/actions.ts: -------------------------------------------------------------------------------- 1 | import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit' 2 | import { TokenList, Version } from '@uniswap/token-lists' 3 | 4 | export const fetchTokenList: Readonly<{ 5 | pending: ActionCreatorWithPayload<{ url: string; requestId: string }> 6 | fulfilled: ActionCreatorWithPayload<{ url: string; tokenList: TokenList; requestId: string }> 7 | rejected: ActionCreatorWithPayload<{ url: string; errorMessage: string; requestId: string }> 8 | }> = { 9 | pending: createAction('lists/fetchTokenList/pending'), 10 | fulfilled: createAction('lists/fetchTokenList/fulfilled'), 11 | rejected: createAction('lists/fetchTokenList/rejected'), 12 | } 13 | // add and remove from list options 14 | export const addList = createAction('lists/addList') 15 | export const removeList = createAction('lists/removeList') 16 | 17 | // select which lists to search across from loaded lists 18 | export const enableList = createAction('lists/enableList') 19 | export const disableList = createAction('lists/disableList') 20 | 21 | // versioning 22 | export const acceptListUpdate = createAction('lists/acceptListUpdate') 23 | export const rejectVersionUpdate = createAction('lists/rejectVersionUpdate') 24 | -------------------------------------------------------------------------------- /src/components/Toast/DescriptionWithTx.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, Text } from '@scads-io/uikit' 3 | import { getBscScanLink } from 'utils' 4 | import useActiveWeb3React from 'hooks/useActiveWeb3React' 5 | import { useTranslation } from 'contexts/Localization' 6 | import { useThemeManager } from 'state/user/hooks' 7 | import truncateHash from 'utils/truncateHash' 8 | 9 | interface DescriptionWithTxProps { 10 | // description?: string 11 | txHash?: string 12 | } 13 | 14 | const DescriptionWithTx: React.FC = ({ txHash, children }) => { 15 | const { chainId } = useActiveWeb3React() 16 | const [isDark] = useThemeManager() 17 | const { t } = useTranslation() 18 | 19 | return ( 20 | <> 21 | {typeof children === 'string' ? {children} : children} 22 | {txHash && ( 23 | 24 | 29 | {t('View on BscScan')}: {truncateHash(txHash, 8, 0)} 30 | 31 | 32 | )} 33 | 34 | ) 35 | } 36 | 37 | export default DescriptionWithTx 38 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as Sentry from '@sentry/react' 3 | import { motion } from 'framer-motion' 4 | import { IoInformation } from 'react-icons/io5' 5 | import { Button } from 'components/ui/button' 6 | 7 | export default function ErrorBoundary({ children }) { 8 | return ( 9 | { 11 | return ( 12 |
    13 | 14 | 21 | Oops, something wrong. 22 | 23 | 28 |
    29 | ) 30 | }} 31 | > 32 | {children} 33 |
    34 | ) 35 | } -------------------------------------------------------------------------------- /src/pages/whitepaper/_components/contact.tsx: -------------------------------------------------------------------------------- 1 | const data = { 2 | end: "End of White Paper", 3 | header: "13. Contact Information:", 4 | text_1: 5 | "Contact Details for Further Inquiries: TG Support @scads_support, Alexander @scadswp; email: j@scads.io", 6 | text_2: 7 | "Investment and Partnership Opportunities: TG @scads_support, Alexander @scadswp", 8 | }; 9 | 10 | const Contact = () => { 11 | return ( 12 |
    13 |

    14 | {data.header} 15 |

    16 |

    17 | 18 | 19 | {data.text_1} 20 | 21 | 22 | 23 | {data.text_2} 24 | 25 |
    26 | {data.end} 27 |

    28 |
    29 | ); 30 | }; 31 | 32 | export default Contact; 33 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const { default: flattenColorPalette } = require('tailwindcss/lib/util/flattenColorPalette') 4 | 5 | const config = { 6 | darkMode: 'class', 7 | content: ['./pages/**/*.{ts,tsx}', './components/**/*.{ts,tsx}', './app/**/*.{ts,tsx}', './src/**/*.{ts,tsx}'], 8 | prefix: '', 9 | theme: { 10 | container: { 11 | center: true, 12 | padding: '2rem', 13 | screens: { 14 | '2xl': '1400px', 15 | }, 16 | }, 17 | extend: { 18 | fontFamily: { 19 | sans: ['var(--sf-pro)'], 20 | }, 21 | colors: { 22 | 'dark-blue': '#030014', 23 | }, 24 | animation: { 25 | 'accordion-down': 'accordion-down 0.2s ease-out', 26 | 'accordion-up': 'accordion-up 0.2s ease-out', 27 | }, 28 | keyframes: { 29 | 'accordion-down': { 30 | from: { height: '0' }, 31 | to: { height: 'var(--radix-accordion-content-height)' }, 32 | }, 33 | 'accordion-up': { 34 | from: { height: 'var(--radix-accordion-content-height)' }, 35 | to: { height: '0' }, 36 | }, 37 | }, 38 | }, 39 | }, 40 | plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')], 41 | } satisfies Config 42 | 43 | export default config 44 | -------------------------------------------------------------------------------- /src/components/footer/footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import { usePathname } from 'next/navigation' 4 | import { cn } from 'lib/utils' 5 | import Socials from '../socials' 6 | import Disclaimer from './disclaimer' 7 | 8 | const Footer = () => { 9 | const currentYear = new Date().getFullYear() 10 | const pathName = usePathname() 11 | 12 | return ( 13 |
    19 |
    20 |
    21 | logo 22 | SCADS 23 |
    24 |

    25 | SCADS © Copyright {currentYear}. All rights reserved. ™ 26 |

    27 |
    28 | 29 | 30 |
    31 | ) 32 | } 33 | 34 | export default Footer 35 | -------------------------------------------------------------------------------- /src/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as TooltipPrimitive from '@radix-ui/react-tooltip' 3 | 4 | import { cn } from 'lib/utils' 5 | 6 | const TooltipProvider = TooltipPrimitive.Provider 7 | 8 | const Tooltip = TooltipPrimitive.Root 9 | 10 | const TooltipTrigger = TooltipPrimitive.Trigger 11 | 12 | const TooltipContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, sideOffset = 4, ...props }, ref) => ( 16 | 25 | )) 26 | TooltipContent.displayName = TooltipPrimitive.Content.displayName 27 | 28 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } 29 | -------------------------------------------------------------------------------- /src/components/Loader/CircleLoader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { keyframes } 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<{ size: string; stroke?: string }>` 14 | animation: 2s ${rotate} linear infinite; 15 | height: ${({ size }) => size}; 16 | width: ${({ size }) => size}; 17 | path { 18 | stroke: ${({ stroke, theme }) => stroke ?? theme.colors.primary}; 19 | } 20 | ` 21 | 22 | /** 23 | * Takes in custom size and stroke for circle color, default to primary color as fill, 24 | * need ...rest for layered styles on top 25 | */ 26 | export default function CircleLoader({ 27 | size = '16px', 28 | stroke, 29 | ...rest 30 | }: { 31 | size?: string 32 | stroke?: string 33 | [k: string]: any 34 | }) { 35 | return ( 36 | 37 | 43 | 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /packages/uikit/src/testHelpers.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react"; 2 | import { render, RenderResult } from "@testing-library/react"; 3 | import { ThemeProvider } from "styled-components"; 4 | import { light } from "./theme"; 5 | 6 | /* eslint-disable import/prefer-default-export */ 7 | export const renderWithTheme = (component: ReactNode, theme = light): RenderResult => { 8 | return render({component}); 9 | }; 10 | 11 | export const setupMockIntersectionObserver = (): void => { 12 | /* eslint-disable class-methods-use-this */ 13 | class MockIntersectionObserver { 14 | readonly root: Element | null; 15 | 16 | readonly rootMargin: string; 17 | 18 | readonly thresholds: ReadonlyArray; 19 | 20 | constructor() { 21 | this.root = null; 22 | this.rootMargin = ""; 23 | this.thresholds = []; 24 | } 25 | 26 | disconnect() { 27 | return jest.fn; 28 | } 29 | 30 | observe() { 31 | return jest.fn; 32 | } 33 | 34 | takeRecords(): IntersectionObserverEntry[] { 35 | return []; 36 | } 37 | 38 | unobserve() { 39 | return jest.fn; 40 | } 41 | } 42 | 43 | Object.defineProperty(window, "IntersectionObserver", { 44 | writable: true, 45 | configurable: true, 46 | value: MockIntersectionObserver, 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Box/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import BoxComponent from "./Box"; 3 | import FlexComponent from "./Flex"; 4 | import GridComponent from "./Grid"; 5 | import Text from "../Text/Text"; 6 | import { Link } from "../Link"; 7 | 8 | export default { 9 | title: "Components/Primitives", 10 | component: BoxComponent, 11 | argTypes: {}, 12 | }; 13 | 14 | export const Box: React.FC = () => { 15 | return ( 16 |
    17 | 18 | Contains background, border, layout, position, and space from{" "} 19 | 20 | Styled System‘s API 21 | 22 | 23 |
    24 | ); 25 | }; 26 | 27 | export const Flex: React.FC = () => { 28 | return ( 29 |
    30 | Based on the Box component. You can apply any flexbox properties on the Flex component. 31 | 32 | List of applicable props 33 | 34 | 35 | Left 36 | right 37 | 38 | 39 | center 40 | 41 |
    42 | ); 43 | }; -------------------------------------------------------------------------------- /packages/uikit/src/__tests__/components/svg.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { renderWithTheme } from "../../testHelpers"; 3 | import { Svg } from "../../components/Svg"; 4 | 5 | it("renders correctly", () => { 6 | const { asFragment } = renderWithTheme( 7 | 8 | 9 | 10 | ); 11 | expect(asFragment()).toMatchInlineSnapshot(` 12 | 13 | .c0 { 14 | -webkit-align-self: center; 15 | -ms-flex-item-align: center; 16 | align-self: center; 17 | fill: #280D5F; 18 | -webkit-flex-shrink: 0; 19 | -ms-flex-negative: 0; 20 | flex-shrink: 0; 21 | } 22 | 23 | 30 | 33 | 34 | 35 | `); 36 | }); 37 | -------------------------------------------------------------------------------- /src/state/types.ts: -------------------------------------------------------------------------------- 1 | import { ThunkAction } from 'redux-thunk' 2 | import { AnyAction } from '@reduxjs/toolkit' 3 | 4 | export type AppThunk = ThunkAction 5 | 6 | export interface BigNumberToJson { 7 | type: 'BigNumber' 8 | hex: string 9 | } 10 | 11 | export type SerializedBigNumber = string 12 | 13 | export interface Profile { 14 | userId: number 15 | points: number 16 | teamId: number 17 | collectionAddress: string 18 | tokenId: number 19 | isActive: boolean 20 | username: string 21 | hasRegistered: boolean 22 | } 23 | 24 | // Slices states 25 | 26 | export enum ProfileAvatarFetchStatus { 27 | NOT_FETCHED = 'not-fetched', 28 | FETCHING = 'fetching', 29 | FETCHED = 'fetched', 30 | } 31 | 32 | export interface ProfileState { 33 | isInitialized: boolean 34 | isLoading: boolean 35 | hasRegistered: boolean 36 | data: Profile 37 | profileAvatars: { 38 | [key: string]: { 39 | username: string 40 | hasRegistered: boolean 41 | usernameFetchStatus: ProfileAvatarFetchStatus 42 | avatarFetchStatus: ProfileAvatarFetchStatus 43 | } 44 | } 45 | } 46 | 47 | // Block 48 | 49 | export interface BlockState { 50 | currentBlock: number 51 | initialBlock: number 52 | } 53 | 54 | // Global state 55 | 56 | export interface State { 57 | block: BlockState 58 | profile: ProfileState 59 | } 60 | -------------------------------------------------------------------------------- /src/components/Toast/ToastContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { TransitionGroup } from 'react-transition-group' 3 | import styled from 'styled-components' 4 | import Toast from './Toast' 5 | import { ToastContainerProps } from './types' 6 | 7 | const ZINDEX = 1000 8 | const TOP_POSITION = 80 // Initial position from the top 9 | 10 | const StyledToastContainer = styled.div` 11 | .enter, 12 | .appear { 13 | opacity: 0.01; 14 | } 15 | 16 | .enter.enter-active, 17 | .appear.appear-active { 18 | opacity: 1; 19 | transition: opacity 250ms ease-in; 20 | } 21 | 22 | .exit { 23 | opacity: 1; 24 | } 25 | 26 | .exit.exit-active { 27 | opacity: 0.01; 28 | transition: opacity 250ms ease-out; 29 | } 30 | ` 31 | 32 | const ToastContainer: React.FC = ({ toasts, onRemove, ttl = 6000, stackSpacing = 24 }) => { 33 | return ( 34 | 35 | 36 | {toasts.map((toast, index) => { 37 | const zIndex = (ZINDEX - index).toString() 38 | const top = TOP_POSITION + index * stackSpacing 39 | 40 | return ( 41 | 42 | ) 43 | })} 44 | 45 | 46 | ) 47 | } 48 | 49 | export default ToastContainer 50 | -------------------------------------------------------------------------------- /src/hooks/ENS/useENSContentHash.ts: -------------------------------------------------------------------------------- 1 | import { namehash } from 'ethers/lib/utils' 2 | import { useMemo } from 'react' 3 | import { useSingleCallResult } from '../../state/multicall/hooks' 4 | import isZero from '../../utils/isZero' 5 | import { useENSRegistrarContract, useENSResolverContract } from '../useContract' 6 | 7 | /** 8 | * Does a lookup for an ENS name to find its contenthash. 9 | */ 10 | export default function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } { 11 | const ensNodeArgument = useMemo(() => { 12 | if (!ensName) return [undefined] 13 | try { 14 | return ensName ? [namehash(ensName)] : [undefined] 15 | } catch (error) { 16 | return [undefined] 17 | } 18 | }, [ensName]) 19 | const registrarContract = useENSRegistrarContract(false) 20 | const resolverAddressResult = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument) 21 | const resolverAddress = resolverAddressResult.result?.[0] 22 | const resolverContract = useENSResolverContract( 23 | resolverAddress && isZero(resolverAddress) ? undefined : resolverAddress, 24 | false, 25 | ) 26 | const contenthash = useSingleCallResult(resolverContract, 'contenthash', ensNodeArgument) 27 | 28 | return { 29 | contenthash: contenthash.result?.[0] ?? null, 30 | loading: resolverAddressResult.loading || contenthash.loading, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/_components/roadmap/roadmap.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { roadmapContent } from 'constants/content' 3 | import { LampContainer } from 'components/ui/lamp-effect' 4 | import SectionHeader from 'components/ui/section-header' 5 | import AnimatedBackground from 'components/ui/animated-background' 6 | import RoadmapItem from './roadmap-item' 7 | 8 | const Roadmap = () => { 9 | return ( 10 |
    11 | 17 | 18 |
    19 |
    20 |
      21 | {roadmapContent.map((item) => ( 22 | 23 | ))} 24 |
    25 |
    26 |
    27 | 28 |
    29 | ) 30 | } 31 | 32 | export default Roadmap 33 | -------------------------------------------------------------------------------- /src/__tests__/config/tokens.test.ts: -------------------------------------------------------------------------------- 1 | import map from 'lodash/map' 2 | import omitBy from 'lodash/omitBy' 3 | import erc20ABI from 'config/abi/erc20.json' 4 | import tokens from 'config/constants/tokens' 5 | import { Token } from '@scads/sdk' 6 | import multicall from 'utils/multicall' 7 | 8 | // remove BNB because it's not a Bep20 token 9 | // remove ONE because there are two tokens with the symbol ONE (Harmony ONE and BigONE) 10 | // remove HERO because there are two tokens with the symbol HERO (StepHero and Hero) 11 | const tokensToTest = omitBy( 12 | tokens, 13 | (token) => 14 | token.symbol.toLowerCase() === 'bnb' || 15 | token.symbol.toLowerCase() === 'one' || 16 | token.symbol.toLowerCase() === 'hero', 17 | ) 18 | 19 | describe('Config tokens', () => { 20 | it.each(map(tokensToTest, (token, key) => [key, token]))( 21 | 'Token %s has the correct key, symbol, and decimal', 22 | async (key, token: Token) => { 23 | const [[symbol], [decimals]] = await multicall(erc20ABI, [ 24 | { 25 | address: token.address, 26 | name: 'symbol', 27 | }, 28 | { 29 | address: token.address, 30 | name: 'decimals', 31 | }, 32 | ]) 33 | 34 | expect(key).toBe(token.symbol.toLowerCase()) 35 | expect(token.symbol.toLowerCase()).toBe(symbol.toLowerCase()) 36 | expect(token.decimals).toBe(parseInt(decimals, 10)) 37 | }, 38 | ) 39 | }) 40 | -------------------------------------------------------------------------------- /src/state/mint/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from '@reduxjs/toolkit' 2 | import { Field, resetMintState, typeInput } from './actions' 3 | 4 | export interface MintState { 5 | readonly independentField: Field 6 | readonly typedValue: string 7 | readonly otherTypedValue: string // for the case when there's no liquidity 8 | } 9 | 10 | const initialState: MintState = { 11 | independentField: Field.CURRENCY_A, 12 | typedValue: '', 13 | otherTypedValue: '', 14 | } 15 | 16 | export default createReducer(initialState, (builder) => 17 | builder 18 | .addCase(resetMintState, () => initialState) 19 | .addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => { 20 | if (noLiquidity) { 21 | // they're typing into the field they've last typed in 22 | if (field === state.independentField) { 23 | return { 24 | ...state, 25 | independentField: field, 26 | typedValue, 27 | } 28 | } 29 | // they're typing into a new field, store the other value 30 | 31 | return { 32 | ...state, 33 | independentField: field, 34 | typedValue, 35 | otherTypedValue: state.typedValue, 36 | } 37 | } 38 | return { 39 | ...state, 40 | independentField: field, 41 | typedValue, 42 | otherTypedValue: '', 43 | } 44 | }), 45 | ) 46 | -------------------------------------------------------------------------------- /packages/wagmi/src/provider.tsx: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from '@ethersproject/providers' 2 | import React, { createContext, useContext } from 'react' 3 | import useSWRImmutable from 'swr/immutable' 4 | import { useAccount, WagmiConfig, WagmiConfigProps, useNetwork } from 'wagmi' 5 | import { Provider, WebSocketProvider } from '@wagmi/core' 6 | 7 | export function WagmiProvider( 8 | props: React.PropsWithChildren>, 9 | ) { 10 | return ( 11 | 12 | {props.children} 13 | 14 | ) 15 | } 16 | 17 | const Web3LibraryContext = createContext(undefined) 18 | 19 | export const useWeb3LibraryContext = () => { 20 | return useContext(Web3LibraryContext) 21 | } 22 | 23 | // eslint-disable-next-line @typescript-eslint/ban-types 24 | const Web3LibraryProvider: React.FC> = (props) => { 25 | const { connector } = useAccount() 26 | const { chain } = useNetwork() 27 | const { data: library } = useSWRImmutable(connector && ['web3-library', connector, chain], async () => { 28 | const provider = await connector?.getProvider() 29 | return new Web3Provider(provider) 30 | }) 31 | 32 | return {props.children} 33 | } 34 | -------------------------------------------------------------------------------- /src/hooks/useCaratMint.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | import { caratBuy, caratRedeem, caratClaim, addAutoCompoundAddress, removeAutoCompoundAddress } from 'utils/calls' 3 | import { useCaratContract } from 'hooks/useContract' 4 | 5 | const useCaratMint = () => { 6 | const caratContract = useCaratContract() 7 | 8 | const handleBuy = useCallback( 9 | async (amount: string) => { 10 | const txHash = await caratBuy(caratContract, amount) 11 | }, 12 | [caratContract], 13 | ) 14 | 15 | const handleRedeem = useCallback( 16 | async (amount: string) => { 17 | const txHash = await caratRedeem(caratContract, amount) 18 | }, 19 | [caratContract], 20 | ) 21 | 22 | const handleClaim = useCallback(async () => { 23 | const txHash = await caratClaim(caratContract) 24 | }, [caratContract]) 25 | 26 | const handleAddAutoCompoundAddress = useCallback(async () => { 27 | const txHash = await addAutoCompoundAddress(caratContract) 28 | }, [caratContract]) 29 | 30 | const handleRemoveAutoCompoundAddress = useCallback(async () => { 31 | const txHash = await removeAutoCompoundAddress(caratContract) 32 | }, [caratContract]) 33 | 34 | return { 35 | caratBuy: handleBuy, 36 | caratRedeem: handleRedeem, 37 | caratClaim: handleClaim, 38 | addCompoundAddress: handleAddAutoCompoundAddress, 39 | removeCompoundAddress: handleRemoveAutoCompoundAddress, 40 | } 41 | } 42 | 43 | export default useCaratMint 44 | -------------------------------------------------------------------------------- /src/components/WalletModal/wallet-list.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import useAuth from 'hooks/useAuth' 3 | import config, { walletLocalStorageKey } from "./config" 4 | import { Config } from "./types" 5 | import WalletCard from "./WalletCard" 6 | 7 | interface Props { 8 | onDismiss: () => void 9 | } 10 | 11 | const getPreferredConfig = (walletConfig: Config[]) => { 12 | const preferredWalletName = localStorage.getItem(walletLocalStorageKey) 13 | const sortedConfig = walletConfig.sort((a: Config, b: Config) => a.priority - b.priority) 14 | 15 | if (!preferredWalletName) { 16 | return sortedConfig 17 | } 18 | 19 | const preferredWallet = sortedConfig.find((sortedWalletConfig) => sortedWalletConfig.title === preferredWalletName) 20 | 21 | if (!preferredWallet) { 22 | return sortedConfig 23 | } 24 | 25 | return [ 26 | preferredWallet, 27 | ...sortedConfig.filter((sortedWalletConfig) => sortedWalletConfig.title !== preferredWalletName), 28 | ] 29 | } 30 | 31 | const WalletList: React.FC = ({ onDismiss }) => { 32 | const { login } = useAuth() 33 | const sortedConfig = getPreferredConfig(config) 34 | 35 | return ( 36 |
      37 | {sortedConfig.map((wallet) => ( 38 | 39 | ))} 40 |
    41 | ) 42 | } 43 | 44 | export default WalletList 45 | -------------------------------------------------------------------------------- /src/Providers.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { light, dark } from '@scads-io/uikit' 3 | import { Provider } from 'react-redux' 4 | import { WagmiProvider } from '@scads-io/wagmi' 5 | import { client } from 'utils/wagmi' 6 | import { ThemeProvider } from 'styled-components' 7 | import { useThemeManager } from 'state/user/hooks' 8 | import { LanguageProvider } from 'contexts/Localization' 9 | import { RefreshContextProvider } from 'contexts/RefreshContext' 10 | import { ToastsProvider } from 'contexts/ToastsContext' 11 | import ToasterProvider from 'contexts/ToasterProvider' 12 | import { Store } from '@reduxjs/toolkit' 13 | 14 | const ThemeProviderWrapper = (props) => { 15 | const [isDark] = useThemeManager() 16 | return 17 | } 18 | 19 | const Providers: React.FC> = ({ 20 | children, 21 | store 22 | }) => { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {children} 32 | 33 | 34 | 35 | 36 | 37 | 38 | ) 39 | } 40 | 41 | export default Providers 42 | -------------------------------------------------------------------------------- /src/pages/tokenomics/_components/tokenomics-card.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useTranslation } from 'contexts/Localization' 4 | import { motion } from 'framer-motion' 5 | 6 | interface TokenomicsCardProps { 7 | title: string 8 | setActive: () => void 9 | id: string 10 | preview: string 11 | } 12 | 13 | const TokenomicsCard: React.FC = ({ title, setActive, id, preview }) => { 14 | const { t } = useTranslation() 15 | 16 | return ( 17 | 23 |
    24 | 25 | {t(title)} 26 | 27 | 28 | {t(preview)} 29 | 30 |
    31 | 35 | {t('Details')} 36 | 37 |
    38 | ) 39 | } 40 | 41 | export default TokenomicsCard 42 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Button/theme.ts: -------------------------------------------------------------------------------- 1 | import { scales, variants } from "./types"; 2 | 3 | export const scaleVariants = { 4 | [scales.MD]: { 5 | height: "48px", 6 | padding: "0 24px", 7 | }, 8 | [scales.SM]: { 9 | height: "32px", 10 | padding: "0 16px", 11 | }, 12 | [scales.XS]: { 13 | height: "20px", 14 | fontSize: "12px", 15 | padding: "0 8px", 16 | }, 17 | }; 18 | 19 | export const styleVariants = { 20 | [variants.PRIMARY]: { 21 | backgroundColor: "primary", 22 | color: "white", 23 | }, 24 | [variants.SECONDARY]: { 25 | backgroundColor: "transparent", 26 | border: "2px solid", 27 | borderColor: "primary", 28 | boxShadow: "none", 29 | color: "primary", 30 | ":disabled": { 31 | backgroundColor: "transparent", 32 | }, 33 | }, 34 | [variants.TERTIARY]: { 35 | backgroundColor: "tertiary", 36 | boxShadow: "none", 37 | color: "primary", 38 | }, 39 | [variants.SUBTLE]: { 40 | backgroundColor: "textSubtle", 41 | color: "backgroundAlt2", 42 | }, 43 | [variants.DANGER]: { 44 | backgroundColor: "failure", 45 | color: "white", 46 | }, 47 | [variants.SUCCESS]: { 48 | backgroundColor: "success", 49 | color: "white", 50 | }, 51 | [variants.TEXT]: { 52 | backgroundColor: "transparent", 53 | color: "primary", 54 | boxShadow: "none", 55 | }, 56 | [variants.LIGHT]: { 57 | backgroundColor: "input", 58 | color: "textSubtle", 59 | boxShadow: "none", 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /src/components/WalletModal/WalletCard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { connectorLocalStorageKey, walletLocalStorageKey } from "./config" 3 | import { Login, Config } from "./types" 4 | 5 | interface Props { 6 | walletConfig: Config 7 | login: Login 8 | onDismiss: () => void 9 | } 10 | 11 | const WalletCard: React.FC = ({ walletConfig, login, onDismiss }) => { 12 | const { title, icon: Icon } = walletConfig 13 | 14 | return ( 15 |
  • 19 | 41 |
  • 42 | ) 43 | } 44 | 45 | export default WalletCard 46 | -------------------------------------------------------------------------------- /src/hooks/useFetchListCallback.ts: -------------------------------------------------------------------------------- 1 | import { nanoid } from '@reduxjs/toolkit' 2 | import { TokenList } from '@uniswap/token-lists' 3 | import { useCallback } from 'react' 4 | import { useAppDispatch } from '../state' 5 | import { fetchTokenList } from '../state/lists/actions' 6 | 7 | function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise { 8 | const dispatch = useAppDispatch() 9 | 10 | // note: prevent dispatch if using for list search or unsupported list 11 | return useCallback( 12 | async (listUrl: string, sendDispatch = true) => { 13 | const requestId = nanoid() 14 | if (sendDispatch) { 15 | dispatch(fetchTokenList.pending({ requestId, url: listUrl })) 16 | } 17 | // lazy load avj and token list schema 18 | const getTokenList = (await import('../utils/getTokenList')).default 19 | return getTokenList(listUrl) 20 | .then((tokenList) => { 21 | if (sendDispatch) { 22 | dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId })) 23 | } 24 | return tokenList 25 | }) 26 | .catch((error) => { 27 | console.error(`Failed to get list at url ${listUrl}`, error) 28 | if (sendDispatch) { 29 | dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message })) 30 | } 31 | throw error 32 | }) 33 | }, 34 | [dispatch], 35 | ) 36 | } 37 | 38 | export default useFetchListCallback 39 | -------------------------------------------------------------------------------- /src/hooks/ENS/useENSAddress.ts: -------------------------------------------------------------------------------- 1 | import { namehash } from 'ethers/lib/utils' 2 | import { useMemo } from 'react' 3 | import { useSingleCallResult } from '../../state/multicall/hooks' 4 | import isZero from '../../utils/isZero' 5 | import { useENSRegistrarContract, useENSResolverContract } from '../useContract' 6 | import useDebounce from '../useDebounce' 7 | 8 | /** 9 | * Does a lookup for an ENS name to find its address. 10 | */ 11 | export default function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } { 12 | const debouncedName = useDebounce(ensName, 200) 13 | const ensNodeArgument = useMemo(() => { 14 | if (!debouncedName) return [undefined] 15 | try { 16 | return debouncedName ? [namehash(debouncedName)] : [undefined] 17 | } catch (error) { 18 | return [undefined] 19 | } 20 | }, [debouncedName]) 21 | const registrarContract = useENSRegistrarContract(false) 22 | const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument) 23 | const resolverAddressResult = resolverAddress.result?.[0] 24 | const resolverContract = useENSResolverContract( 25 | resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, 26 | false, 27 | ) 28 | const addr = useSingleCallResult(resolverContract, 'addr', ensNodeArgument) 29 | 30 | const changed = debouncedName !== ensName 31 | return { 32 | address: changed ? null : addr.result?.[0] ?? null, 33 | loading: changed || resolverAddress.loading || addr.loading, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sentry.client.config.js: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the browser. 2 | // The config you add here will be used whenever a page is visited. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from '@sentry/nextjs' 6 | 7 | const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN 8 | 9 | const isUserRejected = (err) => { 10 | // provider user rejected error code 11 | return typeof err === 'object' && 'code' in err && err.code === 4001 12 | } 13 | 14 | Sentry.init({ 15 | dsn: SENTRY_DSN || 'https://ed98e16b9d704c22bef92d24bdd5f3b7@o1092725.ingest.sentry.io/6111410', 16 | integrations: [ 17 | new Sentry.Integrations.Breadcrumbs({ 18 | console: process.env.NODE_ENV === 'production', 19 | }), 20 | ], 21 | environment: process.env.NODE_ENV, 22 | // Adjust this value in production, or use tracesSampler for greater control 23 | tracesSampleRate: 0.1, 24 | // ... 25 | // Note: if you want to override the automatic release value, do not set a 26 | // `release` value here - use the environment variable `SENTRY_RELEASE`, so 27 | // that it will also get attached to your source maps 28 | beforeSend(event, hint) { 29 | const error = hint?.originalException 30 | if (error && isUserRejected(error)) { 31 | return null 32 | } 33 | return event 34 | }, 35 | ignoreErrors: [ 36 | 'User denied transaction signature', 37 | 'Non-Error promise rejection captured', 38 | 'User rejected the transaction', 39 | 'cancelled', 40 | ], 41 | }) -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "import/resolver": { 4 | "node": { 5 | "paths": ["./src"] 6 | } 7 | } 8 | }, 9 | "env": { 10 | "es6": true, 11 | "browser": true, 12 | "jest": true 13 | }, 14 | "extends": "@scads-io/eslint-config", 15 | "rules": { 16 | "no-console": ["warn", { "allow": ["info", "warn", "error", "debug"] }], 17 | "no-plusplus": 0, 18 | "prefer-destructuring": ["warn", { "object": true, "array": false }], 19 | "no-underscore-dangle": 0, 20 | "import/prefer-default-export": 0, 21 | "react/destructuring-assignment": 0, 22 | "react/jsx-no-bind": 0, 23 | "react/no-unused-prop-types": 0, 24 | "react/jsx-filename-extension": [0], 25 | // Start temporary rules 26 | // These rules are here just to keep the lint error to 0 during the migration to the new rule set 27 | // They need to be removed and fixed as soon as possible 28 | "@typescript-eslint/ban-ts-comment": [1, { "ts-ignore": false, "ts-nocheck": false }], 29 | "@typescript-eslint/no-use-before-define": 0, 30 | "@typescript-eslint/explicit-module-boundary-types": 0, 31 | "@typescript-eslint/no-explicit-any": 0, 32 | "radix": 0, 33 | "import/no-extraneous-dependencies": 0, 34 | "jsx-a11y/media-has-caption": 0, 35 | // Exchange 36 | "no-param-reassign": ["error", { "props": true, "ignorePropertyModificationsFor": ["state", "memo"] }], 37 | "react/require-default-props": 0, 38 | "no-nested-ternary": 0, 39 | "max-classes-per-file": 0 40 | // End temporary rules 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/wagmi.ts: -------------------------------------------------------------------------------- 1 | import { configureChains, createClient } from 'wagmi' 2 | import { InjectedConnector } from 'wagmi/connectors/injected' 3 | import { MetaMaskConnector } from 'wagmi/connectors/metaMask' 4 | import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' 5 | import { bsc } from 'wagmi/chains' 6 | import { publicProvider } from 'wagmi/providers/public' 7 | import { jsonRpcProvider } from 'wagmi/providers/jsonRpc' 8 | 9 | const { chains, provider, webSocketProvider } = configureChains( 10 | [bsc], 11 | [ 12 | // publicProvider(), 13 | jsonRpcProvider({ 14 | rpc: (chain) => ({ 15 | http: process.env.NEXT_PUBLIC_NODE_1, 16 | // webSocket: 'wss://your-custom-ws-rpc-url.com' 17 | }) 18 | }) 19 | ], 20 | ) 21 | 22 | export const injectedConnector = new InjectedConnector({ 23 | chains, 24 | options: { 25 | shimDisconnect: false, 26 | }, 27 | }) 28 | 29 | export const walletConnectConnector = new WalletConnectConnector({ 30 | chains, 31 | options: { 32 | projectId: '0eb93e1311bacfc188ea2df6c42d02fe', 33 | showQrModal: true, 34 | qrModalOptions: { 35 | themeMode: 'dark', 36 | } 37 | }, 38 | }) 39 | 40 | export const metaMaskConnector = new MetaMaskConnector({ 41 | chains, 42 | options: { 43 | shimDisconnect: false, 44 | }, 45 | }) 46 | 47 | export const client = createClient({ 48 | autoConnect: false, 49 | provider, 50 | webSocketProvider, 51 | connectors: [ 52 | metaMaskConnector, 53 | injectedConnector, 54 | walletConnectConnector, 55 | ], 56 | }) 57 | -------------------------------------------------------------------------------- /src/components/socials.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { FaMediumM } from "react-icons/fa" 3 | import { FaXTwitter, FaTelegram, FaGithub, FaDiscord } from "react-icons/fa6" 4 | 5 | const socialLinks = [ 6 | { 7 | id: "X", 8 | icon: , 9 | path: "https://twitter.com/ScadsSwap", 10 | }, 11 | { 12 | id: "Telegram", 13 | icon: , 14 | path: "https://t.me/scads_io", 15 | }, 16 | { 17 | id: "Github", 18 | icon: , 19 | path: "https://github.com/scads-io", 20 | }, 21 | // { 22 | // id: "Discord", 23 | // icon: , 24 | // }, 25 | // { 26 | // id: "Medium", 27 | // icon: , 28 | // }, 29 | ] 30 | 31 | const Socials = ({ navigation }: { navigation?: boolean }) => { 32 | return ( 33 |
    34 | {!navigation && Socials} 35 |
    36 | {socialLinks.map((link) => ( 37 | 45 | {link.icon} 46 | 47 | ))} 48 |
    49 |
    50 | ) 51 | } 52 | 53 | export default Socials 54 | -------------------------------------------------------------------------------- /src/config/constants/contracts.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | masterChef: { 3 | // 97: '0x1d32c2945C8FDCBc7156c553B7cEa4325a17f4f9', 4 | // 97: '0x97B631AcBAC4C38Bb0AA18691ea60dB2754609FB', 5 | 97: '0x8Cd56Abd3dA18E86Ea7CF24c9351Ab9973dFB36b', 6 | 56: '0x73feaa1eE314F8c655E354234017bE2193C9E24E', 7 | }, 8 | treasury: { 9 | 97: '0x8a0dC0f9C2592f9dE18282A2953895a023362F0d', 10 | 56: '0x8F5572852A9DfDbEb13E4821F01e6E0807691b67', 11 | }, 12 | scads: { 13 | 97: '0x8Ab5A56AfB69B31ACFbb2AC29bd2B131e2601f9F', 14 | 56: '0x6D036B813C63c2c2D84De16369F2b532a37A5E97', 15 | }, 16 | carat: { 17 | 97: '0xF5284a7C7c47BAb1C98959786D389a4C60c94Faf', 18 | 56: '0xa86C718a38515699773a1FEB9c3A891A2BE982A1', 19 | }, 20 | pulse: { 21 | 97: '0xA317Cc664F6B7A94B56a40d5F935b3AC295795De', 22 | 56: '0x065D91c0c78aa39Ae52299Fb2521AcD3B1E46AeD', 23 | }, 24 | sousChef: { 25 | 97: '0x48f0Bfb7B03C644Ce1ee21d978eF30D3f97A220e', 26 | 56: '0x6ab8463a4185b80905e05a9ff80a2d6b714b9e95', 27 | }, 28 | multiCall: { 29 | 56: '0xfF6FD90A470Aaa0c1B8A54681746b07AcdFedc9B', 30 | 97: '0x8F3273Fb89B075b1645095ABaC6ed17B2d4Bc576', 31 | }, 32 | scadsProfile: { 33 | 56: '0xDf4dBf6536201370F95e06A0F8a7a70fE40E388a', 34 | 97: '0x4B683C7E13B6d5D7fd1FeA9530F451954c1A7c8A', 35 | }, 36 | scadsPool: { 37 | 56: '0x3a4bF3B94c0e098c267b7bB189ceDb64a4317caE', 38 | 97: '0x3a88e377a53310d829262F8b747a82512B74EA47', 39 | }, 40 | caratPool: { 41 | 56: '0x57c195D4856173313185aF30cEAc1065c45c9f18', 42 | 97: '0xc474FF61B82416d06B0535F5DB5ecAeFC64fca97', 43 | }, 44 | } 45 | -------------------------------------------------------------------------------- /src/utils/calls/caratMint.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js' 2 | import { DEFAULT_GAS_LIMIT } from 'config' 3 | import getGasPrice from 'utils/getGasPrice' 4 | 5 | const options = { 6 | gasLimit: DEFAULT_GAS_LIMIT, 7 | } 8 | 9 | export const caratBuy = async (caratContract, amount) => { 10 | const gasPrice = getGasPrice() 11 | 12 | const value = amount 13 | const tx = await caratContract.caratMint(value, { ...options, gasPrice }) 14 | const receipt = await tx.wait() 15 | return receipt.status 16 | } 17 | 18 | export const caratRedeem = async (caratContract, amount) => { 19 | const gasPrice = getGasPrice() 20 | const value = new BigNumber(amount).toString() 21 | const tx = await caratContract.claim(value, { ...options, gasPrice }) 22 | const receipt = await tx.wait() 23 | return receipt.status 24 | } 25 | 26 | export const caratClaim = async (caratContract) => { 27 | const gasPrice = getGasPrice() 28 | const tx = await caratContract.claimRewardCarat({ ...options, gasPrice }) 29 | const receipt = await tx.wait() 30 | return receipt.status 31 | } 32 | 33 | export const addAutoCompoundAddress = async (caratContract) => { 34 | const gasPrice = getGasPrice() 35 | const tx = await caratContract.addAutoCompoundAddress({ ...options, gasPrice }) 36 | const receipt = await tx.wait() 37 | return receipt.status 38 | } 39 | 40 | export const removeAutoCompoundAddress = async (caratContract) => { 41 | const gasPrice = getGasPrice() 42 | const tx = await caratContract.removeAutoCompoundAddress({ ...options, gasPrice }) 43 | const receipt = await tx.wait() 44 | return receipt.status 45 | } 46 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import React, { cloneElement, ElementType, isValidElement } from "react"; 3 | import getExternalLinkProps from "../../util/getExternalLinkProps"; 4 | import StyledButton from "./StyledButton"; 5 | import { ButtonProps, scales, variants } from "./types"; 6 | 7 | const Button = (props: ButtonProps): JSX.Element => { 8 | const { startIcon, endIcon, external, className, isLoading, disabled, children, ...rest } = props; 9 | const internalProps = external ? getExternalLinkProps() : {}; 10 | const isDisabled = isLoading || disabled; 11 | const classNames = className ? [className] : []; 12 | 13 | if (isLoading) { 14 | classNames.push("scads-button--loading"); 15 | } 16 | 17 | if (isDisabled && !isLoading) { 18 | classNames.push("scads-button--disabled"); 19 | } 20 | 21 | return ( 22 | 29 | <> 30 | {isValidElement(startIcon) && 31 | cloneElement(startIcon, { 32 | mr: "0.5rem", 33 | })} 34 | {children} 35 | {isValidElement(endIcon) && 36 | cloneElement(endIcon, { 37 | ml: "0.5rem", 38 | })} 39 | 40 | 41 | ); 42 | }; 43 | 44 | Button.defaultProps = { 45 | isLoading: false, 46 | external: false, 47 | variant: variants.PRIMARY, 48 | scale: scales.MD, 49 | disabled: false, 50 | }; 51 | 52 | export default Button; 53 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Svg/Icons/WalletConnect.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Svg from "../Svg"; 3 | import { SvgProps } from "../types"; 4 | 5 | const Icon: React.FC = (props) => { 6 | return ( 7 | 8 | 12 | 13 | ); 14 | }; 15 | 16 | export default Icon; 17 | -------------------------------------------------------------------------------- /src/utils/contenthashToUri.ts: -------------------------------------------------------------------------------- 1 | import CID from 'cids' 2 | import { getCodec, rmPrefix } from 'multicodec' 3 | import { decode, toB58String } from 'multihashes' 4 | 5 | function hexToUint8Array(hex: string): Uint8Array { 6 | // eslint-disable-next-line no-param-reassign 7 | hex = hex.startsWith('0x') ? hex.substr(2) : hex 8 | if (hex.length % 2 !== 0) throw new Error('hex must have length that is multiple of 2') 9 | const arr = new Uint8Array(hex.length / 2) 10 | for (let i = 0; i < arr.length; i++) { 11 | arr[i] = parseInt(hex.substr(i * 2, 2), 16) 12 | } 13 | return arr 14 | } 15 | 16 | const UTF_8_DECODER = new TextDecoder() 17 | 18 | /** 19 | * Returns the URI representation of the content hash for supported codecs 20 | * @param contenthash to decode 21 | */ 22 | export default function contenthashToUri(contenthash: string): string { 23 | const buff = hexToUint8Array(contenthash) 24 | const codec = getCodec(buff as Buffer) // the typing is wrong for @types/multicodec 25 | switch (codec) { 26 | case 'ipfs-ns': { 27 | const data = rmPrefix(buff as Buffer) 28 | const cid = new CID(data) 29 | return `ipfs://${toB58String(cid.multihash)}` 30 | } 31 | case 'ipns-ns': { 32 | const data = rmPrefix(buff as Buffer) 33 | const cid = new CID(data) 34 | const multihash = decode(cid.multihash) 35 | if (multihash.name === 'identity') { 36 | return `ipns://${UTF_8_DECODER.decode(multihash.digest).trim()}` 37 | } 38 | return `ipns://${toB58String(cid.multihash)}` 39 | } 40 | default: 41 | throw new Error(`Unrecognized codec: ${codec}`) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/eslint-config/lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "@typescript-eslint/parser", 4 | parserOptions: { 5 | ecmaVersion: 2017, 6 | ecmaFeatures: { 7 | jsx: true, 8 | }, 9 | }, 10 | env: { 11 | es6: true, 12 | browser: true, 13 | }, 14 | settings: { 15 | "import/resolver": { 16 | node: { 17 | extensions: [".js", ".ts", ".jsx", ".tsx"], 18 | }, 19 | }, 20 | "import/extensions": [".js", ".ts", ".jsx", ".tsx"], 21 | }, 22 | extends: [ 23 | "airbnb", 24 | "airbnb/hooks", 25 | "prettier", 26 | "prettier/react", 27 | "prettier/@typescript-eslint", 28 | "plugin:@typescript-eslint/recommended", 29 | ], 30 | rules: { 31 | // Typescript 32 | "@typescript-eslint/no-unused-vars": "warn", 33 | "no-use-before-define": "off", 34 | "@typescript-eslint/no-use-before-define": ["warn"], 35 | "no-shadow": "off", 36 | "@typescript-eslint/no-shadow": ["error"], 37 | "no-console": ["warn", { allow: ["info", "warn", "error"] }], 38 | "no-plusplus": 0, 39 | "prefer-destructuring": ["warn", { object: true, array: false }], 40 | "no-underscore-dangle": 0, 41 | // React 42 | "react/jsx-filename-extension": ["error", { extensions: [".tsx"] }], 43 | "react/prop-types": 0, 44 | "react/jsx-props-no-spreading": 0, 45 | "react/no-multi-comp": 0, 46 | "import/extensions": [ 47 | "error", 48 | "ignorePackages", 49 | { 50 | js: "never", 51 | mjs: "never", 52 | jsx: "never", 53 | ts: "never", 54 | tsx: "never", 55 | }, 56 | ], 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /packages/uikit/src/theme/base.ts: -------------------------------------------------------------------------------- 1 | import { MediaQueries, Spacing } from "./types"; 2 | 3 | export const breakpointMap: { [key: string]: number } = { 4 | xs: 370, 5 | sm: 576, 6 | md: 852, 7 | lg: 968, 8 | xl: 1080, 9 | xxl: 1200, 10 | }; 11 | 12 | const mediaQueries: MediaQueries = { 13 | xs: `@media screen and (min-width: ${breakpointMap.xs}px)`, 14 | sm: `@media screen and (min-width: ${breakpointMap.sm}px)`, 15 | md: `@media screen and (min-width: ${breakpointMap.md}px)`, 16 | lg: `@media screen and (min-width: ${breakpointMap.lg}px)`, 17 | xl: `@media screen and (min-width: ${breakpointMap.xl}px)`, 18 | xxl: `@media screen and (min-width: ${breakpointMap.xxl}px)`, 19 | nav: `@media screen and (min-width: ${breakpointMap.lg}px)`, 20 | }; 21 | 22 | export const shadows = { 23 | level1: "0px 2px 12px -8px rgba(25, 19, 38, 0.1), 0px 1px 1px rgba(25, 19, 38, 0.05)", 24 | active: "0px 0px 0px 1px #0098A1, 0px 0px 4px 8px rgba(31, 199, 212, 0.4)", 25 | success: "0px 0px 0px 1px #31D0AA, 0px 0px 0px 4px rgba(49, 208, 170, 0.2)", 26 | warning: "0px 0px 0px 1px #ED4B9E, 0px 0px 0px 4px rgba(237, 75, 158, 0.2)", 27 | focus: "0px 0px 0px 3px #7E57FF", 28 | inset: "inset 0px 2px 2px -1px rgba(74, 74, 104, 0.1)", 29 | tooltip: "0px 0px 2px rgba(0, 0, 0, 0.2), 0px 4px 12px -8px rgba(14, 14, 44, 0.1)", 30 | }; 31 | 32 | const spacing: Spacing = [0, 4, 8, 16, 24, 32, 48, 64]; 33 | 34 | const radii = { 35 | small: "4px", 36 | default: "16px", 37 | card: "24px", 38 | circle: "50%", 39 | }; 40 | 41 | const zIndices = { 42 | dropdown: 10, 43 | modal: 100, 44 | }; 45 | 46 | export default { 47 | mediaQueries, 48 | spacing, 49 | shadows, 50 | radii, 51 | zIndices, 52 | }; 53 | -------------------------------------------------------------------------------- /packages/uikit/src/theme/types.ts: -------------------------------------------------------------------------------- 1 | export type MediaQueries = { 2 | xs: string; 3 | sm: string; 4 | md: string; 5 | lg: string; 6 | xl: string; 7 | xxl: string; 8 | nav: string; 9 | }; 10 | 11 | export type Spacing = number[]; 12 | 13 | export type Radii = { 14 | small: string; 15 | default: string; 16 | card: string; 17 | circle: string; 18 | }; 19 | 20 | export type Shadows = { 21 | level1: string; 22 | active: string; 23 | success: string; 24 | warning: string; 25 | focus: string; 26 | inset: string; 27 | tooltip: string; 28 | }; 29 | 30 | export type Gradients = { 31 | bubblegum: string; 32 | inverseBubblegum: string; 33 | cardHeader: string; 34 | blue: string; 35 | violet: string; 36 | violetAlt: string; 37 | gold: string; 38 | }; 39 | 40 | export type Colors = { 41 | primary: string; 42 | primaryBright: string; 43 | primaryDark: string; 44 | secondary: string; 45 | tertiary: string; 46 | success: string; 47 | failure: string; 48 | warning: string; 49 | cardBorder: string; 50 | contrast: string; 51 | dropdown: string; 52 | dropdownDeep: string; 53 | invertedContrast: string; 54 | input: string; 55 | inputSecondary: string; 56 | background: string; 57 | backgroundDisabled: string; 58 | backgroundAlt: string; 59 | backgroundAlt2: string; 60 | text: string; 61 | textDisabled: string; 62 | textSubtle: string; 63 | disabled: string; 64 | 65 | // Gradients 66 | gradients: Gradients; 67 | 68 | // Additional colors 69 | binance: string; 70 | overlay: string; 71 | gold: string; 72 | silver: string; 73 | bronze: string; 74 | }; 75 | 76 | export type ZIndices = { 77 | dropdown: number; 78 | modal: number; 79 | }; 80 | -------------------------------------------------------------------------------- /packages/uikit/src/components/Button/types.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { ComponentProps, ElementType, ReactElement, ReactNode } from "react"; 3 | import { Link } from "react-router-dom"; 4 | import { LayoutProps, SpaceProps } from "styled-system"; 5 | 6 | export const scales = { 7 | MD: "md", 8 | SM: "sm", 9 | XS: "xs", 10 | } as const; 11 | 12 | export const variants = { 13 | PRIMARY: "primary", 14 | SECONDARY: "secondary", 15 | TERTIARY: "tertiary", 16 | TEXT: "text", 17 | DANGER: "danger", 18 | SUBTLE: "subtle", 19 | SUCCESS: "success", 20 | LIGHT: "light", 21 | } as const; 22 | 23 | export type Scale = typeof scales[keyof typeof scales]; 24 | export type Variant = typeof variants[keyof typeof variants]; 25 | 26 | /** 27 | * @see https://www.benmvp.com/blog/polymorphic-react-components-typescript/ 28 | */ 29 | export type AsProps = { 30 | as?: E; 31 | }; 32 | 33 | export type MergeProps = AsProps & Omit, keyof AsProps>; 34 | 35 | export type PolymorphicComponentProps = P & MergeProps; 36 | 37 | export type PolymorphicComponent = ( 38 | props: PolymorphicComponentProps 39 | ) => ReactElement | null; 40 | 41 | export interface BaseButtonProps extends LayoutProps, SpaceProps { 42 | as?: "a" | "button" | typeof Link; 43 | external?: boolean; 44 | isLoading?: boolean; 45 | scale?: Scale; 46 | variant?: Variant; 47 | disabled?: boolean; 48 | startIcon?: ReactNode; 49 | endIcon?: ReactNode; 50 | } 51 | 52 | export type ButtonProps

    = PolymorphicComponentProps; 53 | -------------------------------------------------------------------------------- /src/components/Skeleton/Skeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled, { keyframes } from "styled-components" 3 | import { space, layout } from "styled-system" 4 | import { SkeletonProps, animation as ANIMATION, variant as VARIANT } from "./types" 5 | 6 | const waves = keyframes` 7 | from { 8 | left: -150px; 9 | } 10 | to { 11 | left: 100%; 12 | } 13 | ` 14 | 15 | const pulse = keyframes` 16 | 0% { 17 | opacity: 1; 18 | } 19 | 50% { 20 | opacity: 0.4; 21 | } 22 | 100% { 23 | opacity: 1; 24 | } 25 | ` 26 | 27 | const Root = styled.div` 28 | min-height: 20px; 29 | display: block; 30 | background-color: #353688; 31 | border-radius: ${({ variant, theme }) => (variant === VARIANT.CIRCLE ? theme.radii.circle : theme.radii.small)}; 32 | 33 | ${layout} 34 | ${space} 35 | ` 36 | 37 | const Pulse = styled(Root)` 38 | animation: ${pulse} 2s infinite ease-out; 39 | transform: translate3d(0, 0, 0); 40 | border-radius: 16px; 41 | ` 42 | 43 | const Waves = styled(Root)` 44 | position: relative; 45 | overflow: hidden; 46 | transform: translate3d(0, 0, 0); 47 | &:before { 48 | content: ""; 49 | position: absolute; 50 | background-image: #353688; 51 | top: 0; 52 | left: -150px; 53 | height: 100%; 54 | width: 150px; 55 | animation: ${waves} 2s cubic-bezier(0.4, 0, 0.2, 1) infinite; 56 | } 57 | ` 58 | 59 | const Skeleton: React.FC = ({ variant = VARIANT.RECT, animation = ANIMATION.PULSE, ...props }) => { 60 | if (animation === ANIMATION.WAVES) { 61 | return 62 | } 63 | 64 | return 65 | } 66 | 67 | export default Skeleton 68 | -------------------------------------------------------------------------------- /src/utils/getTokenList.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-continue */ 2 | /* eslint-disable no-await-in-loop */ 3 | import { TokenList } from '@uniswap/token-lists' 4 | import schema from '@uniswap/token-lists/src/tokenlist.schema.json' 5 | import Ajv from 'ajv' 6 | import uriToHttp from './uriToHttp' 7 | 8 | const tokenListValidator = new Ajv({ allErrors: true }).compile(schema) 9 | 10 | /** 11 | * Contains the logic for resolving a list URL to a validated token list 12 | * @param listUrl list url 13 | */ 14 | export default async function getTokenList(listUrl: string): Promise { 15 | const urls: string[] = uriToHttp(listUrl) 16 | 17 | for (let i = 0; i < urls.length; i++) { 18 | const url = urls[i] 19 | const isLast = i === urls.length - 1 20 | let response 21 | try { 22 | response = await fetch(url) 23 | } catch (error) { 24 | console.error('Failed to fetch list', listUrl, error) 25 | if (isLast) throw new Error(`Failed to download list ${listUrl}`) 26 | continue 27 | } 28 | 29 | if (!response.ok) { 30 | if (isLast) throw new Error(`Failed to download list ${listUrl}`) 31 | continue 32 | } 33 | 34 | const json = await response.json() 35 | if (!tokenListValidator(json)) { 36 | const validationErrors: string = 37 | tokenListValidator.errors?.reduce((memo, error) => { 38 | const add = `${(error as any).dataPath} ${error.message ?? ''}` 39 | return memo.length > 0 ? `${memo}; ${add}` : `${add}` 40 | }, '') ?? 'unknown error' 41 | throw new Error(`Token list failed validation: ${validationErrors}`) 42 | } 43 | return json as TokenList 44 | } 45 | throw new Error('Unrecognized list URL protocol.') 46 | } 47 | -------------------------------------------------------------------------------- /src/hooks/ENS/useENSName.ts: -------------------------------------------------------------------------------- 1 | import { namehash } from 'ethers/lib/utils' 2 | import { useMemo } from 'react' 3 | import { useSingleCallResult } from '../../state/multicall/hooks' 4 | import { isAddress } from '../../utils' 5 | import isZero from '../../utils/isZero' 6 | import { useENSRegistrarContract, useENSResolverContract } from '../useContract' 7 | import useDebounce from '../useDebounce' 8 | 9 | /** 10 | * Does a reverse lookup for an address to find its ENS name. 11 | * Note this is not the same as looking up an ENS name to find an address. 12 | */ 13 | export default function useENSName(address?: string): { ENSName: string | null; loading: boolean } { 14 | const debouncedAddress = useDebounce(address, 200) 15 | const ensNodeArgument = useMemo(() => { 16 | if (!debouncedAddress || !isAddress(debouncedAddress)) return [undefined] 17 | try { 18 | return debouncedAddress ? [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)] : [undefined] 19 | } catch (error) { 20 | return [undefined] 21 | } 22 | }, [debouncedAddress]) 23 | const registrarContract = useENSRegistrarContract(false) 24 | const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument) 25 | const resolverAddressResult = resolverAddress.result?.[0] 26 | const resolverContract = useENSResolverContract( 27 | resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, 28 | false, 29 | ) 30 | const name = useSingleCallResult(resolverContract, 'name', ensNodeArgument) 31 | 32 | const changed = debouncedAddress !== address 33 | return { 34 | ENSName: changed ? null : name.result?.[0] ?? null, 35 | loading: changed || resolverAddress.loading || name.loading, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area' 3 | 4 | import { cn } from 'lib/utils' 5 | 6 | const ScrollArea = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, children, ...props }, ref) => ( 10 | 11 | {children} 12 | 13 | 14 | 15 | )) 16 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName 17 | 18 | const ScrollBar = React.forwardRef< 19 | React.ElementRef, 20 | React.ComponentPropsWithoutRef 21 | >(({ className, orientation = 'vertical', ...props }, ref) => ( 22 | 33 | 34 | 35 | )) 36 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName 37 | 38 | export { ScrollArea, ScrollBar } 39 | -------------------------------------------------------------------------------- /src/components/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { motion } from 'framer-motion' 3 | import Link from 'next/link' 4 | import { useTranslation } from 'contexts/Localization' 5 | import { Button } from './ui/button' 6 | 7 | const NotFound = () => { 8 | const { t } = useTranslation() 9 | 10 | return ( 11 |

    12 |
    13 |
    14 | 21 | {404} 22 | 23 | 30 | Oops, page not found 31 | 32 |
    33 | 34 | 35 | 36 |
    37 |
    38 |
    39 |
    40 | ) 41 | } 42 | 43 | export default NotFound 44 | -------------------------------------------------------------------------------- /src/pages/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | input[type="number"]::-webkit-inner-spin-button, 7 | input[type="number"]::-webkit-outer-spin-button { 8 | -webkit-appearance: none; 9 | margin: 0; 10 | } 11 | 12 | ::-webkit-scrollbar { 13 | @apply z-50 w-2; 14 | } 15 | 16 | ::-webkit-scrollbar-track { 17 | @apply z-50 rounded-full bg-white/10; 18 | } 19 | 20 | ::-webkit-scrollbar-thumb { 21 | @apply z-50 rounded-full bg-white/20; 22 | } 23 | 24 | ::-webkit-scrollbar-thumb:hover { 25 | @apply bg-white/40; 26 | } 27 | } 28 | 29 | ::-moz-selection { 30 | /* Code for Firefox */ 31 | color: black; 32 | background: #7a87ff; 33 | } 34 | 35 | ::selection { 36 | color: black; 37 | background: #7a87ff; 38 | } 39 | 40 | .button-primary { 41 | background: radial-gradient( 42 | 231.94% 231.94% at 50% 100%, 43 | #8a6cff 0, 44 | rgba(53, 41, 128, 0) 25.24% 45 | ), 46 | linear-gradient(180deg, rgba(243, 238, 255, 0), rgba(243, 238, 255, 0.04)), 47 | rgba(147, 130, 255, 0.01); 48 | box-shadow: 49 | 0 0 0 0 rgba(16, 0, 51, 0.4), 50 | 0 2px 5px 0 rgba(16, 0, 51, 0.39), 51 | 0 8px 8px 0 rgba(16, 0, 51, 0.34), 52 | 0 19px 11px 0 rgba(16, 0, 51, 0.2), 53 | 0 34px 14px 0 rgba(16, 0, 51, 0.06), 54 | 0 53px 15px 0 rgba(16, 0, 51, 0.01), 55 | inset 0 0 12px 0 hsla(0, 0%, 100%, 0.08), 56 | inset 0 -8px 32px 0 #1e0d49; 57 | } 58 | 59 | .button-primary:hover { 60 | background: radial-gradient( 61 | 231.94% 231.94% at 50% 100%, 62 | #8a6cff 0, 63 | rgba(53, 41, 128, 0) 25.24% 64 | ), 65 | linear-gradient(180deg, rgba(243, 238, 255, 0), rgba(243, 238, 255, 0.04)), 66 | rgba(147, 130, 255, 0.6); 67 | } 68 | -------------------------------------------------------------------------------- /src/__tests__/utils/prices.test.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, JSBI, Pair, Route, Token, TokenAmount, Trade, TradeType } from '@scads/sdk' 2 | import { computeTradePriceBreakdown } from 'utils/prices' 3 | 4 | describe('prices', () => { 5 | const token1 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18) 6 | const token2 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000002', 18) 7 | const token3 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000003', 18) 8 | 9 | const pair12 = new Pair(new TokenAmount(token1, JSBI.BigInt(10000)), new TokenAmount(token2, JSBI.BigInt(20000))) 10 | const pair23 = new Pair(new TokenAmount(token2, JSBI.BigInt(20000)), new TokenAmount(token3, JSBI.BigInt(30000))) 11 | 12 | describe('computeTradePriceBreakdown', () => { 13 | it('returns undefined for undefined', () => { 14 | expect(computeTradePriceBreakdown(undefined)).toEqual({ 15 | priceImpactWithoutFee: undefined, 16 | realizedLPFee: undefined, 17 | }) 18 | }) 19 | 20 | it('correct realized lp fee for single hop', () => { 21 | expect( 22 | computeTradePriceBreakdown( 23 | new Trade(new Route([pair12], token1), new TokenAmount(token1, JSBI.BigInt(1000)), TradeType.EXACT_INPUT), 24 | ).realizedLPFee, 25 | ).toEqual(new TokenAmount(token1, JSBI.BigInt(2))) 26 | }) 27 | 28 | it('correct realized lp fee for double hop', () => { 29 | expect( 30 | computeTradePriceBreakdown( 31 | new Trade( 32 | new Route([pair12, pair23], token1), 33 | new TokenAmount(token1, JSBI.BigInt(1000)), 34 | TradeType.EXACT_INPUT, 35 | ), 36 | ).realizedLPFee, 37 | ).toEqual(new TokenAmount(token1, JSBI.BigInt(4))) 38 | }) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /src/pages/_components/outro.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import SwapModal from 'components/swap/swap-modal' 4 | import { SparklesCore } from 'components/ui/sparkles' 5 | import { useTranslation } from 'contexts/Localization' 6 | 7 | const Outro = () => { 8 | const { t } = useTranslation() 9 | 10 | return ( 11 |
    12 | logo 13 |

    14 | {t('The change is here')} 15 |

    16 |
    17 |
    18 |
    19 |
    20 |
    21 | 29 |
    30 |
    31 | 32 |
    33 |
    34 | ) 35 | } 36 | 37 | export default Outro 38 | -------------------------------------------------------------------------------- /src/components/WalletModal/TransactionRow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BlockIcon, CheckmarkCircleIcon, Flex, Link, OpenNewIcon, RefreshIcon } from '@scads-io/uikit' 3 | import styled from 'styled-components' 4 | import { TransactionDetails } from 'state/transactions/reducer' 5 | import useActiveWeb3React from 'hooks/useActiveWeb3React' 6 | import { getBscScanLink } from 'utils' 7 | 8 | interface TransactionRowProps { 9 | txn: TransactionDetails 10 | } 11 | 12 | const TxnIcon = styled(Flex)` 13 | align-items: center; 14 | flex: none; 15 | width: 24px; 16 | ` 17 | 18 | const Summary = styled.div` 19 | flex: 1; 20 | padding: 0 8px; 21 | ` 22 | 23 | const TxnLink = styled(Link)` 24 | align-items: center; 25 | color: ${({ theme }) => theme.colors.text}; 26 | display: flex; 27 | margin-bottom: 16px; 28 | width: 100%; 29 | 30 | &:hover { 31 | text-decoration: none; 32 | } 33 | ` 34 | 35 | const renderIcon = (txn: TransactionDetails) => { 36 | if (!txn.receipt) { 37 | return 38 | } 39 | 40 | return txn.receipt?.status === 1 || typeof txn.receipt?.status === 'undefined' ? ( 41 | 42 | ) : ( 43 | 44 | ) 45 | } 46 | 47 | const TransactionRow: React.FC = ({ txn }) => { 48 | const { chainId } = useActiveWeb3React() 49 | 50 | if (!txn) { 51 | return null 52 | } 53 | 54 | return ( 55 | 56 | {renderIcon(txn)} 57 | {txn.summary ?? txn.hash} 58 | 59 | 60 | 61 | 62 | ) 63 | } 64 | 65 | export default TransactionRow 66 | -------------------------------------------------------------------------------- /src/components/swap/NumericalInput.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTranslation } from 'contexts/Localization' 3 | import { escapeRegExp } from '../../utils' 4 | 5 | const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped '.' characters via in a non-capturing group 6 | const integerinputRegex = RegExp(`^\\d*(?:\\\\[])?\\d*$`) // match escaped '.' characters via in a non-capturing group 7 | 8 | export const Input = React.memo(function InnerInput({ 9 | value, 10 | onUserInput, 11 | placeholder, 12 | onlyInteger, 13 | ...rest 14 | }: { 15 | value: string | number 16 | onUserInput: (input: string) => void 17 | onlyInteger?: boolean 18 | error?: boolean 19 | fontSize?: string 20 | align?: 'right' | 'left' 21 | } & Omit, 'ref' | 'onChange' | 'as'>) { 22 | const enforcer = (nextUserInput: string) => { 23 | const regex = onlyInteger ? integerinputRegex : inputRegex 24 | if (nextUserInput === '' || regex.test(escapeRegExp(nextUserInput))) { 25 | onUserInput(nextUserInput) 26 | } 27 | } 28 | 29 | const { t } = useTranslation() 30 | 31 | return ( 32 | { 36 | // replace commas with periods, because we exclusively uses period as the decimal separator 37 | enforcer(event.target.value.replace(/,/g, '.')) 38 | }} 39 | // universal input options 40 | inputMode='decimal' 41 | title={t('Token Amount')} 42 | autoComplete='off' 43 | autoCorrect='off' 44 | // text-specific options 45 | type='text' 46 | pattern='^[0-9]*[.,]?[0-9]*$' 47 | placeholder={placeholder || '0.0'} 48 | minLength={1} 49 | maxLength={79} 50 | spellCheck='false' 51 | /> 52 | ) 53 | }) 54 | 55 | export default Input 56 | -------------------------------------------------------------------------------- /src/pages/faq/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { motion } from 'framer-motion' 3 | import { IoEllipse } from 'react-icons/io5' 4 | import { useTranslation } from 'contexts/Localization' 5 | import FaqAccordion from './_components/faq-accordion' 6 | import SearchBar from './_components/search-bar' 7 | 8 | const FaqPage = () => { 9 | const [searchValue, setSearchValue] = useState('') 10 | const { t } = useTranslation() 11 | 12 | return ( 13 | <> 14 | 20 | {t('Frequently asked questions')} 21 | 22 | 28 | 29 | 30 |
    31 | 32 |
    33 | 37 | 41 | 42 | ) 43 | } 44 | 45 | export default FaqPage 46 | -------------------------------------------------------------------------------- /src/hooks/useAuth.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | import toast from 'react-hot-toast' 3 | import { useAppDispatch } from 'state' 4 | import { useConnect, useDisconnect, useNetwork, ConnectorNotFoundError, UserRejectedRequestError } from 'wagmi' 5 | import { useTranslation } from 'contexts/Localization' 6 | import { connectorLocalStorageKey, ConnectorNames } from 'components/WalletModal' 7 | import { clearUserStates } from '../utils/clearUserStates' 8 | import useActiveWeb3React from './useActiveWeb3React' 9 | 10 | const useAuth = () => { 11 | const { t } = useTranslation() 12 | const { chainId } = useActiveWeb3React() 13 | const dispatch = useAppDispatch() 14 | const { connectAsync, connectors } = useConnect() 15 | const { chain } = useNetwork() 16 | const { disconnect } = useDisconnect() 17 | 18 | const login = useCallback( 19 | async (connectorID: ConnectorNames) => { 20 | const findConnector = connectors.find((c) => c.id === connectorID) 21 | try { 22 | await connectAsync({ connector: findConnector, chainId }) 23 | } catch (error) { 24 | console.error(error) 25 | window?.localStorage?.removeItem(connectorLocalStorageKey) 26 | if (error instanceof ConnectorNotFoundError) { 27 | toast.error(t('Provider Error')) 28 | return 29 | } 30 | if (error instanceof UserRejectedRequestError) { 31 | return 32 | } 33 | if (error instanceof Error) { 34 | toast.error(t('Please authorize to access your account')) 35 | } 36 | } 37 | }, 38 | [connectors, connectAsync, chainId, t], 39 | ) 40 | 41 | const logout = useCallback(() => { 42 | disconnect() 43 | clearUserStates(dispatch, chain?.id) 44 | }, [disconnect, dispatch, chain?.id]) 45 | 46 | return { login, logout } 47 | } 48 | 49 | export default useAuth 50 | -------------------------------------------------------------------------------- /src/components/swap/sorting.ts: -------------------------------------------------------------------------------- 1 | import { Token, TokenAmount } from '@scads/sdk' 2 | import { useMemo } from 'react' 3 | import { useAllTokenBalances } from '../../state/wallet/hooks' 4 | 5 | // compare two token amounts with highest one coming first 6 | function balanceComparator(balanceA?: TokenAmount, balanceB?: TokenAmount) { 7 | if (balanceA && balanceB) { 8 | return balanceA.greaterThan(balanceB) ? -1 : balanceA.equalTo(balanceB) ? 0 : 1 9 | } 10 | if (balanceA && balanceA.greaterThan('0')) { 11 | return -1 12 | } 13 | if (balanceB && balanceB.greaterThan('0')) { 14 | return 1 15 | } 16 | return 0 17 | } 18 | 19 | function getTokenComparator(balances: { 20 | [tokenAddress: string]: TokenAmount | undefined 21 | }): (tokenA: Token, tokenB: Token) => number { 22 | return function sortTokens(tokenA: Token, tokenB: Token): number { 23 | // -1 = a is first 24 | // 1 = b is first 25 | 26 | // sort by balances 27 | const balanceA = balances[tokenA.address] 28 | const balanceB = balances[tokenB.address] 29 | 30 | const balanceComp = balanceComparator(balanceA, balanceB) 31 | if (balanceComp !== 0) return balanceComp 32 | 33 | if (tokenA.symbol && tokenB.symbol) { 34 | // sort by symbol 35 | return tokenA.symbol.toLowerCase() < tokenB.symbol.toLowerCase() ? -1 : 1 36 | } 37 | return tokenA.symbol ? -1 : tokenB.symbol ? -1 : 0 38 | } 39 | } 40 | 41 | function useTokenComparator(inverted: boolean): (tokenA: Token, tokenB: Token) => number { 42 | const balances = useAllTokenBalances() 43 | const comparator = useMemo(() => getTokenComparator(balances ?? {}), [balances]) 44 | return useMemo(() => { 45 | if (inverted) { 46 | return (tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1 47 | } 48 | return comparator 49 | }, [inverted, comparator]) 50 | } 51 | 52 | export default useTokenComparator 53 | -------------------------------------------------------------------------------- /packages/uikit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@scads-io/uikit", 3 | "version": "0.4.4", 4 | "description": "Set of UI components for scads projects", 5 | "main": "dist/index.cjs.js", 6 | "module": "dist/index.esm.js", 7 | "types": "dist/index.d.ts", 8 | "files": [ 9 | "dist" 10 | ], 11 | "repository": "https://github.com/scads-io/toolkit/tree/main/packages/scads-uikit", 12 | "license": "MIT", 13 | "scripts": { 14 | "start": "npm storybook", 15 | "build": "rollup -c && tsc -d --emitDeclarationOnly --declarationDir dist", 16 | "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", 17 | "format:check": "prettier --check --loglevel error 'src/**/*.{js,jsx,ts,tsx}'", 18 | "format:write": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", 19 | "storybook": "start-storybook -p 6006", 20 | "storybook:build": "build-storybook", 21 | "test": "jest", 22 | "prepublishOnly": "npm run build" 23 | }, 24 | "jest": { 25 | "setupFilesAfterEnv": [ 26 | "/src/setupTests.js" 27 | ] 28 | }, 29 | "devDependencies": { 30 | "@testing-library/jest-dom": "^5.11.6", 31 | "@testing-library/react": "^11.2.5", 32 | "@types/react-dom": "^17.0.5", 33 | "jest-styled-components": "^7.2.0", 34 | "react": "^18.2.0", 35 | "react-dom": "^18.2.0", 36 | "react-router-dom": "^5.2.0", 37 | "styled-components": "^6.0.7" 38 | }, 39 | "peerDependencies": { 40 | "react": "^18.2.0", 41 | "react-dom": "^18.2.0", 42 | "react-router-dom": "^5.2.0", 43 | "styled-components": "^6.0.7" 44 | }, 45 | "dependencies": { 46 | "@popperjs/core": "^2.9.2", 47 | "@types/lodash": "^4.14.168", 48 | "@types/styled-system": "^5.1.10", 49 | "lodash": "^4.17.20", 50 | "prettier": "^3.2.5", 51 | "react-popper": "^2.2.5", 52 | "styled-system": "^5.1.5" 53 | }, 54 | "publishConfig": { 55 | "access": "public" 56 | } 57 | } 58 | --------------------------------------------------------------------------------