├── .env ├── .env.production ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── config.yml │ └── feature-request.md └── pull_request_template.md ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── babel-plugin-macros.config.js ├── config-overrides.js ├── cypress.config.ts ├── cypress.release.config.ts ├── cypress ├── e2e │ ├── landing.test.ts │ ├── link.test.ts │ ├── lists.test.ts │ └── service-worker.test.ts ├── fixtures │ └── feeTierDistribution.json ├── release.ts ├── support │ └── e2e.ts └── tsconfig.json ├── lingui.config.ts ├── package.json ├── prei18n-extract.js ├── public ├── .nojekyll ├── 451.html ├── CNAME ├── favicon.png ├── fonts │ └── Inter-roman.var.woff2 ├── images │ ├── 192x192_App_Icon.png │ ├── 22_open.72c00877.png │ ├── 256x256_App_Icon_Black.svg │ ├── 33_open.43a09438.png │ └── 512x512_App_Icon.png ├── index.html └── manifest.json ├── src ├── assets │ ├── anime_bg.jpg │ ├── aptos_logo.svg │ ├── blog.svg │ ├── blue-loader.svg │ ├── discord.svg │ ├── dot_line.svg │ ├── dropdown.svg │ ├── fewcha.svg │ ├── gas-icon.svg │ ├── github.svg │ ├── help.svg │ ├── logo.png │ ├── martian.png │ ├── menu.svg │ ├── noise.png │ ├── petra.svg │ ├── pontem.svg │ ├── rise.png │ ├── static_route.svg │ ├── tooltip_triangle.svg │ ├── twitter.svg │ ├── verified.svg │ └── x.svg ├── components │ ├── AccountDetails │ │ ├── Copy.tsx │ │ ├── Transaction.tsx │ │ ├── TransactionSummary.tsx │ │ └── index.tsx │ ├── AnimatedDropdown │ │ └── index.tsx │ ├── Badge │ │ ├── RangeBadge.tsx │ │ └── index.tsx │ ├── Button │ │ └── index.tsx │ ├── Card │ │ └── index.tsx │ ├── CoinInputPanel │ │ ├── FiatValue.tsx │ │ └── index.tsx │ ├── CoinLogo │ │ └── index.tsx │ ├── Column │ │ └── index.tsx │ ├── DoubleLogo │ │ └── index.tsx │ ├── ErrorBoundary │ │ └── index.tsx │ ├── FeatureFlagModal │ │ └── FeatureFlagModal.tsx │ ├── Footer │ │ └── index.tsx │ ├── Header │ │ ├── ChainConnectivityWarning.tsx │ │ ├── HeaderStatus.tsx │ │ ├── NetworkSelector.tsx │ │ └── index.tsx │ ├── HoverInlineText │ │ └── index.tsx │ ├── Identicon │ │ └── index.tsx │ ├── ListLogo │ │ └── index.tsx │ ├── Loader │ │ ├── index.tsx │ │ └── styled.tsx │ ├── Logo │ │ └── index.tsx │ ├── Menu │ │ └── index.tsx │ ├── Modal │ │ └── index.tsx │ ├── ModalViews │ │ └── index.tsx │ ├── NavigationTabs │ │ └── index.tsx │ ├── NumericalInput │ │ └── index.tsx │ ├── Popover │ │ └── index.tsx │ ├── Popups │ │ ├── FailedNetworkSwitchPopup.tsx │ │ ├── PopupItem.tsx │ │ ├── TransactionPopup.tsx │ │ └── index.tsx │ ├── PositionCard │ │ └── index.tsx │ ├── PrivacyPolicy │ │ └── index.tsx │ ├── ProgressSteps │ │ └── index.tsx │ ├── QuestionHelper │ │ └── index.tsx │ ├── RoutingDiagram │ │ └── RoutingDiagram.tsx │ ├── Row │ │ └── index.tsx │ ├── SearchModal │ │ ├── BlockedToken.tsx │ │ ├── CoinList │ │ │ └── index.tsx │ │ ├── CoinSearch.tsx │ │ ├── CoinSearchModal.tsx │ │ ├── CommonBases.tsx │ │ ├── ImportList.tsx │ │ ├── ImportRow.tsx │ │ ├── ImportToken.tsx │ │ ├── Manage.tsx │ │ ├── ManageLists.tsx │ │ ├── ManageTokens.tsx │ │ ├── TokenImportCard.tsx │ │ └── styleds.tsx │ ├── Settings │ │ └── index.tsx │ ├── Slider │ │ └── index.tsx │ ├── SwitchLocaleLink │ │ └── index.tsx │ ├── TextInput │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ └── index.tsx │ ├── Toggle │ │ ├── MultiToggle.tsx │ │ └── index.tsx │ ├── TokenSafety │ │ ├── TokenSafetyIcon.tsx │ │ ├── TokenSafetyLabel.tsx │ │ ├── TokenSafetyMessage.tsx │ │ ├── TokenSafetyModal.tsx │ │ ├── index.tsx │ │ └── verified.svg │ ├── TokenWarningModal │ │ └── index.tsx │ ├── Tooltip │ │ └── index.tsx │ ├── TopLevelModals │ │ └── index.tsx │ ├── TransactionConfirmationModal │ │ ├── AnimatedConfirmation.tsx │ │ └── index.tsx │ ├── TransactionSettings │ │ └── index.tsx │ ├── WalletModal │ │ ├── FewchaOption.tsx │ │ ├── MartianOption.tsx │ │ ├── Option.tsx │ │ ├── PetraOption.tsx │ │ ├── PontemOption.tsx │ │ ├── RiseOption.tsx │ │ └── index.tsx │ ├── analytics │ │ ├── GoogleAnalyticsProvider.tsx │ │ └── index.ts │ ├── claim │ │ └── AddressClaimModal.tsx │ ├── earn │ │ ├── ClaimRewardModal.tsx │ │ ├── PoolCard.tsx │ │ ├── StakingModal.tsx │ │ ├── UnstakingModal.tsx │ │ └── styled.ts │ └── swap │ │ ├── AdvancedSwapDetails.tsx │ │ ├── AutoRouterIcon.tsx │ │ ├── ConfirmSwapModal.tsx │ │ ├── FormattedPriceImpact.tsx │ │ ├── PriceImpactWarning.tsx │ │ ├── RouterLabel.tsx │ │ ├── SwapDetailsDropdown.tsx │ │ ├── SwapHeader.tsx │ │ ├── SwapModalFooter.tsx │ │ ├── SwapModalHeader.tsx │ │ ├── SwapRoute.tsx │ │ ├── TradePrice.tsx │ │ └── styleds.tsx ├── constants │ ├── TokenSafetyLookupTable.ts │ ├── chainInfo.ts │ ├── chains.ts │ ├── coinInfo.ts │ ├── locales.ts │ ├── misc.ts │ └── tokenSafety.tsx ├── featureFlags │ ├── flags │ │ ├── phase0.ts │ │ └── phase1.ts │ └── index.tsx ├── hooks │ ├── common │ │ ├── Coin.ts │ │ └── Pair.ts │ ├── useActiveLocale.ts │ ├── useBestTrade.ts │ ├── useColor.ts │ ├── useCopyClipboard.ts │ ├── useDebounce.ts │ ├── useDebouncedChangeHandler.tsx │ ├── useInterval.ts │ ├── useIsWindowVisible.ts │ ├── useLast.ts │ ├── useLocationLinkProps.ts │ ├── useOnClickOutside.tsx │ ├── useParsedQueryString.ts │ ├── usePrevious.ts │ ├── useTheme.ts │ ├── useToggle.ts │ ├── useTokenInfoFromActiveList.ts │ ├── useTokenWarningColor.ts │ ├── useTopTokens.ts │ └── useWindowSize.ts ├── i18n │ ├── LanguageProvider.tsx │ └── lib.tsx ├── index.tsx ├── locales │ ├── README.md │ ├── af-ZA.po │ ├── ar-SA.po │ ├── ca-ES.po │ ├── cs-CZ.po │ ├── da-DK.po │ ├── de-DE.po │ ├── el-GR.po │ ├── es-ES.po │ ├── fi-FI.po │ ├── fr-FR.po │ ├── he-IL.po │ ├── hu-HU.po │ ├── id-ID.po │ ├── it-IT.po │ ├── ja-JP.po │ ├── ko-KR.po │ ├── nl-NL.po │ ├── no-NO.po │ ├── pl-PL.po │ ├── pt-BR.po │ ├── pt-PT.po │ ├── ro-RO.po │ ├── ru-RU.po │ ├── sl-SI.po │ ├── sr-SP.po │ ├── sv-SE.po │ ├── sw-TZ.po │ ├── th-TH.po │ ├── tr-TR.po │ ├── uk-UA.po │ ├── vi-VN.po │ ├── zh-CN.po │ └── zh-TW.po ├── pages │ ├── AddLiquidity │ │ ├── ConfirmAddModalBottom.tsx │ │ ├── PoolPriceBar.tsx │ │ ├── index.tsx │ │ └── redirects.tsx │ ├── App.tsx │ ├── AppBody.tsx │ ├── Explore │ │ └── index.tsx │ ├── Pool │ │ ├── index.tsx │ │ └── styleds.tsx │ ├── RemoveLiquidity │ │ ├── index.tsx │ │ └── styled.ts │ ├── Swap │ │ ├── index.tsx │ │ └── redirects.tsx │ └── styled.tsx ├── react-app-env.d.ts ├── service-worker.ts ├── serviceWorker │ ├── document.test.ts │ ├── document.ts │ ├── index.ts │ └── utils.ts ├── serviceWorkerRegistration.ts ├── setupTests.ts ├── state │ ├── application │ │ ├── hooks.ts │ │ └── reducer.ts │ ├── connection │ │ ├── hooks.ts │ │ ├── instance.ts │ │ └── reducer.ts │ ├── global │ │ └── actions.ts │ ├── hooks.ts │ ├── index.ts │ ├── lists │ │ ├── actions.ts │ │ ├── hooks.ts │ │ └── reducer.ts │ ├── mint │ │ ├── actions.ts │ │ ├── hooks.tsx │ │ └── reducer.ts │ ├── swap │ │ ├── actions.ts │ │ ├── hooks.tsx │ │ └── reducer.ts │ ├── transactions │ │ ├── hooks.tsx │ │ ├── reducer.ts │ │ └── types.ts │ ├── user │ │ ├── hooks.tsx │ │ ├── reducer.ts │ │ └── updater.tsx │ └── wallets │ │ ├── hooks.tsx │ │ ├── reducer.ts │ │ └── types.ts ├── test-utils.tsx ├── theme │ ├── DarkModeQueryParamReader.tsx │ ├── RadialGradientByChainUpdater.ts │ ├── colors.ts │ ├── components.tsx │ ├── index.tsx │ ├── styled.d.ts │ └── utils.tsx └── utils │ ├── anonymizeLink.ts │ ├── env.test.ts │ ├── env.ts │ ├── formatDollarAmt.tsx │ ├── getExplorerLink.ts │ ├── index.ts │ ├── prices.ts │ ├── tryParseCoinAmount.ts │ ├── uriToHttp.ts │ └── userAgent.ts ├── tsconfig.json └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | GENERATE_SOURCEMAP=false 2 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | GENERATE_SOURCEMAP=false 2 | REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8" -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaVersion": 2020, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | // Allows for the parsing of JSX 8 | "jsx": true 9 | } 10 | }, 11 | "settings": { 12 | "react": { 13 | "version": "detect" 14 | } 15 | }, 16 | "ignorePatterns": [ 17 | "src/abis/types", 18 | "src/locales/**/*.js", 19 | "src/locales/**/en-US.po", 20 | "node_modules", 21 | "coverage", 22 | "build", 23 | "dist", 24 | ".DS_Store", 25 | ".env.local", 26 | ".env.development.local", 27 | ".env.test.local", 28 | ".env.production.local", 29 | ".idea/", 30 | ".vscode/", 31 | "package-lock.json", 32 | "yarn.lock" 33 | ], 34 | "extends": [ 35 | "plugin:@typescript-eslint/recommended", 36 | "plugin:react-hooks/recommended", 37 | "plugin:prettier/recommended" 38 | ], 39 | "plugins": [ 40 | "simple-import-sort", 41 | "unused-imports" 42 | ], 43 | "rules": { 44 | "react-hooks/exhaustive-deps": "off", 45 | "simple-import-sort/imports": "error", 46 | "simple-import-sort/exports": "error", 47 | "@typescript-eslint/explicit-function-return-type": "off", 48 | "prettier/prettier": [ 49 | "error" 50 | ], 51 | "unused-imports/no-unused-imports": "off", 52 | "@typescript-eslint/no-explicit-any": "off", 53 | "@typescript-eslint/ban-ts-comment": "off", 54 | "@typescript-eslint/ban-ts-ignore": "off", 55 | "@typescript-eslint/explicit-module-boundary-types": "off", 56 | "@typescript-eslint/no-unused-vars": "off", 57 | "@typescript-eslint/prefer-as-const": "off", 58 | "@typescript-eslint/no-empty-function": "off", 59 | "react/react-in-jsx-scope": "off", 60 | "object-shorthand": [ 61 | "error", 62 | "always" 63 | ] 64 | } 65 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Describe an issue in the AnimeSwap Interface 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Bug Description** 10 | A clear and concise description of the bug. 11 | 12 | **Steps to Reproduce** 13 | 14 | 1. Go to ... 15 | 2. Click on ... 16 | ... 17 | 18 | **Expected Behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Additional Context** 22 | Add any other context about the problem here (screenshots, whether the bug only occurs only in certain mobile/desktop/browser environments, etc.) 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Support 4 | url: https://discord.gg/rbUG6SpRAM 5 | about: Please ask and answer questions here 6 | - name: List a coin 7 | url: https://github.com/AnimeSwap/coin-list#how-to-add-to-this-coin-list 8 | about: Any requests to add a coin to AnimeSwap should go here 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for improving the UX of the AnimeSwap Interface 4 | title: '' 5 | labels: 'improvement' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Your PR title must follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary), and should start with one of the following [types](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type): 2 | 3 | - build: Changes that affect the build system or external dependencies (example scopes: yarn, eslint, typescript) 4 | - ci: Changes to our CI configuration files and scripts (example scopes: vercel, github, cypress) 5 | - docs: Documentation only changes 6 | - feat: A new feature 7 | - fix: A bug fix 8 | - perf: A code change that improves performance 9 | - refactor: A code change that neither fixes a bug nor adds a feature 10 | - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 11 | - test: Adding missing tests or correcting existing tests 12 | 13 | Example commit messages: 14 | 15 | - feat: adds support for gnosis safe wallet 16 | - fix: removes a polling memory leak 17 | - chore: bumps redux version 18 | 19 | Other things to note: 20 | 21 | - Please describe the change using verb statements (ex: Removes X from Y) 22 | - PRs with multiple changes should use a list of verb statements 23 | - Add any relevant unit / integration tests 24 | - Changes will be previewable via vercel. Non-obvious changes should include instructions for how to reproduce them 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # generated contract types 4 | /src/locales/**/*.js 5 | /src/locales/**/en-US.po 6 | /src/locales/**/pseudo.po 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # testing 12 | /coverage 13 | 14 | # builds 15 | /build 16 | /dts 17 | 18 | # misc 19 | .DS_Store 20 | .env.local 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | /.netlify 26 | 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | notes.txt 32 | .idea/ 33 | 34 | package-lock.json 35 | 36 | cypress/videos 37 | cypress/screenshots 38 | 39 | .vercel 40 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "npm.packageManager": "yarn", 3 | "typescript.updateImportsOnFileMove.enabled": "always", 4 | "javascript.updateImportsOnFileMove.enabled": "always", 5 | "editor.formatOnSaveMode": "file", 6 | "editor.tabCompletion": "on", 7 | "editor.tabSize": 2, 8 | "editor.formatOnSave": false, 9 | "editor.inlineSuggest.enabled": true, 10 | "editor.codeActionsOnSave": { 11 | "source.fixAll": true 12 | }, 13 | "files.eol": "\n", 14 | "eslint.enable": true, 15 | "eslint.debug": true, 16 | "[typescript]": { 17 | "editor.defaultFormatter": "vscode.typescript-language-features" 18 | } 19 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AnimeSwap Interface 2 | 3 | An open source interface for AnimeSwap -- a protocol for decentralized exchange on Aptos. 4 | 5 | - Website: [animeswap.org](https://animeswap.org) 6 | - Interface: [app.animeswap.org](https://app.animeswap.org) 7 | - Docs: [docs.animeswap.org](https://docs.animeswap.org) 8 | - Twitter: [@animeswap_org](https://twitter.com/animeswap_org) 9 | - Email: [support@animeswap.org](mailto:support@animeswap.org) 10 | - Discord: [AnimeSwap](https://discord.gg/rbUG6SpRAM) 11 | - Whitepapers: 12 | - [v1](https://docs.animeswap.org/blog/) 13 | ## Accessing the AnimeSwap Interface 14 | 15 | To access the AnimeSwap Interface, visit [app.animeswap.org](https://app.animeswap.org). 16 | -------------------------------------------------------------------------------- /babel-plugin-macros.config.js: -------------------------------------------------------------------------------- 1 | const isDev = process.env.NODE_ENV === 'development' 2 | 3 | module.exports = { 4 | styledComponents: { 5 | fileName: isDev, 6 | displayName: isDev, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const webpack = require('webpack') 3 | module.exports = function override(config) { 4 | const fallback = config.resolve.fallback || {} 5 | Object.assign(fallback, { 6 | stream: require.resolve('stream-browserify'), 7 | }) 8 | config.resolve.fallback = fallback 9 | config.plugins = (config.plugins || []).concat([ 10 | new webpack.ProvidePlugin({ 11 | process: 'process/browser', 12 | Buffer: ['buffer', 'Buffer'], 13 | }), 14 | ]) 15 | return config 16 | } 17 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress' 2 | 3 | export default defineConfig({ 4 | projectId: 'yp82ef', 5 | videoUploadOnPasses: false, 6 | defaultCommandTimeout: 10000, 7 | chromeWebSecurity: false, 8 | e2e: { 9 | setupNodeEvents(on, config) { 10 | return { 11 | ...config, 12 | // Only enable Chrome. 13 | // Electron (the default) has issues injecting window.ethereum before pageload, so it is not viable. 14 | browsers: config.browsers.filter(({ name }) => name === 'chrome'), 15 | } 16 | }, 17 | baseUrl: 'http://localhost:3000', 18 | specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}', 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /cypress.release.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress' 2 | 3 | export default defineConfig({ 4 | projectId: 'yp82ef', 5 | e2e: { 6 | specPattern: 'cypress/release.ts', 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /cypress/e2e/landing.test.ts: -------------------------------------------------------------------------------- 1 | describe('Landing Page', () => { 2 | beforeEach(() => cy.visit('/')) 3 | it('loads swap page', () => { 4 | cy.get('#swap-page') 5 | cy.screenshot() 6 | }) 7 | 8 | it('redirects to url /swap', () => { 9 | cy.url().should('include', '/swap') 10 | }) 11 | 12 | it('allows navigation to pool', () => { 13 | cy.get('#pool-nav-link').click() 14 | cy.url().should('include', '/pool') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /cypress/e2e/link.test.ts: -------------------------------------------------------------------------------- 1 | // see https://github.com/Uniswap/interface/pull/4115 2 | describe('Link', () => { 3 | it('should update route', () => { 4 | cy.visit('/') 5 | cy.get('[data-cy="pool-nav-link"]').click() 6 | cy.get('[data-cy="join-pool-button"]').should('exist') 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /cypress/e2e/lists.test.ts: -------------------------------------------------------------------------------- 1 | describe('Lists', () => { 2 | beforeEach(() => { 3 | cy.visit('/swap') 4 | }) 5 | 6 | // @TODO check if default lists are active when we have them 7 | it('change list', () => { 8 | cy.get('#swap-currency-output .open-currency-select-button').click() 9 | cy.get('.list-token-manage-button').click() 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /cypress/fixtures/feeTierDistribution.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "block": { 4 | "number": 99999999 5 | } 6 | }, 7 | "asToken0": [ 8 | { 9 | "feeTier": "100", 10 | "totalValueLockedToken0": "0", 11 | "totalValueLockedToken1": "3" 12 | }, 13 | { 14 | "feeTier": "500", 15 | "totalValueLockedToken0": "0", 16 | "totalValueLockedToken1": "1" 17 | }, 18 | { 19 | "feeTier": "3000", 20 | "totalValueLockedToken0": "0", 21 | "totalValueLockedToken1": "4" 22 | }, 23 | { 24 | "feeTier": "10000", 25 | "totalValueLockedToken0": "0", 26 | "totalValueLockedToken1": "2" 27 | } 28 | ], 29 | "asToken1": [] 30 | } 31 | -------------------------------------------------------------------------------- /cypress/release.ts: -------------------------------------------------------------------------------- 1 | const ONE_MINUTE = 60_000 2 | 3 | describe( 4 | 'Release', 5 | { 6 | pageLoadTimeout: ONE_MINUTE, 7 | retries: 30, 8 | }, 9 | () => { 10 | it('loads swap page', () => { 11 | // We *must* wait in order to space out the retry attempts. 12 | cy.wait(ONE_MINUTE) 13 | .visit('/', { 14 | retryOnStatusCodeFailure: true, 15 | retryOnNetworkFailure: true, 16 | }) 17 | .get('#swap-page') 18 | }) 19 | } 20 | ) 21 | -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 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 { injected } from './ethereum' 10 | import assert = require('assert') 11 | 12 | declare global { 13 | // eslint-disable-next-line @typescript-eslint/no-namespace 14 | namespace Cypress { 15 | interface ApplicationWindow { 16 | ethereum: typeof injected 17 | } 18 | interface VisitOptions { 19 | serviceWorker?: true 20 | } 21 | } 22 | } 23 | 24 | // sets up the injected provider to be a mock ethereum provider with the given mnemonic/index 25 | // eslint-disable-next-line no-undef 26 | Cypress.Commands.overwrite( 27 | 'visit', 28 | (original, url: string | Partial, options?: Partial) => { 29 | assert(typeof url === 'string') 30 | 31 | cy.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 }).then(() => { 32 | original({ 33 | ...options, 34 | url: (url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url) + '?chain=rinkeby', 35 | onBeforeLoad(win) { 36 | options?.onBeforeLoad?.(win) 37 | win.localStorage.clear() 38 | win.ethereum = injected 39 | }, 40 | }) 41 | }) 42 | } 43 | ) 44 | 45 | beforeEach(() => { 46 | // Infura security policies are based on Origin headers. 47 | // These are stripped by cypress because chromeWebSecurity === false; this adds them back in. 48 | cy.intercept(/infura.io/, (res) => { 49 | res.headers['origin'] = 'http://localhost:3000' 50 | res.continue() 51 | }) 52 | }) 53 | 54 | Cypress.on('uncaught:exception', (_err, _runnable) => { 55 | // returning false here prevents Cypress from 56 | // failing the test 57 | return false 58 | }) 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lingui.config.ts: -------------------------------------------------------------------------------- 1 | const linguiConfig = { 2 | catalogs: [ 3 | { 4 | path: '/src/locales/{locale}', 5 | include: ['/src'], 6 | }, 7 | ], 8 | compileNamespace: 'cjs', 9 | fallbackLocales: { 10 | default: 'en-US', 11 | }, 12 | format: 'po', 13 | formatOptions: { 14 | lineNumbers: false, 15 | }, 16 | locales: [ 17 | 'af-ZA', 18 | 'ar-SA', 19 | 'ca-ES', 20 | 'cs-CZ', 21 | 'da-DK', 22 | 'de-DE', 23 | 'el-GR', 24 | 'en-US', 25 | 'es-ES', 26 | 'fi-FI', 27 | 'fr-FR', 28 | 'he-IL', 29 | 'hu-HU', 30 | 'id-ID', 31 | 'it-IT', 32 | 'ja-JP', 33 | 'ko-KR', 34 | 'nl-NL', 35 | 'no-NO', 36 | 'pl-PL', 37 | 'pt-BR', 38 | 'pt-PT', 39 | 'ro-RO', 40 | 'ru-RU', 41 | 'sr-SP', 42 | 'sv-SE', 43 | 'sw-TZ', 44 | 'tr-TR', 45 | 'uk-UA', 46 | 'vi-VN', 47 | 'zh-CN', 48 | 'zh-TW', 49 | 'pseudo', 50 | ], 51 | orderBy: 'messageId', 52 | rootDir: '.', 53 | runtimeConfigModule: ['@lingui/core', 'i18n'], 54 | sourceLocale: 'en-US', 55 | pseudoLocale: 'pseudo', 56 | } 57 | 58 | export default linguiConfig 59 | -------------------------------------------------------------------------------- /prei18n-extract.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const exec = require('child_process').exec 3 | const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE) 4 | 5 | if (isWindows) { 6 | exec(`type nul > src/locales/en-US.po`) 7 | } else { 8 | exec(`touch src/locales/en-US.po`) 9 | } 10 | -------------------------------------------------------------------------------- /public/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/.nojekyll -------------------------------------------------------------------------------- /public/451.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Unavailable For Legal Reasons 6 | 7 | 8 |

Unavailable For Legal Reasons

9 | 10 | 11 | -------------------------------------------------------------------------------- /public/CNAME: -------------------------------------------------------------------------------- 1 | app.animeswap.org -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/favicon.png -------------------------------------------------------------------------------- /public/fonts/Inter-roman.var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/fonts/Inter-roman.var.woff2 -------------------------------------------------------------------------------- /public/images/192x192_App_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/images/192x192_App_Icon.png -------------------------------------------------------------------------------- /public/images/22_open.72c00877.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/images/22_open.72c00877.png -------------------------------------------------------------------------------- /public/images/33_open.43a09438.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/images/33_open.43a09438.png -------------------------------------------------------------------------------- /public/images/512x512_App_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/public/images/512x512_App_Icon.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background_color": "#fff", 3 | "display": "standalone", 4 | "homepage_url": "https://app.animeswap.org", 5 | "providedBy": { 6 | "name": "AnimeSwap", 7 | "url": "https://animeswap.org" 8 | }, 9 | "icons": [ 10 | { 11 | "src": "./images/192x192_App_Icon.png", 12 | "sizes": "192x192", 13 | "type": "image/png", 14 | "purpose": "any maskable" 15 | }, 16 | { 17 | "src": "./images/512x512_App_Icon.png", 18 | "sizes": "512x512", 19 | "type": "image/png", 20 | "purpose": "any maskable" 21 | } 22 | ], 23 | "orientation": "portrait", 24 | "name": "AnimeSwap", 25 | "description": "Swap or provide liquidity on the AnimeSwap Protocol", 26 | "iconPath": "./images/256x256_App_Icon_Black.svg", 27 | "short_name": "AnimeSwap", 28 | "start_url": ".", 29 | "theme_color": "#8A2BE2" 30 | } -------------------------------------------------------------------------------- /src/assets/anime_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/src/assets/anime_bg.jpg -------------------------------------------------------------------------------- /src/assets/aptos_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/blog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/blue-loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/dot_line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/fewcha.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/gas-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/help.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/martian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/src/assets/martian.png -------------------------------------------------------------------------------- /src/assets/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/src/assets/noise.png -------------------------------------------------------------------------------- /src/assets/petra.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/pontem.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/rise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto27dev/animswap/816b0dd0e439c324f80942f47c9bbdfe56bf5581/src/assets/rise.png -------------------------------------------------------------------------------- /src/assets/static_route.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/tooltip_triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/verified.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/AccountDetails/Copy.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import useCopyClipboard from 'hooks/useCopyClipboard' 3 | import React, { useCallback } from 'react' 4 | import { CheckCircle, Copy } from 'react-feather' 5 | import styled from 'styled-components/macro' 6 | import { LinkStyledButton } from 'theme' 7 | 8 | const CopyIcon = styled(LinkStyledButton)` 9 | color: ${({ color, theme }) => color || theme.accentAction}; 10 | flex-shrink: 0; 11 | display: flex; 12 | text-decoration: none; 13 | :hover, 14 | :active, 15 | :focus { 16 | text-decoration: none; 17 | color: ${({ color, theme }) => color || theme.accentAction}; 18 | } 19 | ` 20 | const StyledText = styled.span` 21 | margin-left: 0.25rem; 22 | ${({ theme }) => theme.flexRowNoWrap}; 23 | align-items: center; 24 | ` 25 | 26 | const Copied = ({ iconSize }: { iconSize?: number }) => ( 27 | 28 | 29 | 30 | Copied 31 | 32 | 33 | ) 34 | 35 | const Icon = ({ iconSize }: { iconSize?: number }) => ( 36 | 37 | 38 | 39 | ) 40 | 41 | interface BaseProps { 42 | toCopy: string 43 | color?: string 44 | iconSize?: number 45 | iconPosition?: 'left' | 'right' 46 | } 47 | export type CopyHelperProps = BaseProps & Omit, keyof BaseProps> 48 | 49 | export default function CopyHelper({ color, toCopy, children, iconSize, iconPosition }: CopyHelperProps) { 50 | const [isCopied, setCopied] = useCopyClipboard() 51 | const copy = useCallback(() => { 52 | setCopied(toCopy) 53 | }, [toCopy, setCopied]) 54 | 55 | return ( 56 | 57 | {iconPosition === 'left' ? isCopied ? : : null} 58 | {iconPosition === 'left' && <> } 59 | {isCopied ? '' : children} 60 | {iconPosition === 'right' && <> } 61 | {iconPosition === 'right' ? isCopied ? : : null} 62 | 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /src/components/AccountDetails/Transaction.tsx: -------------------------------------------------------------------------------- 1 | import { CheckCircle, Triangle } from 'react-feather' 2 | import { useChainId } from 'state/user/hooks' 3 | import styled from 'styled-components/macro' 4 | 5 | import { useAllTransactions } from '../../state/transactions/hooks' 6 | import { ExternalLink } from '../../theme' 7 | import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' 8 | import Loader from '../Loader' 9 | import { RowFixed } from '../Row' 10 | import { TransactionSummary } from './TransactionSummary' 11 | 12 | const TransactionStatusText = styled.div` 13 | margin-right: 0.5rem; 14 | display: flex; 15 | align-items: center; 16 | :hover { 17 | text-decoration: underline; 18 | } 19 | ` 20 | 21 | const TransactionState = styled(ExternalLink)<{ pending: boolean; success?: boolean }>` 22 | display: flex; 23 | justify-content: space-between; 24 | align-items: center; 25 | text-decoration: none !important; 26 | border-radius: 0.5rem; 27 | padding: 0.25rem 0rem; 28 | font-weight: 500; 29 | font-size: 0.825rem; 30 | color: ${({ theme }) => theme.deprecated_primary1}; 31 | ` 32 | 33 | const IconWrapper = styled.div<{ pending: boolean; success?: boolean }>` 34 | color: ${({ pending, success, theme }) => 35 | pending ? theme.deprecated_primary1 : success ? theme.deprecated_green1 : theme.deprecated_red1}; 36 | ` 37 | 38 | export default function Transaction({ hash }: { hash: string }) { 39 | const chainId = useChainId() 40 | const allTransactions = useAllTransactions() 41 | 42 | const tx = allTransactions?.[hash] 43 | const info = tx?.info 44 | const pending = !tx?.receipt 45 | const success = !pending && tx && (tx.receipt?.status === 1 || typeof tx.receipt?.status === 'undefined') 46 | 47 | if (!chainId) return null 48 | 49 | return ( 50 |
51 | 56 | 57 | 58 | ↗ 59 | 60 | 61 | 62 | {pending ? : success ? : } 63 | 64 | 65 |
66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /src/components/AnimatedDropdown/index.tsx: -------------------------------------------------------------------------------- 1 | import { animated, useSpring } from 'react-spring' 2 | import useResizeObserver from 'use-resize-observer' 3 | 4 | /** 5 | * @param open conditional to show content or hide 6 | * @returns Wrapper to smoothly hide and expand content 7 | */ 8 | export default function AnimatedDropdown({ open, children }: React.PropsWithChildren<{ open: boolean }>) { 9 | const { ref, height } = useResizeObserver() 10 | 11 | const props = useSpring({ 12 | height: open ? height ?? 0 : 0, 13 | config: { 14 | mass: 1.2, 15 | tension: 300, 16 | friction: 20, 17 | clamp: true, 18 | velocity: 0.01, 19 | }, 20 | }) 21 | 22 | return ( 23 | 31 |
{children}
32 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Badge/RangeBadge.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import Badge, { BadgeVariant } from 'components/Badge' 3 | import { AlertCircle } from 'react-feather' 4 | import styled from 'styled-components/macro' 5 | 6 | import { MouseoverTooltip } from '../../components/Tooltip' 7 | 8 | const BadgeWrapper = styled.div` 9 | font-size: 14px; 10 | display: flex; 11 | justify-content: flex-end; 12 | ` 13 | 14 | const BadgeText = styled.div` 15 | font-weight: 500; 16 | font-size: 14px; 17 | ` 18 | 19 | const ActiveDot = styled.span` 20 | background-color: ${({ theme }) => theme.deprecated_success}; 21 | border-radius: 50%; 22 | height: 8px; 23 | width: 8px; 24 | margin-right: 4px; 25 | ` 26 | 27 | export default function RangeBadge({ 28 | removed, 29 | inRange, 30 | }: { 31 | removed: boolean | undefined 32 | inRange: boolean | undefined 33 | }) { 34 | return ( 35 | 36 | {removed ? ( 37 | Your position has 0 liquidity, and is not earning fees.}> 38 | 39 | 40 |   41 | 42 | Closed 43 | 44 | 45 | 46 | ) : inRange ? ( 47 | 50 | The price of this pool is within your selected range. Your position is currently earning fees. 51 | 52 | } 53 | > 54 | 55 |   56 | 57 | In range 58 | 59 | 60 | 61 | ) : ( 62 | 65 | The price of this pool is outside of your selected range. Your position is not currently earning fees. 66 | 67 | } 68 | > 69 | 70 | 71 |   72 | 73 | Out of range 74 | 75 | 76 | 77 | )} 78 | 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /src/components/Badge/index.tsx: -------------------------------------------------------------------------------- 1 | import { readableColor } from 'polished' 2 | import { PropsWithChildren } from 'react' 3 | import styled, { DefaultTheme } from 'styled-components/macro' 4 | import { Color } from 'theme/styled' 5 | 6 | export enum BadgeVariant { 7 | DEFAULT = 'DEFAULT', 8 | NEGATIVE = 'NEGATIVE', 9 | POSITIVE = 'POSITIVE', 10 | PRIMARY = 'PRIMARY', 11 | WARNING = 'WARNING', 12 | 13 | WARNING_OUTLINE = 'WARNING_OUTLINE', 14 | } 15 | 16 | interface BadgeProps { 17 | variant?: BadgeVariant 18 | } 19 | 20 | function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): Color { 21 | switch (variant) { 22 | case BadgeVariant.NEGATIVE: 23 | return theme.deprecated_error 24 | case BadgeVariant.POSITIVE: 25 | return theme.deprecated_success 26 | case BadgeVariant.PRIMARY: 27 | return theme.deprecated_primary1 28 | case BadgeVariant.WARNING: 29 | return theme.deprecated_warning 30 | case BadgeVariant.WARNING_OUTLINE: 31 | return 'transparent' 32 | default: 33 | return theme.deprecated_bg2 34 | } 35 | } 36 | 37 | function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string { 38 | switch (variant) { 39 | case BadgeVariant.WARNING_OUTLINE: 40 | return `1px solid ${theme.deprecated_warning}` 41 | default: 42 | return 'unset' 43 | } 44 | } 45 | 46 | function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string { 47 | switch (variant) { 48 | case BadgeVariant.NEGATIVE: 49 | return readableColor(theme.deprecated_error) 50 | case BadgeVariant.POSITIVE: 51 | return readableColor(theme.deprecated_success) 52 | case BadgeVariant.WARNING: 53 | return readableColor(theme.deprecated_warning) 54 | case BadgeVariant.WARNING_OUTLINE: 55 | return theme.deprecated_warning 56 | default: 57 | return readableColor(theme.deprecated_bg2) 58 | } 59 | } 60 | 61 | const Badge = styled.div>` 62 | align-items: center; 63 | background-color: ${({ theme, variant }) => pickBackgroundColor(variant, theme)}; 64 | border: ${({ theme, variant }) => pickBorder(variant, theme)}; 65 | border-radius: 0.5rem; 66 | color: ${({ theme, variant }) => pickFontColor(variant, theme)}; 67 | display: inline-flex; 68 | padding: 4px 6px; 69 | justify-content: center; 70 | font-weight: 500; 71 | ` 72 | 73 | export default Badge 74 | -------------------------------------------------------------------------------- /src/components/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from 'rebass/styled-components' 2 | import styled from 'styled-components/macro' 3 | 4 | const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $borderRadius?: string }>` 5 | width: ${({ width }) => width ?? '100%'}; 6 | padding: ${({ padding }) => padding ?? '1rem'}; 7 | border-radius: ${({ $borderRadius }) => $borderRadius ?? '16px'}; 8 | border: ${({ border }) => border}; 9 | ` 10 | export default Card 11 | 12 | export const LightCard = styled(Card)` 13 | border: 1px solid ${({ theme }) => theme.deprecated_bg2}; 14 | background-color: ${({ theme }) => theme.deprecated_bg1}; 15 | ` 16 | 17 | export const LightGreyCard = styled(Card)` 18 | background-color: ${({ theme }) => theme.deprecated_bg2}; 19 | ` 20 | 21 | export const GreyCard = styled(Card)` 22 | background-color: ${({ theme }) => theme.deprecated_bg3}; 23 | ` 24 | 25 | export const DarkGreyCard = styled(Card)` 26 | background-color: ${({ theme }) => theme.deprecated_bg2}; 27 | ` 28 | 29 | export const DarkCard = styled(Card)` 30 | background-color: ${({ theme }) => theme.deprecated_bg0}; 31 | ` 32 | 33 | export const OutlineCard = styled(Card)` 34 | border: 1px solid ${({ theme }) => theme.deprecated_bg3}; 35 | ` 36 | 37 | export const YellowCard = styled(Card)` 38 | background-color: rgba(243, 132, 30, 0.05); 39 | color: ${({ theme }) => theme.deprecated_yellow3}; 40 | font-weight: 500; 41 | ` 42 | 43 | export const BlueCard = styled(Card)` 44 | background-color: ${({ theme }) => theme.deprecated_primary5}; 45 | color: ${({ theme }) => theme.deprecated_blue2}; 46 | border-radius: 12px; 47 | ` 48 | -------------------------------------------------------------------------------- /src/components/CoinInputPanel/FiatValue.tsx: -------------------------------------------------------------------------------- 1 | import { Decimal } from '@animeswap.org/v1-sdk' 2 | import { Trans } from '@lingui/macro' 3 | // eslint-disable-next-line no-restricted-imports 4 | import { t } from '@lingui/macro' 5 | import HoverInlineText from 'components/HoverInlineText' 6 | import { useMemo } from 'react' 7 | 8 | import useTheme from '../../hooks/useTheme' 9 | import { ThemedText } from '../../theme' 10 | import { MouseoverTooltip } from '../Tooltip' 11 | 12 | export function FiatValue({ 13 | fiatValue, 14 | priceImpact, 15 | }: { 16 | fiatValue: Decimal | null | undefined 17 | priceImpact?: Decimal 18 | }) { 19 | const theme = useTheme() 20 | const priceImpactColor = useMemo(() => { 21 | if (!priceImpact) return undefined 22 | if (priceImpact.lessThan('0')) return theme.deprecated_green1 23 | // const severity = warningSeverity(priceImpact) 24 | // if (severity < 1) return theme.deprecated_text3 25 | // if (severity < 3) return theme.deprecated_yellow1 26 | return theme.deprecated_red1 27 | }, [priceImpact, theme.deprecated_green1, theme.deprecated_red1, theme.deprecated_text3, theme.deprecated_yellow1]) 28 | 29 | const p = Number(fiatValue?.toFixed()) 30 | const visibleDecimalPlaces = p < 1.05 ? 4 : 2 31 | 32 | return ( 33 | 34 | {fiatValue ? ( 35 | 36 | $ 37 | 41 | 42 | ) : ( 43 | '' 44 | )} 45 | {priceImpact ? ( 46 | 47 | {' '} 48 | 49 | ({priceImpact.mul(-1).toSD(3).toString()}%) 50 | 51 | 52 | ) : null} 53 | 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /src/components/CoinLogo/index.tsx: -------------------------------------------------------------------------------- 1 | import { Coin } from 'hooks/common/Coin' 2 | import React from 'react' 3 | import styled from 'styled-components/macro' 4 | 5 | import Logo from '../Logo' 6 | 7 | const StyledLogo = styled(Logo)<{ size: string }>` 8 | width: ${({ size }) => size}; 9 | height: ${({ size }) => size}; 10 | // background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%); 11 | border-radius: 50%; 12 | -mox-box-shadow: 0 0 1px black; 13 | -webkit-box-shadow: 0 0 1px black; 14 | box-shadow: 0 0 1px black; 15 | border: 0px solid rgba(255, 255, 255, 0); 16 | ` 17 | 18 | export default function CoinLogo({ 19 | coin, 20 | size = '24px', 21 | style, 22 | ...rest 23 | }: { 24 | coin?: Coin | null 25 | size?: string 26 | style?: React.CSSProperties 27 | }) { 28 | const props = { 29 | alt: `${coin?.symbol ?? 'coin'} logo`, 30 | size, 31 | srcs: coin?.logoURL, 32 | style, 33 | ...rest, 34 | } 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /src/components/Column/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/macro' 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 | ` 22 | 23 | export default Column 24 | -------------------------------------------------------------------------------- /src/components/DoubleLogo/index.tsx: -------------------------------------------------------------------------------- 1 | import { Coin } from 'hooks/common/Coin' 2 | import styled from 'styled-components/macro' 3 | 4 | import CoinLogo from '../CoinLogo' 5 | 6 | const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>` 7 | position: relative; 8 | display: flex; 9 | flex-direction: row; 10 | margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'}; 11 | ` 12 | 13 | interface DoubleCoinLogoProps { 14 | margin?: boolean 15 | size?: number 16 | coinX?: Coin 17 | coinY?: Coin 18 | } 19 | 20 | const HigherLogo = styled(CoinLogo)` 21 | z-index: 2; 22 | ` 23 | const CoveredLogo = styled(CoinLogo)<{ sizeraw: number }>` 24 | position: absolute; 25 | left: ${({ sizeraw }) => '-' + (sizeraw / 2).toString() + 'px'} !important; 26 | ` 27 | 28 | export default function DoubleCoinLogo({ coinX, coinY, size = 16, margin = false }: DoubleCoinLogoProps) { 29 | return ( 30 | 31 | {coinX && } 32 | {coinY && } 33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactComponent as Blog } from 'assets/blog.svg' 2 | import { ReactComponent as Discord } from 'assets/discord.svg' 3 | import { ReactComponent as GitHub } from 'assets/github.svg' 4 | import { ReactComponent as Help } from 'assets/help.svg' 5 | import { ReactComponent as Twitter } from 'assets/twitter.svg' 6 | import styled from 'styled-components/macro' 7 | 8 | const FooterItem = styled.a` 9 | margin-left: 0.5rem; 10 | margin-right: 0.5rem; 11 | fill: ${({ theme }) => theme.deprecated_text3}; 12 | :hover { 13 | fill: ${({ theme }) => theme.deprecated_text2}; 14 | } 15 | ` 16 | 17 | export default function Footer() { 18 | return ( 19 | <> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /src/components/Header/ChainConnectivityWarning.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import { getChainInfoOrDefault } from 'constants/chainInfo' 3 | import { SupportedChainId } from 'constants/chains' 4 | import { AlertOctagon } from 'react-feather' 5 | import { useChainId } from 'state/user/hooks' 6 | import styled from 'styled-components/macro' 7 | import { ExternalLink, MEDIA_WIDTHS } from 'theme' 8 | 9 | const BodyRow = styled.div` 10 | color: ${({ theme }) => theme.deprecated_black}; 11 | font-size: 12px; 12 | ` 13 | const CautionIcon = styled(AlertOctagon)` 14 | color: ${({ theme }) => theme.deprecated_black}; 15 | ` 16 | const Link = styled(ExternalLink)` 17 | color: ${({ theme }) => theme.deprecated_black}; 18 | text-decoration: underline; 19 | ` 20 | const TitleRow = styled.div` 21 | align-items: center; 22 | display: flex; 23 | justify-content: flex-start; 24 | margin-bottom: 8px; 25 | ` 26 | const TitleText = styled.div` 27 | color: black; 28 | font-weight: 600; 29 | font-size: 16px; 30 | line-height: 20px; 31 | margin: 0px 12px; 32 | ` 33 | const Wrapper = styled.div` 34 | background-color: ${({ theme }) => theme.deprecated_yellow3}; 35 | border-radius: 12px; 36 | bottom: 60px; 37 | display: none; 38 | max-width: 348px; 39 | padding: 16px 20px; 40 | position: absolute; 41 | right: 16px; 42 | @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { 43 | display: block; 44 | } 45 | ` 46 | 47 | export function ChainConnectivityWarning() { 48 | const chainId = useChainId() 49 | const info = getChainInfoOrDefault(chainId) 50 | const label = info?.label 51 | 52 | return ( 53 | 54 | 55 | 56 | 57 | Network Warning 58 | 59 | 60 | 61 | {chainId === SupportedChainId.APTOS ? ( 62 | You may have lost your network connection. 63 | ) : ( 64 | You may have lost your network connection, or {label} might be down right now. 65 | )}{' '} 66 | 67 | 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /src/components/HoverInlineText/index.tsx: -------------------------------------------------------------------------------- 1 | import Tooltip from 'components/Tooltip' 2 | import { useState } from 'react' 3 | import styled from 'styled-components/macro' 4 | 5 | const TextWrapper = styled.span<{ 6 | margin: boolean 7 | link?: boolean 8 | fontSize?: string 9 | adjustSize?: boolean 10 | textColor?: string 11 | }>` 12 | margin-left: ${({ margin }) => margin && '4px'}; 13 | color: ${({ theme, link, textColor }) => (link ? theme.deprecated_blue1 : textColor ?? theme.deprecated_text1)}; 14 | font-size: ${({ fontSize }) => fontSize ?? 'inherit'}; 15 | 16 | @media screen and (max-width: 600px) { 17 | font-size: ${({ adjustSize }) => adjustSize && '12px'}; 18 | } 19 | ` 20 | 21 | const HoverInlineText = ({ 22 | text, 23 | maxCharacters = 20, 24 | margin = false, 25 | adjustSize = false, 26 | fontSize, 27 | textColor, 28 | link, 29 | ...rest 30 | }: { 31 | text?: string 32 | maxCharacters?: number 33 | margin?: boolean 34 | adjustSize?: boolean 35 | fontSize?: string 36 | textColor?: string 37 | link?: boolean 38 | }) => { 39 | const [showHover, setShowHover] = useState(false) 40 | 41 | if (!text) { 42 | return 43 | } 44 | 45 | if (text.length > maxCharacters) { 46 | return ( 47 | 48 | setShowHover(true)} 50 | onMouseLeave={() => setShowHover(false)} 51 | margin={margin} 52 | adjustSize={adjustSize} 53 | textColor={textColor} 54 | link={link} 55 | fontSize={fontSize} 56 | {...rest} 57 | > 58 | {' ' + text.slice(0, maxCharacters - 1) + '...'} 59 | 60 | 61 | ) 62 | } 63 | 64 | return ( 65 | 73 | {text} 74 | 75 | ) 76 | } 77 | 78 | export default HoverInlineText 79 | -------------------------------------------------------------------------------- /src/components/Identicon/index.tsx: -------------------------------------------------------------------------------- 1 | import jazzicon from '@metamask/jazzicon' 2 | import { useLayoutEffect, useMemo, useRef, useState } from 'react' 3 | import { useAccount } from 'state/wallets/hooks' 4 | import styled from 'styled-components/macro' 5 | 6 | const StyledIdenticon = styled.div` 7 | height: 1rem; 8 | width: 1rem; 9 | border-radius: 1.125rem; 10 | background-color: ${({ theme }) => theme.deprecated_bg4}; 11 | font-size: initial; 12 | ` 13 | 14 | const StyledAvatar = styled.img` 15 | height: inherit; 16 | width: inherit; 17 | border-radius: inherit; 18 | ` 19 | 20 | export default function Identicon() { 21 | const account = useAccount() 22 | // const { avatar } = useENSAvatar(account ?? undefined) 23 | const [fetchable, setFetchable] = useState(true) 24 | 25 | const icon = useMemo(() => account && jazzicon(16, parseInt(account.slice(2, 10), 16)), [account]) 26 | const iconRef = useRef(null) 27 | useLayoutEffect(() => { 28 | const current = iconRef.current 29 | if (icon) { 30 | current?.appendChild(icon) 31 | return () => { 32 | try { 33 | current?.removeChild(icon) 34 | } catch (e) { 35 | console.error('Avatar icon not found') 36 | } 37 | } 38 | } 39 | return 40 | }, [icon, iconRef]) 41 | 42 | return ( 43 | 44 | {/* {avatar && fetchable ? ( 45 | setFetchable(false)}> 46 | ) : ( */} 47 | 48 | {/* )} */} 49 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /src/components/ListLogo/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components/macro' 3 | 4 | import Logo from '../Logo' 5 | 6 | const StyledListLogo = styled(Logo)<{ size: string }>` 7 | width: ${({ size }) => size}; 8 | height: ${({ size }) => size}; 9 | ` 10 | 11 | export default function ListLogo({ 12 | logoURL, 13 | style, 14 | size = '24px', 15 | alt, 16 | }: { 17 | logoURL: string 18 | size?: string 19 | style?: React.CSSProperties 20 | alt?: string 21 | }) { 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Loader/index.tsx: -------------------------------------------------------------------------------- 1 | import styled, { keyframes } from 'styled-components/macro' 2 | 3 | const rotate = keyframes` 4 | from { 5 | transform: rotate(0deg); 6 | } 7 | to { 8 | transform: rotate(360deg); 9 | } 10 | ` 11 | 12 | const StyledSVG = styled.svg<{ size: string; stroke?: string }>` 13 | animation: 2s ${rotate} linear infinite; 14 | height: ${({ size }) => size}; 15 | width: ${({ size }) => size}; 16 | path { 17 | stroke: ${({ stroke, theme }) => stroke ?? theme.deprecated_primary1}; 18 | } 19 | ` 20 | 21 | /** 22 | * Takes in custom size and stroke for circle color, default to primary color as fill, 23 | * need ...rest for layered styles on top 24 | */ 25 | export default function Loader({ 26 | size = '16px', 27 | stroke, 28 | ...rest 29 | }: { 30 | size?: string 31 | stroke?: string 32 | [k: string]: any 33 | }) { 34 | return ( 35 | 36 | 42 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/components/Loader/styled.tsx: -------------------------------------------------------------------------------- 1 | import styled, { css, keyframes } from 'styled-components/macro' 2 | 3 | export const loadingAnimation = keyframes` 4 | 0% { 5 | background-position: 100% 50%; 6 | } 7 | 100% { 8 | background-position: 0% 50%; 9 | } 10 | ` 11 | 12 | export const LoadingRows = styled.div` 13 | display: grid; 14 | 15 | & > div { 16 | animation: ${loadingAnimation} 1.5s infinite; 17 | animation-fill-mode: both; 18 | background: linear-gradient( 19 | to left, 20 | ${({ theme }) => theme.deprecated_bg1} 25%, 21 | ${({ theme }) => theme.deprecated_bg2} 50%, 22 | ${({ theme }) => theme.deprecated_bg1} 75% 23 | ); 24 | background-size: 400%; 25 | border-radius: 12px; 26 | height: 2.4em; 27 | will-change: background-position; 28 | } 29 | ` 30 | 31 | export const loadingOpacityMixin = css<{ $loading: boolean }>` 32 | filter: ${({ $loading }) => ($loading ? 'grayscale(1)' : 'none')}; 33 | opacity: ${({ $loading }) => ($loading ? '0.4' : '1')}; 34 | transition: opacity 0.2s ease-in-out; 35 | ` 36 | 37 | export const LoadingOpacityContainer = styled.div<{ $loading: boolean }>` 38 | ${loadingOpacityMixin} 39 | ` 40 | -------------------------------------------------------------------------------- /src/components/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { Slash } from 'react-feather' 3 | import { ImageProps } from 'rebass' 4 | 5 | import useTheme from '../../hooks/useTheme' 6 | 7 | const BAD_SRCS: { [address: string]: true } = {} 8 | 9 | interface LogoProps extends Pick { 10 | srcs: string[] 11 | } 12 | 13 | /** 14 | * Renders an image by sequentially trying a list of URIs, and then eventually a fallback triangle alert 15 | */ 16 | export default function Logo({ srcs, alt, style, ...rest }: LogoProps) { 17 | const [, refresh] = useState(0) 18 | const theme = useTheme() 19 | 20 | const src: string | undefined = srcs.find((src) => !BAD_SRCS[src]) 21 | 22 | if (src) { 23 | return ( 24 | {alt} { 30 | if (src) BAD_SRCS[src] = true 31 | refresh((i) => i + 1) 32 | }} 33 | /> 34 | ) 35 | } 36 | 37 | return 38 | } 39 | -------------------------------------------------------------------------------- /src/components/ModalViews/index.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import { useContext } from 'react' 3 | import { ArrowUpCircle } from 'react-feather' 4 | import { useChainId } from 'state/user/hooks' 5 | import styled, { ThemeContext } from 'styled-components/macro' 6 | 7 | import Circle from '../../assets/blue-loader.svg' 8 | import { CloseIcon, CustomLightSpinner, ThemedText } from '../../theme' 9 | import { ExternalLink } from '../../theme/components' 10 | import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' 11 | import { AutoColumn, ColumnCenter } from '../Column' 12 | import { RowBetween } from '../Row' 13 | 14 | const ConfirmOrLoadingWrapper = styled.div` 15 | width: 100%; 16 | padding: 24px; 17 | ` 18 | 19 | const ConfirmedIcon = styled(ColumnCenter)` 20 | padding: 60px 0; 21 | ` 22 | 23 | export function LoadingView({ children, onDismiss }: { children: any; onDismiss: () => void }) { 24 | return ( 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | {children} 35 | 36 | Confirm this transaction in your wallet 37 | 38 | 39 | 40 | ) 41 | } 42 | 43 | export function SubmittedView({ 44 | children, 45 | onDismiss, 46 | hash, 47 | }: { 48 | children: any 49 | onDismiss: () => void 50 | hash: string | undefined 51 | }) { 52 | const theme = useContext(ThemeContext) 53 | const chainId = useChainId() 54 | 55 | return ( 56 | 57 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | {children} 66 | {chainId && hash && ( 67 | 71 | 72 | View transaction on Explorer 73 | 74 | 75 | )} 76 | 77 | 78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /src/components/Popups/FailedNetworkSwitchPopup.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import { getChainInfo } from 'constants/chainInfo' 3 | import { SupportedChainId } from 'constants/chains' 4 | import { useContext } from 'react' 5 | import { AlertCircle } from 'react-feather' 6 | import styled, { ThemeContext } from 'styled-components/macro' 7 | 8 | import { ThemedText } from '../../theme' 9 | import { AutoColumn } from '../Column' 10 | import { AutoRow } from '../Row' 11 | 12 | const RowNoFlex = styled(AutoRow)` 13 | flex-wrap: nowrap; 14 | ` 15 | 16 | export default function FailedNetworkSwitchPopup({ chainId }: { chainId: SupportedChainId }) { 17 | const chainInfo = getChainInfo(chainId) 18 | const theme = useContext(ThemeContext) 19 | 20 | return ( 21 | 22 |
23 | 24 |
25 | 26 | 27 | 28 | Failed to switch networks from the Uniswap Interface. In order to use Uniswap on {chainInfo.label}, you must 29 | change the network in your wallet. 30 | 31 | 32 | 33 |
34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Popups/PopupItem.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useContext, useEffect } from 'react' 2 | import { X } from 'react-feather' 3 | import { animated, useSpring } from 'react-spring' 4 | import styled, { ThemeContext } from 'styled-components/macro' 5 | 6 | import { useRemovePopup } from '../../state/application/hooks' 7 | import { PopupContent } from '../../state/application/reducer' 8 | import FailedNetworkSwitchPopup from './FailedNetworkSwitchPopup' 9 | import TransactionPopup from './TransactionPopup' 10 | 11 | const StyledClose = styled(X)` 12 | position: absolute; 13 | right: 10px; 14 | top: 10px; 15 | 16 | :hover { 17 | cursor: pointer; 18 | } 19 | ` 20 | const Popup = styled.div` 21 | display: inline-block; 22 | width: 100%; 23 | padding: 1em; 24 | background-color: ${({ theme }) => theme.deprecated_bg0}; 25 | position: relative; 26 | border-radius: 10px; 27 | padding: 20px; 28 | padding-right: 35px; 29 | overflow: hidden; 30 | 31 | ${({ theme }) => theme.mediaWidth.upToSmall` 32 | min-width: 290px; 33 | &:not(:last-of-type) { 34 | margin-right: 20px; 35 | } 36 | `} 37 | ` 38 | const Fader = styled.div` 39 | position: absolute; 40 | bottom: 0px; 41 | left: 0px; 42 | width: 100%; 43 | height: 2px; 44 | background-color: ${({ theme }) => theme.deprecated_bg3}; 45 | ` 46 | 47 | const AnimatedFader = animated(Fader) 48 | 49 | export default function PopupItem({ 50 | removeAfterMs, 51 | content, 52 | popKey, 53 | }: { 54 | removeAfterMs: number | null 55 | content: PopupContent 56 | popKey: string 57 | }) { 58 | const removePopup = useRemovePopup() 59 | const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup]) 60 | useEffect(() => { 61 | if (removeAfterMs === null) return undefined 62 | 63 | const timeout = setTimeout(() => { 64 | removeThisPopup() 65 | }, removeAfterMs) 66 | 67 | return () => { 68 | clearTimeout(timeout) 69 | } 70 | }, [removeAfterMs, removeThisPopup]) 71 | 72 | const theme = useContext(ThemeContext) 73 | 74 | let popupContent 75 | if ('txn' in content) { 76 | const { 77 | txn: { hash }, 78 | } = content 79 | popupContent = 80 | } else if ('failedSwitchNetwork' in content) { 81 | popupContent = 82 | } 83 | 84 | const faderStyle = useSpring({ 85 | from: { width: '100%' }, 86 | to: { width: '0%' }, 87 | config: { duration: removeAfterMs ?? undefined }, 88 | }) 89 | 90 | return ( 91 | 92 | 93 | {popupContent} 94 | {removeAfterMs !== null ? : null} 95 | 96 | ) 97 | } 98 | -------------------------------------------------------------------------------- /src/components/Popups/TransactionPopup.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { AlertCircle, CheckCircle } from 'react-feather' 3 | import { useChainId } from 'state/user/hooks' 4 | import styled, { ThemeContext } from 'styled-components/macro' 5 | 6 | import { useTransaction } from '../../state/transactions/hooks' 7 | import { ThemedText } from '../../theme' 8 | import { ExternalLink } from '../../theme' 9 | import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' 10 | import { TransactionSummary } from '../AccountDetails/TransactionSummary' 11 | import { AutoColumn } from '../Column' 12 | import { AutoRow } from '../Row' 13 | 14 | const RowNoFlex = styled(AutoRow)` 15 | flex-wrap: nowrap; 16 | ` 17 | 18 | export default function TransactionPopup({ hash }: { hash: string }) { 19 | const chainId = useChainId() 20 | 21 | const tx = useTransaction(hash) 22 | const theme = useContext(ThemeContext) 23 | 24 | if (!tx) return null 25 | const success = Boolean(tx.receipt && tx.receipt.status === 1) 26 | 27 | return ( 28 | 29 |
30 | {success ? ( 31 | 32 | ) : ( 33 | 34 | )} 35 |
36 | 37 | 38 | 39 | 40 | {chainId && ( 41 | 42 | View on Explorer 43 | 44 | )} 45 | 46 |
47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Popups/index.tsx: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from 'constants/chains' 2 | import { useChainId } from 'state/user/hooks' 3 | import styled from 'styled-components/macro' 4 | import { MEDIA_WIDTHS } from 'theme' 5 | 6 | import { useActivePopups } from '../../state/application/hooks' 7 | import { AutoColumn } from '../Column' 8 | import PopupItem from './PopupItem' 9 | 10 | const MobilePopupWrapper = styled.div<{ height: string | number }>` 11 | position: relative; 12 | max-width: 100%; 13 | height: ${({ height }) => height}; 14 | margin: ${({ height }) => (height ? '0 auto;' : 0)}; 15 | margin-bottom: ${({ height }) => (height ? '20px' : 0)}; 16 | 17 | display: none; 18 | ${({ theme }) => theme.mediaWidth.upToSmall` 19 | display: block; 20 | padding-top: 20px; 21 | `}; 22 | ` 23 | 24 | const MobilePopupInner = styled.div` 25 | height: 99%; 26 | overflow-x: auto; 27 | overflow-y: hidden; 28 | display: flex; 29 | flex-direction: row; 30 | -webkit-overflow-scrolling: touch; 31 | ::-webkit-scrollbar { 32 | display: none; 33 | } 34 | ` 35 | 36 | const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${ 37 | MEDIA_WIDTHS.upToMedium + 500 38 | }px)` 39 | 40 | const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>` 41 | position: fixed; 42 | top: ${({ extraPadding }) => (extraPadding ? '64px' : '56px')}; 43 | right: 1rem; 44 | max-width: 355px !important; 45 | width: 100%; 46 | z-index: 3; 47 | 48 | ${({ theme }) => theme.mediaWidth.upToSmall` 49 | display: none; 50 | `}; 51 | 52 | ${StopOverflowQuery} { 53 | top: ${({ extraPadding, xlPadding }) => (xlPadding ? '64px' : extraPadding ? '64px' : '56px')}; 54 | } 55 | ` 56 | 57 | export default function Popups() { 58 | // get all popups 59 | const activePopups = useActivePopups() 60 | const chainId = useChainId() 61 | const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.APTOS) 62 | 63 | return ( 64 | <> 65 | 66 | {activePopups.map((item) => ( 67 | 68 | ))} 69 | 70 | 0 ? 'fit-content' : 0}> 71 | 72 | {activePopups // reverse so new items up front 73 | .slice(0) 74 | .reverse() 75 | .map((item) => ( 76 | 77 | ))} 78 | 79 | 80 | 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /src/components/ProgressSteps/index.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import styled from 'styled-components/macro' 3 | import { ThemeContext } from 'styled-components/macro' 4 | 5 | import { ThemedText } from '../../theme' 6 | import { AutoColumn } from '../Column' 7 | 8 | const Wrapper = styled(AutoColumn)` 9 | margin-right: 8px; 10 | height: 100%; 11 | ` 12 | 13 | const Grouping = styled(AutoColumn)` 14 | width: fit-content; 15 | padding: 4px; 16 | /* background-color: ${({ theme }) => theme.deprecated_bg2}; */ 17 | border-radius: 16px; 18 | ` 19 | 20 | const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>` 21 | width: 48px; 22 | height: 48px; 23 | background-color: ${({ theme, confirmed, disabled }) => 24 | disabled ? theme.deprecated_bg3 : confirmed ? theme.deprecated_green1 : theme.deprecated_primary1}; 25 | border-radius: 50%; 26 | color: ${({ theme, disabled }) => (disabled ? theme.deprecated_text3 : theme.deprecated_text1)}; 27 | display: flex; 28 | align-items: center; 29 | justify-content: center; 30 | line-height: 8px; 31 | font-size: 16px; 32 | padding: 1rem; 33 | ` 34 | 35 | const CircleRow = styled.div` 36 | display: flex; 37 | flex-direction: column; 38 | align-items: center; 39 | ` 40 | 41 | interface ProgressCirclesProps { 42 | steps: boolean[] 43 | disabled?: boolean 44 | } 45 | 46 | /** 47 | * Based on array of steps, create a step counter of circles. 48 | * A circle can be enabled, disabled, or confirmed. States are derived 49 | * from previous step. 50 | * 51 | * An extra circle is added to represent the ability to swap, add, or remove. 52 | * This step will never be marked as complete (because no 'txn done' state in body ui). 53 | * 54 | * @param steps array of booleans where true means step is complete 55 | */ 56 | export default function ProgressCircles({ steps, disabled = false, ...rest }: ProgressCirclesProps) { 57 | const theme = useContext(ThemeContext) 58 | 59 | return ( 60 | 61 | 62 | {steps.map((step, i) => { 63 | return ( 64 | 65 | 66 | {step ? '✓' : i + 1 + '.'} 67 | 68 | | 69 | 70 | ) 71 | })} 72 | {steps.length + 1 + '.'} 73 | 74 | 75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /src/components/QuestionHelper/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, useCallback, useState } from 'react' 2 | import styled from 'styled-components/macro' 3 | 4 | import Tooltip from '../Tooltip' 5 | 6 | const QuestionWrapper = styled.div` 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | padding: 0px; 11 | width: 18px; 12 | height: 18px; 13 | border: none; 14 | background: none; 15 | outline: none; 16 | cursor: default; 17 | border-radius: 36px; 18 | font-size: 12px; 19 | background-color: ${({ theme }) => theme.deprecated_bg2}; 20 | color: ${({ theme }) => theme.deprecated_text2}; 21 | 22 | :hover, 23 | :focus { 24 | opacity: 0.7; 25 | } 26 | ` 27 | 28 | const QuestionMark = styled.span` 29 | font-size: 14px; 30 | ` 31 | 32 | export default function QuestionHelper({ text }: { text: ReactNode; size?: number }) { 33 | const [show, setShow] = useState(false) 34 | 35 | const open = useCallback(() => setShow(true), [setShow]) 36 | const close = useCallback(() => setShow(false), [setShow]) 37 | 38 | return ( 39 | 40 | 41 | 42 | ? 43 | 44 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/components/Row/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from 'rebass/styled-components' 2 | import styled from 'styled-components/macro' 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 RowFlat = styled.div` 27 | display: flex; 28 | align-items: flex-end; 29 | ` 30 | 31 | export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>` 32 | flex-wrap: wrap; 33 | margin: ${({ gap }) => gap && `-${gap}`}; 34 | justify-content: ${({ justify }) => justify && justify}; 35 | 36 | & > * { 37 | margin: ${({ gap }) => gap} !important; 38 | } 39 | ` 40 | 41 | export const RowFixed = styled(Row)<{ gap?: string; justify?: string }>` 42 | width: fit-content; 43 | margin: ${({ gap }) => gap && `-${gap}`}; 44 | ` 45 | 46 | export default Row 47 | -------------------------------------------------------------------------------- /src/components/SearchModal/BlockedToken.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import { ButtonPrimary } from 'components/Button' 3 | import { Coin } from 'hooks/common/Coin' 4 | import { AlertCircle, ArrowLeft } from 'react-feather' 5 | import styled from 'styled-components/macro' 6 | import { CloseIcon, ThemedText } from 'theme' 7 | 8 | import TokenImportCard from './TokenImportCard' 9 | 10 | const Wrapper = styled.div` 11 | align-items: center; 12 | display: flex; 13 | flex-direction: column; 14 | flex: 1 1 auto; 15 | height: 100%; 16 | width: 100%; 17 | ` 18 | const Button = styled(ButtonPrimary)` 19 | margin-top: 1em; 20 | padding: 10px 1em; 21 | ` 22 | const Content = styled.div` 23 | padding: 1em; 24 | ` 25 | const Copy = styled(ThemedText.DeprecatedBody)` 26 | text-align: center; 27 | margin: 0 2em 1em !important; 28 | font-weight: 400; 29 | font-size: 16px; 30 | ` 31 | const Header = styled.div` 32 | align-items: center; 33 | display: flex; 34 | gap: 14px; 35 | justify-content: space-between; 36 | padding: 20px; 37 | width: 100%; 38 | ` 39 | const Icon = styled(AlertCircle)` 40 | stroke: ${({ theme }) => theme.deprecated_text2}; 41 | width: 48px; 42 | height: 48px; 43 | ` 44 | interface BlockedTokenProps { 45 | onBack: (() => void) | undefined 46 | onDismiss: (() => void) | undefined 47 | blockedTokens: Coin[] 48 | } 49 | 50 | const BlockedToken = ({ onBack, onDismiss, blockedTokens }: BlockedTokenProps) => ( 51 | 52 |
53 | {onBack ? :
} 54 | 55 | Token not supported 56 | 57 | {onDismiss ? :
} 58 |
59 | 60 | 61 | 62 | This token is not supported in the Uniswap Labs app 63 | 64 | 65 | 68 | 69 |
70 | ) 71 | export default BlockedToken 72 | -------------------------------------------------------------------------------- /src/components/SearchModal/CommonBases.tsx: -------------------------------------------------------------------------------- 1 | import CoinLogo from 'components/CoinLogo' 2 | import { AutoColumn } from 'components/Column' 3 | import { AutoRow } from 'components/Row' 4 | import { Coin } from 'hooks/common/Coin' 5 | import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList' 6 | import { Text } from 'rebass' 7 | import styled from 'styled-components/macro' 8 | 9 | const MobileWrapper = styled(AutoColumn)` 10 | ${({ theme }) => theme.mediaWidth.upToSmall` 11 | display: none; 12 | `}; 13 | ` 14 | 15 | const BaseWrapper = styled.div<{ disable?: boolean }>` 16 | border: 1px solid ${({ theme, disable }) => (disable ? 'transparent' : theme.deprecated_bg3)}; 17 | border-radius: 10px; 18 | display: flex; 19 | padding: 6px; 20 | 21 | align-items: center; 22 | :hover { 23 | cursor: ${({ disable }) => !disable && 'pointer'}; 24 | background-color: ${({ theme, disable }) => !disable && theme.deprecated_bg2}; 25 | } 26 | 27 | color: ${({ theme, disable }) => disable && theme.deprecated_text3}; 28 | background-color: ${({ theme, disable }) => disable && theme.deprecated_bg3}; 29 | filter: ${({ disable }) => disable && 'grayscale(1)'}; 30 | ` 31 | 32 | export default function CommonBases({ 33 | chainId, 34 | onSelect, 35 | selectedCurrency, 36 | searchQuery, 37 | isAddressSearch, 38 | }: { 39 | chainId?: number 40 | selectedCurrency?: Coin | null 41 | onSelect: (currency: Coin) => void 42 | searchQuery: string 43 | isAddressSearch: string | false 44 | }) { 45 | // const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : [] 46 | const bases = [] 47 | 48 | return bases.length > 0 ? ( 49 | 50 | 51 | {bases.map((currency: Coin) => { 52 | const isSelected = selectedCurrency.address === currency.address 53 | 54 | return ( 55 | !isSelected && e.key === 'Enter' && onSelect(currency)} 58 | onClick={() => !isSelected && onSelect(currency)} 59 | disable={isSelected} 60 | key={currency.address} 61 | > 62 | 63 | 64 | {currency.symbol} 65 | 66 | 67 | ) 68 | })} 69 | 70 | 71 | ) : null 72 | } 73 | 74 | /** helper component to retrieve a base currency from the active token lists */ 75 | function CurrencyLogoFromList({ currency }: { currency: Coin }) { 76 | const token = useTokenInfoFromActiveList(currency) 77 | 78 | return 79 | } 80 | -------------------------------------------------------------------------------- /src/components/SearchModal/styleds.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingRows as BaseLoadingRows } from 'components/Loader/styled' 2 | import styled from 'styled-components/macro' 3 | 4 | import { AutoColumn } from '../Column' 5 | import { RowBetween } from '../Row' 6 | 7 | export const TextDot = styled.div` 8 | height: 3px; 9 | width: 3px; 10 | background-color: ${({ theme }) => theme.deprecated_text2}; 11 | border-radius: 50%; 12 | ` 13 | 14 | export const Checkbox = styled.input` 15 | border: 1px solid ${({ theme }) => theme.deprecated_red3}; 16 | height: 20px; 17 | margin: 0; 18 | ` 19 | 20 | export const PaddedColumn = styled(AutoColumn)` 21 | padding: 20px; 22 | ` 23 | 24 | export const MenuItem = styled(RowBetween)` 25 | padding: 4px 20px; 26 | height: 56px; 27 | display: grid; 28 | grid-template-columns: auto minmax(auto, 1fr) auto minmax(0, 72px); 29 | grid-gap: 16px; 30 | cursor: ${({ disabled }) => !disabled && 'pointer'}; 31 | pointer-events: ${({ disabled }) => disabled && 'none'}; 32 | :hover { 33 | background-color: ${({ theme, disabled }) => !disabled && theme.deprecated_bg2}; 34 | } 35 | opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)}; 36 | ` 37 | 38 | export const SearchInput = styled.input` 39 | position: relative; 40 | display: flex; 41 | padding: 16px; 42 | align-items: center; 43 | width: 100%; 44 | white-space: nowrap; 45 | background: none; 46 | border: none; 47 | outline: none; 48 | border-radius: 20px; 49 | color: ${({ theme }) => theme.deprecated_text1}; 50 | border-style: solid; 51 | border: 1px solid ${({ theme }) => theme.deprecated_bg3}; 52 | -webkit-appearance: none; 53 | 54 | font-size: 18px; 55 | 56 | ::placeholder { 57 | color: ${({ theme }) => theme.deprecated_text3}; 58 | } 59 | transition: border 100ms; 60 | :focus { 61 | border: 1px solid ${({ theme }) => theme.deprecated_primary1}; 62 | outline: none; 63 | } 64 | ` 65 | export const Separator = styled.div` 66 | width: 100%; 67 | height: 1px; 68 | background-color: ${({ theme }) => theme.deprecated_bg2}; 69 | ` 70 | 71 | export const SeparatorDark = styled.div` 72 | width: 100%; 73 | height: 1px; 74 | background-color: ${({ theme }) => theme.deprecated_bg3}; 75 | ` 76 | 77 | export const LoadingRows = styled(BaseLoadingRows)` 78 | grid-column-gap: 0.5em; 79 | grid-template-columns: repeat(12, 1fr); 80 | max-width: 960px; 81 | padding: 12px 20px; 82 | 83 | & > div:nth-child(4n + 1) { 84 | grid-column: 1 / 8; 85 | height: 1em; 86 | margin-bottom: 0.25em; 87 | } 88 | & > div:nth-child(4n + 2) { 89 | grid-column: 12; 90 | height: 1em; 91 | margin-top: 0.25em; 92 | } 93 | & > div:nth-child(4n + 3) { 94 | grid-column: 1 / 4; 95 | height: 0.75em; 96 | } 97 | ` 98 | -------------------------------------------------------------------------------- /src/components/SwitchLocaleLink/index.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import { useLocationLinkProps } from 'hooks/useLocationLinkProps' 3 | import { useMemo } from 'react' 4 | import styled from 'styled-components/macro' 5 | 6 | import { DEFAULT_LOCALE, LOCALE_LABEL, SupportedLocale } from '../../constants/locales' 7 | import { navigatorLocale, useActiveLocale } from '../../hooks/useActiveLocale' 8 | import { StyledInternalLink, ThemedText } from '../../theme' 9 | 10 | const Container = styled(ThemedText.DeprecatedSmall)` 11 | opacity: 0.6; 12 | :hover { 13 | opacity: 1; 14 | } 15 | margin-top: 1rem !important; 16 | ` 17 | 18 | const useTargetLocale = (activeLocale: SupportedLocale) => { 19 | const browserLocale = useMemo(() => navigatorLocale(), []) 20 | 21 | if (browserLocale && (browserLocale !== DEFAULT_LOCALE || activeLocale !== DEFAULT_LOCALE)) { 22 | if (activeLocale === browserLocale) { 23 | return DEFAULT_LOCALE 24 | } else { 25 | return browserLocale 26 | } 27 | } 28 | return null 29 | } 30 | 31 | export function SwitchLocaleLink() { 32 | const activeLocale = useActiveLocale() 33 | const targetLocale = useTargetLocale(activeLocale) 34 | 35 | const { to, onClick } = useLocationLinkProps(targetLocale) 36 | 37 | if (!targetLocale || !to) return null 38 | 39 | return ( 40 | 41 | 42 | AnimeSwap available in:{' '} 43 | 44 | {LOCALE_LABEL[targetLocale]} 45 | 46 | 47 | 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /src/components/TextInput/index.test.tsx: -------------------------------------------------------------------------------- 1 | import { fireEvent, render, screen } from 'test-utils' 2 | 3 | import { ResizingTextArea, TextInput } from './' 4 | 5 | describe('TextInput', () => { 6 | it('renders correctly', () => { 7 | const { asFragment } = render( 8 | null} 12 | placeholder="Test Placeholder" 13 | fontSize="12" 14 | /> 15 | ) 16 | expect(asFragment()).toMatchSnapshot() 17 | }) 18 | 19 | it('calls the handler on user input', () => { 20 | const onUserInputSpy = jest.fn() 21 | render( 22 | 29 | ) 30 | 31 | fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } }) 32 | 33 | expect(onUserInputSpy).toHaveBeenCalledWith('New value') 34 | expect(onUserInputSpy).toHaveBeenCalledTimes(1) 35 | }) 36 | }) 37 | 38 | describe('ResizableTextArea', () => { 39 | it('renders correctly', () => { 40 | const { asFragment } = render( 41 | null} 45 | placeholder="Test Placeholder" 46 | fontSize="12" 47 | /> 48 | ) 49 | expect(asFragment()).toMatchSnapshot() 50 | }) 51 | 52 | it('calls the handler on user input', () => { 53 | const onUserInputSpy = jest.fn() 54 | render( 55 | 62 | ) 63 | 64 | fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } }) 65 | 66 | expect(onUserInputSpy).toHaveBeenCalledWith('New value') 67 | expect(onUserInputSpy).toHaveBeenCalledTimes(1) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /src/components/Toggle/MultiToggle.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/macro' 2 | 3 | export const ToggleWrapper = styled.button<{ width?: string }>` 4 | display: flex; 5 | align-items: center; 6 | width: ${({ width }) => width ?? '100%'}; 7 | padding: 1px; 8 | background: ${({ theme }) => theme.deprecated_bg1}; 9 | border-radius: 8px; 10 | border: ${({ theme }) => '1px solid ' + theme.deprecated_bg2}; 11 | cursor: pointer; 12 | outline: none; 13 | ` 14 | 15 | export const ToggleElement = styled.span<{ isActive?: boolean; fontSize?: string }>` 16 | display: flex; 17 | align-items: center; 18 | width: 100%; 19 | padding: 4px 0.5rem; 20 | border-radius: 6px; 21 | justify-content: center; 22 | height: 100%; 23 | background: ${({ theme, isActive }) => (isActive ? theme.deprecated_bg0 : 'none')}; 24 | color: ${({ theme, isActive }) => (isActive ? theme.deprecated_text1 : theme.deprecated_text3)}; 25 | font-size: ${({ fontSize }) => fontSize ?? '1rem'}; 26 | font-weight: 500; 27 | white-space: nowrap; 28 | :hover { 29 | user-select: initial; 30 | color: ${({ theme, isActive }) => (isActive ? theme.deprecated_text2 : theme.deprecated_text3)}; 31 | } 32 | ` 33 | -------------------------------------------------------------------------------- /src/components/Toggle/index.tsx: -------------------------------------------------------------------------------- 1 | import { darken } from 'polished' 2 | import { useState } from 'react' 3 | import styled, { keyframes } from 'styled-components/macro' 4 | 5 | const Wrapper = styled.button<{ isActive?: boolean; activeElement?: boolean }>` 6 | align-items: center; 7 | background: ${({ theme }) => theme.deprecated_bg1}; 8 | border: none; 9 | border-radius: 20px; 10 | cursor: pointer; 11 | display: flex; 12 | outline: none; 13 | padding: 0.4rem 0.4rem; 14 | width: fit-content; 15 | ` 16 | 17 | const turnOnToggle = keyframes` 18 | from { 19 | margin-left: 0em; 20 | margin-right: 2.2em; 21 | } 22 | to { 23 | margin-left: 2.2em; 24 | margin-right: 0em; 25 | } 26 | ` 27 | 28 | const turnOffToggle = keyframes` 29 | from { 30 | margin-left: 2.2em; 31 | margin-right: 0em; 32 | } 33 | to { 34 | margin-left: 0em; 35 | margin-right: 2.2em; 36 | } 37 | ` 38 | 39 | const ToggleElementHoverStyle = (hasBgColor: boolean, theme: any, isActive?: boolean) => 40 | hasBgColor 41 | ? { 42 | opacity: '0.8', 43 | } 44 | : { 45 | background: isActive ? darken(0.05, theme.deprecated_primary1) : darken(0.05, theme.deprecated_bg4), 46 | color: isActive ? theme.deprecated_white : theme.deprecated_text3, 47 | } 48 | 49 | const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string; isInitialToggleLoad?: boolean }>` 50 | animation: 0.1s 51 | ${({ isActive, isInitialToggleLoad }) => (isInitialToggleLoad ? 'none' : isActive ? turnOnToggle : turnOffToggle)} 52 | ease-in; 53 | background: ${({ theme, bgColor, isActive }) => 54 | isActive ? bgColor ?? theme.deprecated_primary1 : !!bgColor ? theme.deprecated_bg4 : theme.deprecated_text3}; 55 | border-radius: 50%; 56 | height: 24px; 57 | :hover { 58 | ${({ bgColor, theme, isActive }) => ToggleElementHoverStyle(!!bgColor, theme, isActive)} 59 | } 60 | margin-left: ${({ isActive }) => (isActive ? '2.2em' : '0em')}; 61 | margin-right: ${({ isActive }) => (!isActive ? '2.2em' : '0em')}; 62 | width: 24px; 63 | ` 64 | 65 | interface ToggleProps { 66 | id?: string 67 | bgColor?: string 68 | isActive: boolean 69 | toggle: () => void 70 | } 71 | 72 | export default function Toggle({ id, bgColor, isActive, toggle }: ToggleProps) { 73 | const [isInitialToggleLoad, setIsInitialToggleLoad] = useState(true) 74 | 75 | const switchToggle = () => { 76 | toggle() 77 | if (isInitialToggleLoad) setIsInitialToggleLoad(false) 78 | } 79 | 80 | return ( 81 | 82 | 83 | 84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /src/components/TokenSafety/TokenSafetyIcon.tsx: -------------------------------------------------------------------------------- 1 | import { ReactComponent as Verified } from 'assets/verified.svg' 2 | import { Warning, WARNING_LEVEL } from 'constants/tokenSafety' 3 | import { useTokenWarningColor } from 'hooks/useTokenWarningColor' 4 | import { AlertOctagon, AlertTriangle } from 'react-feather' 5 | import styled from 'styled-components/macro' 6 | import { Color } from 'theme/styled' 7 | 8 | const Container = styled.div<{ color: Color }>` 9 | width: 0.9rem; 10 | height: 0.9rem; 11 | margin-left: 4px; 12 | color: ${({ color }) => color}; 13 | display: inline-flex; 14 | align-items: center; 15 | ` 16 | 17 | const VerifiedContainer = styled.div` 18 | margin-left: 4px; 19 | display: flex; 20 | justify-content: center; 21 | ` 22 | 23 | export const VerifiedIcon = styled(Verified)<{ size?: string }>` 24 | width: ${({ size }) => size ?? '1em'}; 25 | height: ${({ size }) => size ?? '1em'}; 26 | ` 27 | 28 | export default function TokenSafetyIcon({ warning }: { warning: Warning | null }) { 29 | const color = useTokenWarningColor(warning ? warning.level : WARNING_LEVEL.UNKNOWN) 30 | if (!warning) { 31 | return ( 32 | 33 | 34 | 35 | ) 36 | } 37 | return {warning.canProceed ? : } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/TokenSafety/TokenSafetyLabel.tsx: -------------------------------------------------------------------------------- 1 | import { WARNING_LEVEL } from 'constants/tokenSafety' 2 | import { useTokenWarningColor } from 'hooks/useTokenWarningColor' 3 | import { ReactNode } from 'react' 4 | import { AlertOctagon, AlertTriangle } from 'react-feather' 5 | import { Text } from 'rebass' 6 | import styled from 'styled-components/macro' 7 | import { Color } from 'theme/styled' 8 | 9 | const Label = styled.div<{ color: Color }>` 10 | padding: 4px 4px; 11 | font-size: 12px; 12 | background-color: ${({ color }) => color + '1F'}; 13 | border-radius: 8px; 14 | color: ${({ color }) => color}; 15 | display: inline-flex; 16 | align-items: center; 17 | ` 18 | 19 | const Title = styled(Text)` 20 | margin-right: 5px; 21 | font-weight: 700; 22 | font-size: 12px; 23 | ` 24 | 25 | type TokenWarningLabelProps = { 26 | level: WARNING_LEVEL 27 | canProceed: boolean 28 | children: ReactNode 29 | } 30 | export default function TokenSafetyLabel({ level, canProceed, children }: TokenWarningLabelProps) { 31 | return ( 32 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /src/components/TokenSafety/TokenSafetyMessage.tsx: -------------------------------------------------------------------------------- 1 | import { Trans } from '@lingui/macro' 2 | import { getWarningCopy, TOKEN_SAFETY_ARTICLE, Warning } from 'constants/tokenSafety' 3 | import { useTokenWarningColor } from 'hooks/useTokenWarningColor' 4 | import { AlertOctagon, AlertTriangle } from 'react-feather' 5 | import { Text } from 'rebass' 6 | import styled from 'styled-components/macro' 7 | import { ExternalLink } from 'theme' 8 | import { Color } from 'theme/styled' 9 | 10 | const Label = styled.div<{ color: Color }>` 11 | width: 284px; 12 | padding: 12px 20px; 13 | background-color: ${({ color }) => color + '1F'}; 14 | border-radius: 16px; 15 | color: ${({ color }) => color}; 16 | ` 17 | 18 | const TitleRow = styled.div` 19 | align-items: center; 20 | font-weight: 700; 21 | display: inline-flex; 22 | ` 23 | 24 | const Title = styled(Text)` 25 | font-weight: 600; 26 | font-size: 16px; 27 | line-height: 24px; 28 | margin-left: 7px; 29 | ` 30 | 31 | const DetailsRow = styled.div` 32 | margin-top: 8px; 33 | font-size: 12px; 34 | color: ${({ theme }) => theme.textSecondary}; 35 | ` 36 | 37 | type TokenWarningMessageProps = { 38 | warning: Warning 39 | tokenAddress: string 40 | } 41 | 42 | export default function TokenWarningMessage({ warning, tokenAddress }: TokenWarningMessageProps) { 43 | const color = useTokenWarningColor(warning.level) 44 | const { heading, description } = getWarningCopy(warning) 45 | 46 | return ( 47 | 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /src/components/TokenSafety/TokenSafetyModal.tsx: -------------------------------------------------------------------------------- 1 | import Modal from '../Modal' 2 | import TokenSafety from '.' 3 | 4 | interface TokenSafetyModalProps { 5 | isOpen: boolean 6 | tokenAddress: string | null 7 | secondTokenAddress?: string 8 | onContinue: () => void 9 | onCancel: () => void 10 | } 11 | 12 | export default function TokenSafetyModal({ 13 | isOpen, 14 | tokenAddress, 15 | secondTokenAddress, 16 | onContinue, 17 | onCancel, 18 | }: TokenSafetyModalProps) { 19 | return ( 20 | 21 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/components/TokenSafety/verified.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/TokenWarningModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { ImportToken } from 'components/SearchModal/ImportToken' 2 | import { Coin } from 'hooks/common/Coin' 3 | 4 | import Modal from '../Modal' 5 | 6 | export default function TokenWarningModal({ 7 | isOpen, 8 | tokens, 9 | onConfirm, 10 | onDismiss, 11 | }: { 12 | isOpen: boolean 13 | tokens: Coin[] 14 | onConfirm: () => void 15 | onDismiss: () => void 16 | }) { 17 | return ( 18 | 19 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/TopLevelModals/index.tsx: -------------------------------------------------------------------------------- 1 | import AddressClaimModal from 'components/claim/AddressClaimModal' 2 | import { useModalIsOpen, useToggleModal } from 'state/application/hooks' 3 | import { ApplicationModal } from 'state/application/reducer' 4 | import { useAccount } from 'state/wallets/hooks' 5 | 6 | export default function TopLevelModals() { 7 | const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM) 8 | const addressClaimToggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM) 9 | 10 | const blockedAccountModalOpen = useModalIsOpen(ApplicationModal.BLOCKED_ACCOUNT) 11 | const account = useAccount() 12 | 13 | const open = Boolean(blockedAccountModalOpen && account) 14 | return ( 15 | <> 16 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx: -------------------------------------------------------------------------------- 1 | import useTheme from 'hooks/useTheme' 2 | import styled, { keyframes } from 'styled-components/macro' 3 | 4 | const Wrapper = styled.div` 5 | height: 90px; 6 | width: 90px; 7 | ` 8 | 9 | const dash = keyframes` 10 | 0% { 11 | stroke-dashoffset: 1000; 12 | } 13 | 100% { 14 | stroke-dashoffset: 0; 15 | } 16 | ` 17 | 18 | const dashCheck = keyframes` 19 | 0% { 20 | stroke-dashoffset: -100; 21 | } 22 | 100% { 23 | stroke-dashoffset: 900; 24 | } 25 | ` 26 | 27 | const Circle = styled.circle` 28 | stroke-dasharray: 1000; 29 | stroke-dashoffset: 0; 30 | -webkit-animation: ${dash} 0.9s ease-in-out; 31 | animation: ${dash} 0.9s ease-in-out; 32 | ` 33 | 34 | const PolyLine = styled.polyline` 35 | stroke-dasharray: 1000; 36 | stroke-dashoffset: 0; 37 | stroke-dashoffset: -100; 38 | -webkit-animation: ${dashCheck} 0.9s 0.35s ease-in-out forwards; 39 | animation: ${dashCheck} 0.9s 0.35s ease-in-out forwards; 40 | ` 41 | 42 | export default function AnimatedConfirmation() { 43 | const theme = useTheme() 44 | 45 | return ( 46 | 47 | 48 | 58 | 67 | 68 | 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /src/components/WalletModal/FewchaOption.tsx: -------------------------------------------------------------------------------- 1 | import FEWCHA_ICON_URL from 'assets/fewcha.svg' 2 | import { ConnectFewcha, useAccount, useWallet } from 'state/wallets/hooks' 3 | import { getWalletName, WalletType } from 'state/wallets/types' 4 | 5 | import Option from './Option' 6 | 7 | const BASE_PROPS = { 8 | color: '#6748FF', 9 | icon: FEWCHA_ICON_URL, 10 | id: 'fewcha-option', 11 | } 12 | 13 | export default function FewchaOption() { 14 | const account = useAccount() 15 | const walletType = useWallet() 16 | const isActive = walletType === WalletType.FEWCHA && account !== undefined 17 | const isInstall = 'fewcha' in window 18 | return ( 19 |